Title: [259984] trunk/Source/WebCore
Revision
259984
Author
[email protected]
Date
2020-04-12 11:32:07 -0700 (Sun, 12 Apr 2020)

Log Message

[LFC][TFC] Add support for column spanners
https://bugs.webkit.org/show_bug.cgi?id=210403

Reviewed by Antti Koivisto.

Table width constraint computation with spanner support is as follows:

1. Collect each cells' width constraints.
2. Collect fixed column widths set by <colgroup>'s and <col>s.
3. Find the min/max width for each columns using the cell constraints and the <col> fixed widths but ignore column spans.
4. Distribute column spanning cells min/max widths.
5. Add them all up and return the computed min/max widths.

* layout/FormattingContext.h:
(WebCore::Layout::FormattingContext::IntrinsicWidthConstraints::operator-=):
* layout/tableformatting/TableFormattingContext.cpp:
(WebCore::Layout::TableFormattingContext::computedPreferredWidthForColumns):
(WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace):
* layout/tableformatting/TableFormattingContextGeometry.cpp:
(WebCore::Layout::TableFormattingContext::Geometry::intrinsicWidthConstraintsForCell):
* layout/tableformatting/TableGrid.cpp:
(WebCore::Layout::TableGrid::Columns::hasFixedColumnsOnly const):
* layout/tableformatting/TableGrid.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (259983 => 259984)


--- trunk/Source/WebCore/ChangeLog	2020-04-12 17:53:37 UTC (rev 259983)
+++ trunk/Source/WebCore/ChangeLog	2020-04-12 18:32:07 UTC (rev 259984)
@@ -1,5 +1,31 @@
 2020-04-12  Zalan Bujtas  <[email protected]>
 
+        [LFC][TFC] Add support for column spanners
+        https://bugs.webkit.org/show_bug.cgi?id=210403
+
+        Reviewed by Antti Koivisto.
+
+        Table width constraint computation with spanner support is as follows:
+
+        1. Collect each cells' width constraints.
+        2. Collect fixed column widths set by <colgroup>'s and <col>s.
+        3. Find the min/max width for each columns using the cell constraints and the <col> fixed widths but ignore column spans.
+        4. Distribute column spanning cells min/max widths.
+        5. Add them all up and return the computed min/max widths.
+
+        * layout/FormattingContext.h:
+        (WebCore::Layout::FormattingContext::IntrinsicWidthConstraints::operator-=):
+        * layout/tableformatting/TableFormattingContext.cpp:
+        (WebCore::Layout::TableFormattingContext::computedPreferredWidthForColumns):
+        (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace):
+        * layout/tableformatting/TableFormattingContextGeometry.cpp:
+        (WebCore::Layout::TableFormattingContext::Geometry::intrinsicWidthConstraintsForCell):
+        * layout/tableformatting/TableGrid.cpp:
+        (WebCore::Layout::TableGrid::Columns::hasFixedColumnsOnly const):
+        * layout/tableformatting/TableGrid.h:
+
+2020-04-12  Zalan Bujtas  <[email protected]>
+
         [LFC][TFC] Column, Row and Cell boxes are always ContainerBoxes
         https://bugs.webkit.org/show_bug.cgi?id=210402
 

Modified: trunk/Source/WebCore/layout/FormattingContext.h (259983 => 259984)


--- trunk/Source/WebCore/layout/FormattingContext.h	2020-04-12 17:53:37 UTC (rev 259983)
+++ trunk/Source/WebCore/layout/FormattingContext.h	2020-04-12 18:32:07 UTC (rev 259984)
@@ -71,6 +71,7 @@
     struct IntrinsicWidthConstraints {
         void expand(LayoutUnit horizontalValue);
         IntrinsicWidthConstraints& operator+=(const IntrinsicWidthConstraints&);
+        IntrinsicWidthConstraints& operator-=(const IntrinsicWidthConstraints&);
         IntrinsicWidthConstraints& operator-=(LayoutUnit);
 
         LayoutUnit minimum;
@@ -229,6 +230,13 @@
     return *this;
 }
 
+inline FormattingContext::IntrinsicWidthConstraints& FormattingContext::IntrinsicWidthConstraints::operator-=(const IntrinsicWidthConstraints& other)
+{
+    minimum -= other.minimum;
+    maximum -= other.maximum;
+    return *this;
+}
+
 inline FormattingContext::IntrinsicWidthConstraints& FormattingContext::IntrinsicWidthConstraints::operator-=(LayoutUnit value)
 {
     minimum -= value;

Modified: trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp (259983 => 259984)


--- trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp	2020-04-12 17:53:37 UTC (rev 259983)
+++ trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp	2020-04-12 18:32:07 UTC (rev 259984)
@@ -241,9 +241,12 @@
     auto& grid = formattingState.tableGrid();
     ASSERT(!grid.widthConstraints());
 
-    // 1. Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box.
-    //    If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum cell width.
-    //    Also, calculate the "maximum" cell width of each cell: formatting the content without breaking lines other than where explicit line breaks occur.
+    // Column preferred width computation as follows:
+    // 1. Collect each cells' width constraints
+    // 2. Collect fixed column widths set by <colgroup>'s and <col>s
+    // 3. Find the min/max width for each columns using the cell constraints and the <col> fixed widths but ignore column spans.
+    // 4. Distribute column spanning cells min/max widths.
+    // 5. Add them all up and return the computed min/max widths.
     for (auto& cell : grid.cells()) {
         auto& cellBox = cell->box();
         ASSERT(cellBox.establishesBlockFormattingContext());
@@ -257,13 +260,14 @@
         grid.slot(cell->position())->setWidthConstraints(*intrinsicWidth);
     }
 
+    // 2. Collect the fixed width <col>s.
     auto& columnList = grid.columns().list();
     Vector<Optional<LayoutUnit>> fixedWidthColumns;
-    for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
+    for (auto& column : columnList) {
         auto fixedWidth = [&] () -> Optional<LayoutUnit> {
-            auto* columnBox = columnList[columnIndex].box();
+            auto* columnBox = column.box();
             if (!columnBox) {
-                // Anoynmous columns don't have associated layout boxes.
+                // Anoynmous columns don't have associated layout boxes and can't have fixed col size.
                 return { };
             }
             if (auto width = columnBox->columnWidth())
@@ -274,33 +278,43 @@
     }
 
     Vector<FormattingContext::IntrinsicWidthConstraints> columnIntrinsicWidths;
-    // Collect he min/max width for each column and finalize the preferred width for the table.
+    // 3. Collect he min/max width for each column but ignore column spans for now.
+    Vector<SlotPosition> spanningCellPositionList;
     for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
         columnIntrinsicWidths.append(FormattingContext::IntrinsicWidthConstraints { });
         for (size_t rowIndex = 0; rowIndex < grid.rows().size(); ++rowIndex) {
             auto& slot = *grid.slot({ columnIndex, rowIndex });
             if (slot.hasColumnSpan()) {
-                auto& cell = slot.cell();
-                auto widthConstraintsToDistribute = slot.widthConstraints();
-                auto numberOfNonFixedSpannedColumns = cell.columnSpan();
-                for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex) {
-                    if (auto fixedWidth = fixedWidthColumns[columnSpanIndex]) {
-                        widthConstraintsToDistribute -= *fixedWidth;
-                        --numberOfNonFixedSpannedColumns;
-                    }
-                }
-                for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex) {
-                    columnIntrinsicWidths[columnIndex].minimum = std::max(widthConstraintsToDistribute.minimum / numberOfNonFixedSpannedColumns, columnIntrinsicWidths[columnIndex].minimum);
-                    columnIntrinsicWidths[columnIndex].maximum = std::max(widthConstraintsToDistribute.maximum / numberOfNonFixedSpannedColumns, columnIntrinsicWidths[columnIndex].maximum);
-                }
-            } else {
-                auto columnFixedWidth = fixedWidthColumns[columnIndex];
-                auto widthConstraints = !columnFixedWidth ? slot.widthConstraints() : FormattingContext::IntrinsicWidthConstraints { *columnFixedWidth, *columnFixedWidth };
-                columnIntrinsicWidths[columnIndex].minimum = std::max(widthConstraints.minimum, columnIntrinsicWidths[columnIndex].minimum);
-                columnIntrinsicWidths[columnIndex].maximum = std::max(widthConstraints.maximum, columnIntrinsicWidths[columnIndex].maximum);
+                spanningCellPositionList.append({ columnIndex, rowIndex });
+                continue;
             }
+            if (slot.isColumnSpanned())
+                continue;
+            auto columnFixedWidth = fixedWidthColumns[columnIndex];
+            auto widthConstraints = !columnFixedWidth ? slot.widthConstraints() : FormattingContext::IntrinsicWidthConstraints { *columnFixedWidth, *columnFixedWidth };
+            columnIntrinsicWidths[columnIndex].minimum = std::max(widthConstraints.minimum, columnIntrinsicWidths[columnIndex].minimum);
+            columnIntrinsicWidths[columnIndex].maximum = std::max(widthConstraints.maximum, columnIntrinsicWidths[columnIndex].maximum);
         }
     }
+
+    // 4. Distribute the spanning min/max widths.
+    for (auto spanningCellPosition : spanningCellPositionList) {
+        auto& slot = *grid.slot(spanningCellPosition);
+        auto& cell = slot.cell();
+        ASSERT(slot.hasColumnSpan());
+        auto widthConstraintsToDistribute = slot.widthConstraints();
+        for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
+            widthConstraintsToDistribute -= columnIntrinsicWidths[columnSpanIndex];
+        // FIXME: Check if fixed width columns should be skipped here.
+        widthConstraintsToDistribute.minimum = std::max(LayoutUnit { }, widthConstraintsToDistribute.minimum / cell.columnSpan());
+        widthConstraintsToDistribute.maximum = std::max(LayoutUnit { }, widthConstraintsToDistribute.maximum / cell.columnSpan());
+        if (widthConstraintsToDistribute.minimum || widthConstraintsToDistribute.maximum) {
+            for (size_t columnSpanIndex = cell.startColumn(); columnSpanIndex < cell.endColumn(); ++columnSpanIndex)
+                columnIntrinsicWidths[columnSpanIndex] += widthConstraintsToDistribute;
+        }
+    }
+
+    // 5. The final table min/max widths is just the accumulated column constraints.
     auto tableWidthConstraints = IntrinsicWidthConstraints { };
     for (auto& columnIntrinsicWidth : columnIntrinsicWidths)
         tableWidthConstraints += columnIntrinsicWidth;
@@ -314,19 +328,16 @@
     auto& rows = grid.rows();
     auto tableWidthConstraints = *grid.widthConstraints();
 
-    // Column and caption widths influence the final table width as follows:
-    // If the 'table' or 'inline-table' element's 'width' property has a computed value (W) other than 'auto', the used width is the greater of
-    // W, CAPMIN, and the minimum width required by all the columns plus cell spacing or borders (MIN).
-    // If the used width is greater than MIN, the extra width should be distributed over the columns.
-    // If the 'table' or 'inline-table' element has 'width: auto', the used width is the greater of the table's containing block width,
-    // CAPMIN, and MIN. However, if either CAPMIN or the maximum width required by the columns plus cell spacing or borders (MAX) is
-    // less than that of the containing block, use max(MAX, CAPMIN).
     auto distributeExtraHorizontalSpace = [&] (auto horizontalSpaceToDistribute) {
         auto& columnList = grid.columns().list();
         ASSERT(!columnList.isEmpty());
 
-        // 1. Collect minimum widths across columns but ignore spanning cells first.
-        Vector<float> columnMinimumWidths;
+        // 1. Collect minimum widths driven by <td> across columns but ignore spanning cells first.
+        struct ColumnMinimumWidth {
+            float value { 0 };
+            bool shouldFlex { true };
+        };
+        Vector<ColumnMinimumWidth> columnMinimumWidths;
         Vector<SlotPosition> spanningCellPositionList;
         for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
             for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
@@ -338,12 +349,25 @@
                 if (slot.isColumnSpanned())
                     continue;
                 if (!rowIndex)
-                    columnMinimumWidths.append(slot.widthConstraints().minimum);
+                    columnMinimumWidths.append({ slot.widthConstraints().minimum, !columns.list()[columnIndex].isFixedWidth() });
                 else
-                    columnMinimumWidths[columnIndex] = std::max<float>(columnMinimumWidths[columnIndex], slot.widthConstraints().minimum);
+                    columnMinimumWidths[columnIndex].value = std::max<float>(columnMinimumWidths[columnIndex].value, slot.widthConstraints().minimum);
             }
         }
-        // 2. Distribute the spanning cells' mimimum widths across the columns using the non-spanning minium widths.
+        // 2. Adjust the minimum width with fixed column widths (<col> vs. <td>) and also manage all-fixed-width-column content.
+        auto hasFixedColumnsOnly = columns.hasFixedColumnsOnly();
+        for (size_t columnIndex = 0; columnIndex < columnList.size(); ++columnIndex) {
+            auto& column = columnList[columnIndex];
+            if (!column.isFixedWidth())
+                continue;
+            // This is the column width based on <col width=""> and not <td style="width: ">.
+            auto columnFixedWidth = column.box() ? column.box()->columnWidth() : WTF::nullopt; 
+            columnMinimumWidths[columnIndex].value = std::max(columnMinimumWidths[columnIndex].value, columnFixedWidth.valueOr(0).toFloat());
+            // Fixed columns flex when there are no other flexable columns.
+            columnMinimumWidths[columnIndex].shouldFlex = hasFixedColumnsOnly | columnMinimumWidths[columnIndex].shouldFlex;
+        }
+
+        // 3. Distribute the spanning cells' mimimum widths across the columns using the non-spanning minimum widths.
         // e.g. [ 1 ][ 5 ][ 1 ]
         //      [    9   ][ 1 ]
         // The minimum widths are: [ 2 ][ 7 ][ 1 ]
@@ -354,7 +378,7 @@
             float currentSpanningMinimumWidth = 0;
             // 1. Collect the non-spaning minimum widths.
             for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
-                currentSpanningMinimumWidth += columnMinimumWidths[columnIndex];
+                currentSpanningMinimumWidth += columnMinimumWidths[columnIndex].value;
             float spanningMinimumWidth = slot.widthConstraints().minimum;
             if (currentSpanningMinimumWidth >= spanningMinimumWidth) {
                 // The spanning cell fits the spanned columns just fine. Nothing to distribute.
@@ -365,30 +389,29 @@
             // New minimum widths: [ 3 ] [ 6 ].
             auto spaceToDistribute = spanningMinimumWidth - currentSpanningMinimumWidth;
             for (auto columnIndex = cell.startColumn(); columnIndex < cell.endColumn(); ++columnIndex)
-                columnMinimumWidths[columnIndex] += spaceToDistribute / currentSpanningMinimumWidth * columnMinimumWidths[columnIndex];
+                columnMinimumWidths[columnIndex].value += spaceToDistribute / currentSpanningMinimumWidth * columnMinimumWidths[columnIndex].value;
         }
         // 3. Distribute the extra space using the final minimum widths.
-        float adjustabledHorizontalSpace = tableWidthConstraints.minimum - grid.totalHorizontalSpacing();
-        auto numberOfColumns = columnList.size();
         // Fixed width columns don't participate in available space distribution.
-        for (auto& column : columnList) {
-            if (!column.isFixedWidth())
+        // Unless there are no flexing column at all, then they start flexing as if they were not fixed at all.
+        float adjustabledHorizontalSpace = 0;
+        for (auto& columnMinimumWidth : columnMinimumWidths) {
+            if (!columnMinimumWidth.shouldFlex)
                 continue;
-            auto columnFixedWidth = *column.box()->columnWidth();
-            column.setLogicalWidth(columnFixedWidth);
-
-            --numberOfColumns;
-            adjustabledHorizontalSpace -= columnFixedWidth;
+            adjustabledHorizontalSpace += columnMinimumWidth.value;
         }
-        if (!numberOfColumns || !adjustabledHorizontalSpace)
+        if (!adjustabledHorizontalSpace)
             return;
-        ASSERT(adjustabledHorizontalSpace > 0);
+        // FIXME: Implement overconstrained columns when fixed width content is wider than the table.
         for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
             auto& column = columns.list()[columnIndex];
-            if (column.isFixedWidth())
+            auto minimumWidth = columnMinimumWidths[columnIndex].value;
+            if (!columnMinimumWidths[columnIndex].shouldFlex) {
+                column.setLogicalWidth(LayoutUnit { minimumWidth });
                 continue;
-            auto columnExtraSpace = horizontalSpaceToDistribute / adjustabledHorizontalSpace * columnMinimumWidths[columnIndex];
-            column.setLogicalWidth(LayoutUnit { columnMinimumWidths[columnIndex] + columnExtraSpace });
+            }
+            auto columnExtraSpace = horizontalSpaceToDistribute / adjustabledHorizontalSpace * minimumWidth;
+            column.setLogicalWidth(LayoutUnit { minimumWidth + columnExtraSpace });
         }
     };
 

Modified: trunk/Source/WebCore/layout/tableformatting/TableGrid.cpp (259983 => 259984)


--- trunk/Source/WebCore/layout/tableformatting/TableGrid.cpp	2020-04-12 17:53:37 UTC (rev 259983)
+++ trunk/Source/WebCore/layout/tableformatting/TableGrid.cpp	2020-04-12 18:32:07 UTC (rev 259984)
@@ -83,6 +83,15 @@
     m_columnList.append({ nullptr });
 }
 
+bool TableGrid::Columns::hasFixedColumnsOnly() const
+{
+    for (auto& column : m_columnList) {
+        if (!column.isFixedWidth())
+            return false;
+    }
+    return true;
+}
+
 void TableGrid::Rows::addRow(const ContainerBox& rowBox)
 {
     m_rowList.append({ rowBox });

Modified: trunk/Source/WebCore/layout/tableformatting/TableGrid.h (259983 => 259984)


--- trunk/Source/WebCore/layout/tableformatting/TableGrid.h	2020-04-12 17:53:37 UTC (rev 259983)
+++ trunk/Source/WebCore/layout/tableformatting/TableGrid.h	2020-04-12 18:32:07 UTC (rev 259984)
@@ -100,6 +100,7 @@
         void addAnonymousColumn();
 
         LayoutUnit logicalWidth() const { return m_columnList.last().logicalRight() - m_columnList.first().logicalLeft(); }
+        bool hasFixedColumnsOnly() const;
 
     private:
         ColumnList m_columnList;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to