⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 278605 in webkit


Ignore:
Timestamp:
Jun 8, 2021, 7:02:56 AM (5 years ago)
Author:
Alan Bujtas
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.

  1. 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.

  1. 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).

  1. 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).

  1. 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.
Location:
trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r278604 r278605  
     12021-06-08  Alan Bujtas  <zalan@apple.com>
     2
     3        [LFC][TFC] Add initial percent value support for columns
     4        https://bugs.webkit.org/show_bug.cgi?id=226751
     5
     6        Reviewed by Simon Fraser.
     7
     8        * fast/layoutformattingcontext/table-with-percent-columns-only-no-content-expected.html: Added.
     9        * fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html: Added.
     10
    1112021-06-08  Diego Pino Garcia  <dpino@igalia.com>
    212
  • trunk/Source/WebCore/ChangeLog

    r278603 r278605  
     12021-06-08  Alan Bujtas  <zalan@apple.com>
     2
     3        [LFC][TFC] Add initial percent value support for columns
     4        https://bugs.webkit.org/show_bug.cgi?id=226751
     5
     6        Reviewed by Simon Fraser.
     7
     8        This patch adds the initial support for content like this:
     9        <table>
     10          <tr>
     11            <td style="width: 10%"></td><td style="width: 90%"></td>
     12          </tr>
     13        </table>
     14
     15        Percent values work in mysterious ways in cases when the table has no fixed width.
     16
     17        1. The smaller the percent value is, the wider the table may become.
     18
     19         Percent values are resolved against the cell's border box (so essentially they are resolved
     20         against their own content as opposed to the table/containing block) and the formula is slightly different.
     21
     22            <td style="padding: 5px; width: 20%;"></td> : produces a 10px wide border box (horizontal border: 0px, padding: 10px, content: 0px).
     23              The maximum constraint is resolved to 50px (width / percent * 100)
     24            <td style="padding: 5px; width: 100%;"></td> : produces a 10px wide border box and the maximum constraint is resolved to 10px.
     25
     26          This maximum constraint value turns into the available width for the table content and becomes the final table width.
     27
     28        2. With multiple rows, we pick the highest _percent_ value for each column (as opposed to the resolved values).
     29
     30              <tr><td style="width: 20%"></td></tr> (assum same 5px padding on both sides)
     31              <tr><td style="width: 80%"></td></tr>
     32
     33          While the second row's cell has a higher maximum constraint value (50px see #1) since we only look at the raw percent values,
     34          this content only produces a 12.5px wide table.
     35
     36        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.
     37
     38            <tr><td style="width: 60%"></td><td style="width: 40%"></td></tr>
     39
     40          60% resolves to 16.6px
     41          40% resolves to 25px and we use the 25px value as the width for the entire table (and not 16.6px + 25px).
     42
     43        4. Since we pick the highest percent values across rows for each columns, we may end up with > 100%.
     44          In such cases we start dropping percent values for subsequent columns:
     45
     46            <tr><td style="width: 20%;"></td><td style="width: 80%;"></td></tr>
     47            <tr><td style="width: 60%;"></td><td style="width: 10%;"></td></tr>
     48
     49          First column width is max(20%, 60%) -> 60%
     50          Second column width is max(80%, 10%) -> 80%
     51          As we limit the accumulated percent value to 100%, the final column percent values are 60% and 40% (and not 80%).
     52          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)
     53          the final table width is 25px (based on a percent value which is not even in the markup).
     54
     55        5. While the smaller percent values produce wider tables (see #1), during the available space distribution
     56          columns with smaller percent values get assigned less space.
     57
     58            <tr><td style="width: 1%"></td><td style="width: 99%"></td></tr>
     59
     60          This content produces a 1000px wide table due to the small (1%) percent value (see #1 #2 and #3).
     61          When we distribute the available space (1000px), the first cell gets only 10px (1%) while the second cell ends up with 990px (99%).
     62
     63        (and this is the cherry on top (not included in this patch):
     64         Imagine the following scenario:
     65           1. the accumulated column percent value > 100% (let's say 80% and 30%)
     66           2. as we reach the 100% while walking the columns one by one (see #4), the remaining percent value becomes 0%.
     67           3. In order to avoid division by 0, we pick a very small epsilon value to run the formula.
     68           4. Now this very small percent value produces a large resolved value (see #2) which means
     69
     70            <tr><td style="width: 100%"></td></tr>
     71
     72            produces a 10px wide table
     73
     74            <tr><td style="width: 100%"></td><td style="width: 1%"></td></tr> <- note the 1%
     75
     76            produces a very very very wide table.
     77        )
     78
     79        Test: fast/layoutformattingcontext/table-with-percent-columns-only-no-content.html
     80
     81        * layout/formattingContexts/table/TableFormattingContext.cpp:
     82        (WebCore::Layout::TableFormattingContext::computedIntrinsicWidthConstraints):
     83        (WebCore::Layout::TableFormattingContext::computedPreferredWidthForColumns):
     84        * layout/formattingContexts/table/TableGrid.h:
     85        (WebCore::Layout::TableGrid::Column::percent const):
     86        (WebCore::Layout::TableGrid::Column::setFixedWidth):
     87        (WebCore::Layout::TableGrid::Column::setPercent):
     88        * layout/formattingContexts/table/TableLayout.cpp:
     89        (WebCore::Layout::TableFormattingContext::TableLayout::distributedHorizontalSpace):
     90
    1912021-06-08  Jean-Yves Avenard  <jya@apple.com>
    292
  • trunk/Source/WebCore/layout/formattingContexts/table/TableFormattingContext.cpp

    r278537 r278605  
    297297{
    298298    ASSERT(!root().isSizeContainmentBox());
    299     // Tables have a slighty different concept of shrink to fit. It's really only different with non-auto "width" values, where
     299    // Tables have a slightly different concept of shrink to fit. It's really only different with non-auto "width" values, where
    300300    // a generic shrink-to fit block level box like a float box would be just sized to the computed value of "width", tables
    301     // can actually be streched way over.
     301    // can actually be stretched way over.
    302302    auto& grid = formattingState().tableGrid();
    303303    if (auto computedWidthConstraints = grid.widthConstraints())
     
    329329            auto* columnBox = column.box();
    330330            if (!columnBox) {
    331                 // Anoynmous columns don't have associated layout boxes and can't have fixed col size.
     331                // Anonymous columns don't have associated layout boxes and can't have fixed col size.
    332332                return { };
    333333            }
     
    340340    }
    341341
     342    Vector<std::optional<float>> columnPercentList(columnList.size());
    342343    for (auto& cell : grid.cells()) {
    343344        auto& cellBox = cell->box();
     
    360361            *fixedWidth += horizontalBorderAndPaddingWidth;
    361362            columnList[cellPosition.column].setFixedWidth(std::max(*fixedWidth, columnList[cellPosition.column].fixedWidth().value_or(0)));
     363        }
     364        // Collect the percent values so that we can compute the maximum values per column.
     365        auto& cellLogicalWidth = cellBox.style().logicalWidth();
     366        if (cellLogicalWidth.isPercent()) {
     367            // FIXME: Add support for column spanning distribution.
     368            columnPercentList[cellPosition.column] = std::max(cellLogicalWidth.percent(), columnPercentList[cellPosition.column].value_or(0.0f));
    362369        }
    363370    }
     
    412419    }
    413420
    414     // 5. The final table min/max widths is just the accumulated column constraints.
     421    // 5. Resolve the percent values.
     422    auto remainingPercent = 100.0f;
     423    auto percentMaximumConstraint = LayoutUnit { };
     424    for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
     425        auto percent = columnPercentList[columnIndex];
     426        // FIXME: Add support for mixed content with and without percent values.
     427        if (!percent)
     428            continue;
     429        ASSERT(*percent > 0);
     430        columnList[columnIndex].setPercent(std::min(remainingPercent, *percent));
     431        percentMaximumConstraint = std::max(percentMaximumConstraint, LayoutUnit { columnIntrinsicWidths[columnIndex].maximum * 100.0f / *columnList[columnIndex].percent() });
     432        remainingPercent -= *columnList[columnIndex].percent();
     433    }
     434
     435    // 6. The final table min/max widths is just the accumulated column constraints.
    415436    auto tableWidthConstraints = IntrinsicWidthConstraints { };
    416437    for (auto& columnIntrinsicWidth : columnIntrinsicWidths)
    417438        tableWidthConstraints += columnIntrinsicWidth;
    418     // Exapand the preferred width with leading and trailing cell spacing (note that column spanners count as one cell).
     439    tableWidthConstraints.maximum = std::max(tableWidthConstraints.maximum, percentMaximumConstraint);
     440    // Expand the preferred width with leading and trailing cell spacing (note that column spanners count as one cell).
    419441    tableWidthConstraints += (numberOfActualColumns + 1) * grid.horizontalSpacing();
    420442    return tableWidthConstraints;
  • trunk/Source/WebCore/layout/formattingContexts/table/TableGrid.h

    r278537 r278605  
    7474        LayoutUnit logicalWidth() const;
    7575
    76         void setFixedWidth(LayoutUnit fixedValue) { m_fixedWidth = fixedValue; }
     76        void setFixedWidth(LayoutUnit fixedValue);
    7777        std::optional<LayoutUnit> fixedWidth() const { return m_fixedWidth; }
     78
     79        void setPercent(float);
     80        std::optional<float> percent() const { return m_percent; }
    7881
    7982        const ContainerBox* box() const { return m_layoutBox.get(); }
     
    8386        LayoutUnit m_computedLogicalLeft;
    8487        std::optional<LayoutUnit> m_fixedWidth;
     88        std::optional<float> m_percent;
    8589        WeakPtr<const ContainerBox> m_layoutBox;
    8690
     
    231235};
    232236
     237
     238inline void TableGrid::Column::setFixedWidth(LayoutUnit fixedValue)
     239{
     240    ASSERT(!m_percent);
     241    m_fixedWidth = fixedValue;
     242}
     243
     244inline void TableGrid::Column::setPercent(float percent)
     245{
     246    ASSERT(!m_fixedWidth);
     247    m_percent = percent;
     248}
     249
    233250}
    234251}
  • trunk/Source/WebCore/layout/formattingContexts/table/TableLayout.cpp

    r278537 r278605  
    248248        float maximumWidth = slot.widthConstraints().maximum;
    249249
    250         if (auto fixedWidth = m_grid.columns().list()[columnIndex].fixedWidth())
     250        auto& column = m_grid.columns().list()[columnIndex];
     251        if (auto fixedWidth = column.fixedWidth())
    251252            maximumWidth = std::max<float>(minimumWidth, *fixedWidth);
     253        else if (auto percent = column.percent())
     254            maximumWidth = std::max(minimumWidth, *percent * availableHorizontalSpace / 100.0f);
    252255
    253256        if (columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth) {
Note: See TracChangeset for help on using the changeset viewer.