Title: [278605] trunk
Revision
278605
Author
[email protected]
Date
2021-06-08 07:02:56 -0700 (Tue, 08 Jun 2021)

Log Message

[LFC][TFC] Add initial percent value support for columns
https://bugs.webkit.org/show_bug.cgi?id=226751

Reviewed by Simon Fraser.

Source/WebCore:

This patch adds the initial support for content like this:
<table>
  <tr>
    <td style="width: 10%"></td><td style="width: 90%"></td>
  </tr>
</table>

Percent values work in mysterious ways in cases when the table has no fixed width.

1. The smaller the percent value is, the wider the table may become.

 Percent values are resolved against the cell's border box (so essentially they are resolved
 against their own content as opposed to the table/containing block) and the formula is slightly different.

    <td style="padding: 5px; width: 20%;"></td> : produces a 10px wide border box (horizontal border: 0px, padding: 10px, content: 0px).
      The maximum constraint is resolved to 50px (width / percent * 100)
    <td style="padding: 5px; width: 100%;"></td> : produces a 10px wide border box and the maximum constraint is resolved to 10px.

  This maximum constraint value turns into the available width for the table content and becomes the final table width.

2. With multiple rows, we pick the highest _percent_ value for each column (as opposed to the resolved values).

      <tr><td style="width: 20%"></td></tr> (assum same 5px padding on both sides)
      <tr><td style="width: 80%"></td></tr>

  While the second row's cell has a higher maximum constraint value (50px see #1) since we only look at the raw percent values,
  this content only produces a 12.5px wide table.

3. The percent values do not accumulate across columns but instead we pick the largest one to represent the entire table's max constraint width.

    <tr><td style="width: 60%"></td><td style="width: 40%"></td></tr>

  60% resolves to 16.6px
  40% resolves to 25px and we use the 25px value as the width for the entire table (and not 16.6px + 25px).

4. Since we pick the highest percent values across rows for each columns, we may end up with > 100%.
  In such cases we start dropping percent values for subsequent columns:

    <tr><td style="width: 20%;"></td><td style="width: 80%;"></td></tr>
    <tr><td style="width: 60%;"></td><td style="width: 10%;"></td></tr>

  First column width is max(20%, 60%) -> 60%
  Second column width is max(80%, 10%) -> 80%
  As we limit the accumulated percent value to 100%, the final column percent values are 60% and 40% (and not 80%).
  Now the 60% is resolved to 16.6px and the 40% is resolved to 25px and since we don't accumulate these values (see #3)
  the final table width is 25px (based on a percent value which is not even in the markup).

5. While the smaller percent values produce wider tables (see #1), during the available space distribution
  columns with smaller percent values get assigned less space.

    <tr><td style="width: 1%"></td><td style="width: 99%"></td></tr>

  This content produces a 1000px wide table due to the small (1%) percent value (see #1 #2 and #3).
  When we distribute the available space (1000px), the first cell gets only 10px (1%) while the second cell ends up with 990px (99%).

(and this is the cherry on top (not included in this patch):
 Imagine the following scenario:
   1. the accumulated column percent value > 100% (let's say 80% and 30%)
   2. as we reach the 100% while walking the columns one by one (see #4), the remaining percent value becomes 0%.
   3. In order to avoid division by 0, we pick a very small epsilon value to run the formula.
   4. Now this very small percent value produces a large resolved value (see #2) which means

    <tr><td style="width: 100%"></td></tr>

    produces a 10px wide table

    <tr><td style="width: 100%"></td><td style="width: 1%"></td></tr> <- note the 1%

    produces a very very very wide table.
)

Test: fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html

* layout/formattingContexts/table/TableFormattingContext.cpp:
(WebCore::Layout::TableFormattingContext::computedIntrinsicWidthConstraints):
(WebCore::Layout::TableFormattingContext::computedPreferredWidthForColumns):
* layout/formattingContexts/table/TableGrid.h:
(WebCore::Layout::TableGrid::Column::percent const):
(WebCore::Layout::TableGrid::Column::setFixedWidth):
(WebCore::Layout::TableGrid::Column::setPercent):
* layout/formattingContexts/table/TableLayout.cpp:
(WebCore::Layout::TableFormattingContext::TableLayout::distributedHorizontalSpace):

LayoutTests:

* fast/layoutformattingcontext/table-with-percent-columns-only-no-content-expected.html: Added.
* fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (278604 => 278605)


--- trunk/LayoutTests/ChangeLog	2021-06-08 13:53:03 UTC (rev 278604)
+++ trunk/LayoutTests/ChangeLog	2021-06-08 14:02:56 UTC (rev 278605)
@@ -1,3 +1,13 @@
+2021-06-08  Alan Bujtas  <[email protected]>
+
+        [LFC][TFC] Add initial percent value support for columns
+        https://bugs.webkit.org/show_bug.cgi?id=226751
+
+        Reviewed by Simon Fraser.
+
+        * fast/layoutformattingcontext/table-with-percent-columns-only-no-content-expected.html: Added.
+        * fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html: Added.
+
 2021-06-08  Diego Pino Garcia  <[email protected]>
 
         [GTK] Unreviewed test gardening. Update baselines of tests marked a text flaky.

Added: trunk/LayoutTests/fast/layoutformattingcontext/table-with-percent-columns-only-no-content-expected.html (0 => 278605)


--- trunk/LayoutTests/fast/layoutformattingcontext/table-with-percent-columns-only-no-content-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/layoutformattingcontext/table-with-percent-columns-only-no-content-expected.html	2021-06-08 14:02:56 UTC (rev 278605)
@@ -0,0 +1,26 @@
+<!DOCTYPE html><!-- webkit-test-runner [ LayoutFormattingContextEnabled=true LayoutFormattingContextIntegrationEnabled=false ] -->
+<style>
+div {
+  background-color: green;
+  height: 10px;
+  display: inline-block;
+}
+
+div:nth-child(even) {
+  background-color: blue;
+}
+
+pre {
+  line-height: 0px;
+  margin-top: 8px;
+}
+</style>
+<pre>
+<div style="width: 990px;"></div><div style="width: 10px;"></div>
+<div style="width: 500px;"></div><div style="width: 500px;"></div>
+<div style="width: 10px;"></div><div style="width: 990px;"></div>
+<div style="width: 990px;"></div><div style="width: 10px;"></div>
+<div style="width: 990px;"></div><div style="width: 10px;"></div>
+<div style="width: 20px;"></div><div style="width: 980px;"></div>
+<div style="width: 20px;"></div><div style="width: 980px;"></div>
+</pre>
\ No newline at end of file

Added: trunk/LayoutTests/fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html (0 => 278605)


--- trunk/LayoutTests/fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html	                        (rev 0)
+++ trunk/LayoutTests/fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html	2021-06-08 14:02:56 UTC (rev 278605)
@@ -0,0 +1,39 @@
+<!DOCTYPE html><!-- webkit-test-runner [ LayoutFormattingContextEnabled=true LayoutFormattingContextIntegrationEnabled=false ] -->
+<style>
+table {
+  border-spacing: 0px;
+  background-color: blue;
+}
+td {
+  background-color: green;
+  padding: 5px;
+}
+
+td:nth-child(even) {
+  background-color: blue;
+}
+
+</style>
+<div style="width: 1000px;">
+<table>
+<tr><td style="width: 99%"></td><td style="width: 1%"></td></tr>
+</table>
+
+<table>
+<tr><td style="width: 1%"></td><td style="width: 1%"></td></tr>
+</table>
+
+<table>
+<tr><td style="width: 1%"></td><td style="width: 101%"></td></tr>
+</table>
+
+<table>
+<tr><td style="width: 1%"></td><td style="width: 99%"></td></tr>
+<tr><td style="width: 99%"></td><td style="width: 1%"></td></tr>
+</table>
+
+<table>
+<tr><td style="width: 1%"></td><td style="width: 50%"></td></tr>
+<tr><td style="width: 1%"></td><td style="width: 1%"></td></tr>
+</table>
+</div>
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (278604 => 278605)


--- trunk/Source/WebCore/ChangeLog	2021-06-08 13:53:03 UTC (rev 278604)
+++ trunk/Source/WebCore/ChangeLog	2021-06-08 14:02:56 UTC (rev 278605)
@@ -1,3 +1,93 @@
+2021-06-08  Alan Bujtas  <[email protected]>
+
+        [LFC][TFC] Add initial percent value support for columns
+        https://bugs.webkit.org/show_bug.cgi?id=226751
+
+        Reviewed by Simon Fraser.
+
+        This patch adds the initial support for content like this:
+        <table>
+          <tr>
+            <td style="width: 10%"></td><td style="width: 90%"></td>
+          </tr>
+        </table>
+
+        Percent values work in mysterious ways in cases when the table has no fixed width.
+
+        1. The smaller the percent value is, the wider the table may become.
+
+         Percent values are resolved against the cell's border box (so essentially they are resolved
+         against their own content as opposed to the table/containing block) and the formula is slightly different.
+
+            <td style="padding: 5px; width: 20%;"></td> : produces a 10px wide border box (horizontal border: 0px, padding: 10px, content: 0px).
+              The maximum constraint is resolved to 50px (width / percent * 100)
+            <td style="padding: 5px; width: 100%;"></td> : produces a 10px wide border box and the maximum constraint is resolved to 10px. 
+
+          This maximum constraint value turns into the available width for the table content and becomes the final table width.
+
+        2. With multiple rows, we pick the highest _percent_ value for each column (as opposed to the resolved values).
+
+              <tr><td style="width: 20%"></td></tr> (assum same 5px padding on both sides)
+              <tr><td style="width: 80%"></td></tr>
+
+          While the second row's cell has a higher maximum constraint value (50px see #1) since we only look at the raw percent values,
+          this content only produces a 12.5px wide table.
+
+        3. The percent values do not accumulate across columns but instead we pick the largest one to represent the entire table's max constraint width.
+
+            <tr><td style="width: 60%"></td><td style="width: 40%"></td></tr>
+
+          60% resolves to 16.6px
+          40% resolves to 25px and we use the 25px value as the width for the entire table (and not 16.6px + 25px).
+
+        4. Since we pick the highest percent values across rows for each columns, we may end up with > 100%.
+          In such cases we start dropping percent values for subsequent columns:
+
+            <tr><td style="width: 20%;"></td><td style="width: 80%;"></td></tr>
+            <tr><td style="width: 60%;"></td><td style="width: 10%;"></td></tr>
+
+          First column width is max(20%, 60%) -> 60%
+          Second column width is max(80%, 10%) -> 80%
+          As we limit the accumulated percent value to 100%, the final column percent values are 60% and 40% (and not 80%).
+          Now the 60% is resolved to 16.6px and the 40% is resolved to 25px and since we don't accumulate these values (see #3)
+          the final table width is 25px (based on a percent value which is not even in the markup).
+
+        5. While the smaller percent values produce wider tables (see #1), during the available space distribution
+          columns with smaller percent values get assigned less space.
+
+            <tr><td style="width: 1%"></td><td style="width: 99%"></td></tr>
+
+          This content produces a 1000px wide table due to the small (1%) percent value (see #1 #2 and #3).
+          When we distribute the available space (1000px), the first cell gets only 10px (1%) while the second cell ends up with 990px (99%).
+
+        (and this is the cherry on top (not included in this patch):
+         Imagine the following scenario:
+           1. the accumulated column percent value > 100% (let's say 80% and 30%)
+           2. as we reach the 100% while walking the columns one by one (see #4), the remaining percent value becomes 0%.
+           3. In order to avoid division by 0, we pick a very small epsilon value to run the formula.
+           4. Now this very small percent value produces a large resolved value (see #2) which means
+
+            <tr><td style="width: 100%"></td></tr>
+
+            produces a 10px wide table
+
+            <tr><td style="width: 100%"></td><td style="width: 1%"></td></tr> <- note the 1%
+
+            produces a very very very wide table.
+        )
+
+        Test: fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html
+
+        * layout/formattingContexts/table/TableFormattingContext.cpp:
+        (WebCore::Layout::TableFormattingContext::computedIntrinsicWidthConstraints):
+        (WebCore::Layout::TableFormattingContext::computedPreferredWidthForColumns):
+        * layout/formattingContexts/table/TableGrid.h:
+        (WebCore::Layout::TableGrid::Column::percent const):
+        (WebCore::Layout::TableGrid::Column::setFixedWidth):
+        (WebCore::Layout::TableGrid::Column::setPercent):
+        * layout/formattingContexts/table/TableLayout.cpp:
+        (WebCore::Layout::TableFormattingContext::TableLayout::distributedHorizontalSpace):
+
 2021-06-08  Jean-Yves Avenard  <[email protected]>
 
         [MSE] Rework handling of SourceBuffer's buffer full.

Modified: trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp (278604 => 278605)


--- trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp	2021-06-08 13:53:03 UTC (rev 278604)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp	2021-06-08 14:02:56 UTC (rev 278605)
@@ -296,9 +296,9 @@
 IntrinsicWidthConstraints TableFormattingContext::computedIntrinsicWidthConstraints()
 {
     ASSERT(!root().isSizeContainmentBox());
-    // Tables have a slighty different concept of shrink to fit. It's really only different with non-auto "width" values, where
+    // Tables have a slightly different concept of shrink to fit. It's really only different with non-auto "width" values, where
     // a generic shrink-to fit block level box like a float box would be just sized to the computed value of "width", tables
-    // can actually be streched way over.
+    // can actually be stretched way over.
     auto& grid = formattingState().tableGrid();
     if (auto computedWidthConstraints = grid.widthConstraints())
         return *computedWidthConstraints;
@@ -328,7 +328,7 @@
         auto fixedWidth = [&] () -> std::optional<LayoutUnit> {
             auto* columnBox = column.box();
             if (!columnBox) {
-                // Anoynmous columns don't have associated layout boxes and can't have fixed col size.
+                // Anonymous columns don't have associated layout boxes and can't have fixed col size.
                 return { };
             }
             if (auto width = columnBox->columnWidth())
@@ -339,6 +339,7 @@
             column.setFixedWidth(*fixedWidth);
     }
 
+    Vector<std::optional<float>> columnPercentList(columnList.size());
     for (auto& cell : grid.cells()) {
         auto& cellBox = cell->box();
         ASSERT(cellBox.establishesBlockFormattingContext());
@@ -360,6 +361,12 @@
             *fixedWidth += horizontalBorderAndPaddingWidth;
             columnList[cellPosition.column].setFixedWidth(std::max(*fixedWidth, columnList[cellPosition.column].fixedWidth().value_or(0)));
         }
+        // Collect the percent values so that we can compute the maximum values per column.
+        auto& cellLogicalWidth = cellBox.style().logicalWidth();
+        if (cellLogicalWidth.isPercent()) {
+            // FIXME: Add support for column spanning distribution.
+            columnPercentList[cellPosition.column] = std::max(cellLogicalWidth.percent(), columnPercentList[cellPosition.column].value_or(0.0f));
+        }
     }
 
     Vector<IntrinsicWidthConstraints> columnIntrinsicWidths(columnList.size());
@@ -411,11 +418,26 @@
         }
     }
 
-    // 5. The final table min/max widths is just the accumulated column constraints.
+    // 5. Resolve the percent values.
+    auto remainingPercent = 100.0f;
+    auto percentMaximumConstraint = LayoutUnit { };
+    for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
+        auto percent = columnPercentList[columnIndex];
+        // FIXME: Add support for mixed content with and without percent values.
+        if (!percent)
+            continue;
+        ASSERT(*percent > 0);
+        columnList[columnIndex].setPercent(std::min(remainingPercent, *percent));
+        percentMaximumConstraint = std::max(percentMaximumConstraint, LayoutUnit { columnIntrinsicWidths[columnIndex].maximum * 100.0f / *columnList[columnIndex].percent() });
+        remainingPercent -= *columnList[columnIndex].percent();
+    }
+
+    // 6. The final table min/max widths is just the accumulated column constraints.
     auto tableWidthConstraints = IntrinsicWidthConstraints { };
     for (auto& columnIntrinsicWidth : columnIntrinsicWidths)
         tableWidthConstraints += columnIntrinsicWidth;
-    // Exapand the preferred width with leading and trailing cell spacing (note that column spanners count as one cell).
+    tableWidthConstraints.maximum = std::max(tableWidthConstraints.maximum, percentMaximumConstraint);
+    // Expand the preferred width with leading and trailing cell spacing (note that column spanners count as one cell).
     tableWidthConstraints += (numberOfActualColumns + 1) * grid.horizontalSpacing();
     return tableWidthConstraints;
 }

Modified: trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.h (278604 => 278605)


--- trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.h	2021-06-08 13:53:03 UTC (rev 278604)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.h	2021-06-08 14:02:56 UTC (rev 278605)
@@ -73,9 +73,12 @@
         void setLogicalWidth(LayoutUnit);
         LayoutUnit logicalWidth() const;
 
-        void setFixedWidth(LayoutUnit fixedValue) { m_fixedWidth = fixedValue; }
+        void setFixedWidth(LayoutUnit fixedValue);
         std::optional<LayoutUnit> fixedWidth() const { return m_fixedWidth; }
 
+        void setPercent(float);
+        std::optional<float> percent() const { return m_percent; }
+
         const ContainerBox* box() const { return m_layoutBox.get(); }
 
     private:
@@ -82,6 +85,7 @@
         LayoutUnit m_computedLogicalWidth;
         LayoutUnit m_computedLogicalLeft;
         std::optional<LayoutUnit> m_fixedWidth;
+        std::optional<float> m_percent;
         WeakPtr<const ContainerBox> m_layoutBox;
 
 #if ASSERT_ENABLED
@@ -230,7 +234,20 @@
     std::optional<Edges> m_collapsedBorder;
 };
 
+
+inline void TableGrid::Column::setFixedWidth(LayoutUnit fixedValue)
+{
+    ASSERT(!m_percent);
+    m_fixedWidth = fixedValue;
 }
+
+inline void TableGrid::Column::setPercent(float percent)
+{
+    ASSERT(!m_fixedWidth);
+    m_percent = percent;
 }
 
+}
+}
+
 #endif

Modified: trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp (278604 => 278605)


--- trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp	2021-06-08 13:53:03 UTC (rev 278604)
+++ trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp	2021-06-08 14:02:56 UTC (rev 278605)
@@ -247,8 +247,11 @@
         float minimumWidth = slot.widthConstraints().minimum;
         float maximumWidth = slot.widthConstraints().maximum;
 
-        if (auto fixedWidth = m_grid.columns().list()[columnIndex].fixedWidth())
+        auto& column = m_grid.columns().list()[columnIndex];
+        if (auto fixedWidth = column.fixedWidth())
             maximumWidth = std::max<float>(minimumWidth, *fixedWidth);
+        else if (auto percent = column.percent())
+            maximumWidth = std::max(minimumWidth, *percent * availableHorizontalSpace / 100.0f);
 
         if (columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth) {
             ASSERT(maximumWidth >= minimumWidth);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to