Diff
Modified: trunk/Source/WebCore/ChangeLog (261744 => 261745)
--- trunk/Source/WebCore/ChangeLog 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/ChangeLog 2020-05-15 14:44:52 UTC (rev 261745)
@@ -1,3 +1,51 @@
+2020-05-15 Zalan Bujtas <[email protected]>
+
+ [LFC][TFC] Move column and row balancing logic to a dedicated class
+ https://bugs.webkit.org/show_bug.cgi?id=211937
+
+ Reviewed by Antti Koivisto.
+
+ * Sources.txt:
+ * WebCore.xcodeproj/project.pbxproj:
+ * layout/tableformatting/TableFormattingContext.cpp:
+ (WebCore::Layout::TableFormattingContext::layoutInFlowContent):
+ (WebCore::Layout::TableFormattingContext::setUsedGeometryForRows):
+ (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraSpace):
+ (WebCore::Layout::ColumnSpan::hasSpan): Deleted.
+ (WebCore::Layout::ColumnSpan::isSpanned): Deleted.
+ (WebCore::Layout::ColumnSpan::spanCount): Deleted.
+ (WebCore::Layout::ColumnSpan::startSpan): Deleted.
+ (WebCore::Layout::ColumnSpan::endSpan): Deleted.
+ (WebCore::Layout::ColumnSpan::index): Deleted.
+ (WebCore::Layout::ColumnSpan::size): Deleted.
+ (WebCore::Layout::ColumnSpan::spacing): Deleted.
+ (WebCore::Layout::RowSpan::hasSpan): Deleted.
+ (WebCore::Layout::RowSpan::isSpanned): Deleted.
+ (WebCore::Layout::RowSpan::spanCount): Deleted.
+ (WebCore::Layout::RowSpan::startSpan): Deleted.
+ (WebCore::Layout::RowSpan::endSpan): Deleted.
+ (WebCore::Layout::RowSpan::index): Deleted.
+ (WebCore::Layout::RowSpan::size): Deleted.
+ (WebCore::Layout::RowSpan::spacing): Deleted.
+ (WebCore::Layout::GridSpace::isEmpty const): Deleted.
+ (): Deleted.
+ (WebCore::Layout::max): Deleted.
+ (WebCore::Layout::operator-): Deleted.
+ (WebCore::Layout::operator+=): Deleted.
+ (WebCore::Layout::operator-=): Deleted.
+ (WebCore::Layout::operator/): Deleted.
+ (WebCore::Layout::distributeAvailableSpace): Deleted.
+ (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraHorizontalSpace): Deleted.
+ (WebCore::Layout::TableFormattingContext::computeAndDistributeExtraVerticalSpace): Deleted.
+ * layout/tableformatting/TableFormattingContext.h:
+ * layout/tableformatting/TableFormattingState.h:
+ (WebCore::Layout::TableFormattingState::tableGrid const):
+ * layout/tableformatting/TableGrid.h:
+ (WebCore::Layout::TableGrid::widthConstraints const):
+ (WebCore::Layout::TableGrid::Rows::list const):
+ (WebCore::Layout::TableGrid::widthConstraints): Deleted.
+ (WebCore::Layout::TableGrid::Rows::rowList const): Deleted.
+
2020-05-15 Alicia Boya GarcĂa <[email protected]>
[GStreamer][MediaStream] Fix missing video size
Modified: trunk/Source/WebCore/Sources.txt (261744 => 261745)
--- trunk/Source/WebCore/Sources.txt 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/Sources.txt 2020-05-15 14:44:52 UTC (rev 261745)
@@ -1494,6 +1494,7 @@
layout/tableformatting/TableFormattingContextGeometry.cpp
layout/tableformatting/TableFormattingState.cpp
layout/tableformatting/TableGrid.cpp
+layout/tableformatting/TableLayout.cpp
loader/AdClickAttribution.cpp
loader/CanvasActivityRecord.cpp
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (261744 => 261745)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-05-15 14:44:52 UTC (rev 261745)
@@ -9491,6 +9491,7 @@
6FE8E6F8237BA6B200758D26 /* InvalidationState.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InvalidationState.cpp; sourceTree = "<group>"; };
6FE9F09222211035004C5082 /* ContentChangeObserver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentChangeObserver.cpp; sourceTree = "<group>"; };
6FEFE81D22F9D22A00114927 /* LayoutPhase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutPhase.h; sourceTree = "<group>"; };
+ 6FF9F1BE246D966C00435083 /* TableLayout.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TableLayout.cpp; sourceTree = "<group>"; };
6FFA4BFE23F2FECD007E4EBC /* LayoutLineBreakBox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutLineBreakBox.cpp; sourceTree = "<group>"; };
6FFA4C0023F2FED9007E4EBC /* LayoutLineBreakBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LayoutLineBreakBox.h; sourceTree = "<group>"; };
6FFDC43E212EFF1600A9CA91 /* FloatAvoider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FloatAvoider.cpp; sourceTree = "<group>"; };
@@ -21265,6 +21266,7 @@
children = (
6FC5CA9422E3599400B13E11 /* TableFormattingContext.cpp */,
6FC5CA9522E3599400B13E11 /* TableFormattingContext.h */,
+ 6FF9F1BE246D966C00435083 /* TableLayout.cpp */,
11D19C2E23159BAE008F24D3 /* TableFormattingContextGeometry.cpp */,
6FC5CA9222E3599300B13E11 /* TableFormattingState.cpp */,
6FC5CA9622E3599500B13E11 /* TableFormattingState.h */,
Modified: trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp (261744 => 261745)
--- trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.cpp 2020-05-15 14:44:52 UTC (rev 261745)
@@ -53,8 +53,7 @@
auto availableHorizontalSpace = constraints.horizontal.logicalWidth;
auto availableVerticalSpace = constraints.vertical.logicalHeight;
// 1. Compute width and height for the grid.
- computeAndDistributeExtraHorizontalSpace(availableHorizontalSpace);
- computeAndDistributeExtraVerticalSpace(availableHorizontalSpace, availableVerticalSpace);
+ computeAndDistributeExtraSpace(availableHorizontalSpace, availableVerticalSpace);
// 2. Finalize cells.
setUsedGeometryForCells(availableHorizontalSpace);
// 3. Finalize rows.
@@ -111,9 +110,11 @@
void TableFormattingContext::setUsedGeometryForRows(LayoutUnit availableHorizontalSpace)
{
auto& grid = formattingState().tableGrid();
+ auto& rows = grid.rows().list();
+
auto rowWidth = grid.columns().logicalWidth() + 2 * grid.horizontalSpacing();
auto rowLogicalTop = grid.verticalSpacing();
- for (auto& row : grid.rows().list()) {
+ for (auto& row : rows) {
auto& rowBox = row.box();
auto& rowDisplayBox = formattingState().displayBox(rowBox);
computeBorderAndPadding(rowBox, HorizontalConstraints { { }, availableHorizontalSpace });
@@ -130,6 +131,23 @@
row.setLogicalTop(rowLogicalTop);
rowLogicalTop += row.logicalHeight() + grid.verticalSpacing();
}
+
+ auto& columns = grid.columns();
+ Vector<InlineLayoutUnit> rowBaselines(rows.size());
+ // Now that cells are laid out, let's compute the row baselines.
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+ for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+ auto& slot = *grid.slot({ columnIndex, rowIndex });
+ if (slot.isRowSpanned())
+ continue;
+ if (slot.hasRowSpan())
+ continue;
+ auto& cell = slot.cell();
+ rowBaselines[rowIndex] = std::max(rowBaselines[rowIndex], cell.baselineOffset());
+ }
+ }
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex)
+ rows[rowIndex].setBaselineOffset(rowBaselines[rowIndex]);
}
void TableFormattingContext::setUsedGeometryForSections(const ConstraintsForInFlowContent& constraints)
@@ -350,259 +368,29 @@
return tableWidthConstraints;
}
-struct ColumnSpan {
- static size_t hasSpan(const TableGrid::Slot& slot) { return slot.hasColumnSpan(); }
- static size_t isSpanned(const TableGrid::Slot& slot) { return slot.isColumnSpanned(); }
-
- static size_t spanCount(const TableGrid::Cell& cell) { return cell.columnSpan(); }
- static size_t startSpan(const TableGrid::Cell& cell) { return cell.startColumn(); }
- static size_t endSpan(const TableGrid::Cell& cell) { return cell.endColumn(); }
-
- static size_t index(size_t columnIndex, size_t /*rowIndex*/) { return columnIndex; }
- static size_t size(const TableGrid& grid) { return grid.columns().size(); }
-
- static LayoutUnit spacing(const TableGrid& grid) { return grid.horizontalSpacing(); }
-};
-
-struct RowSpan {
- static size_t hasSpan(const TableGrid::Slot& slot) { return slot.hasRowSpan(); }
- static size_t isSpanned(const TableGrid::Slot& slot) { return slot.isRowSpanned(); }
-
- static size_t spanCount(const TableGrid::Cell& cell) { return cell.rowSpan(); }
- static size_t startSpan(const TableGrid::Cell& cell) { return cell.startRow(); }
- static size_t endSpan(const TableGrid::Cell& cell) { return cell.endRow(); }
-
- static size_t index(size_t /*columnIndex*/, size_t rowIndex) { return rowIndex; }
- static size_t size(const TableGrid& grid) { return grid.rows().size(); }
-
- static LayoutUnit spacing(const TableGrid& grid) { return grid.verticalSpacing(); }
-};
-
-struct GridSpace {
- bool isEmpty() const { return !value; }
-
- // Initial width/height for column/row we start the distribution width (usually a minumum width).
- float value { 0 };
- // The base to compute the distribution ratio. It normally matches the [value] but in some cases we use the maximum value to distribute the extra space.
- float distributionBase { 0 };
-};
-
-inline static GridSpace max(const GridSpace& a, const GridSpace& b)
+void TableFormattingContext::computeAndDistributeExtraSpace(LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> availableVerticalSpace)
{
- return { std::max(a.value, b.value), std::max(a.distributionBase, b.distributionBase) };
-}
+ // Compute and balance the column and row spaces.
+ auto& grid = formattingState().tableGrid();
+ auto& columns = grid.columns().list();
+ auto tableLayout = this->tableLayout();
-inline static GridSpace& operator-(GridSpace& a, const GridSpace& b)
-{
- a.value = std::max(0.0f, a.value - b.value);
- a.distributionBase = std::max(0.0f, a.distributionBase - b.distributionBase);
- return a;
-}
-
-inline static GridSpace& operator+=(GridSpace& a, const GridSpace& b)
-{
- a.value += b.value;
- a.distributionBase += b.distributionBase;
- return a;
-}
-
-inline static GridSpace& operator-=(GridSpace& a, const GridSpace& b)
-{
- return a - b;
-}
-
-inline static GridSpace& operator/(GridSpace& a, unsigned value)
-{
- a.value /= value;
- a.distributionBase /= value;
- return a;
-}
-
-using DistributedSpaces = Vector<float>;
-template <typename SpanType>
-static DistributedSpaces distributeAvailableSpace(const TableGrid& grid, LayoutUnit availableSpace, const WTF::Function<GridSpace(const TableGrid::Slot&, size_t)>& slotSpace)
-{
- struct ResolvedItem {
- GridSpace slotSpace;
- bool isFixed { false };
- };
-
- auto& columns = grid.columns();
- auto& rows = grid.rows();
- // 1. Collect the non-spanning spaces first. They are used for the final distribution as well as for distributing the spanning space.
- Vector<Optional<ResolvedItem>> resolvedItems(SpanType::size(grid));
+ // Columns first.
+ auto distributedHorizontalSpaces = tableLayout.distributedHorizontalSpace(availableHorizontalSpace);
+ ASSERT(distributedHorizontalSpaces.size() == columns.size());
+ auto columnLogicalLeft = grid.horizontalSpacing();
for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
- for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
- auto& slot = *grid.slot({ columnIndex, rowIndex });
- if (SpanType::hasSpan(slot) || SpanType::isSpanned(slot))
- continue;
- auto index = SpanType::index(columnIndex, rowIndex);
- if (!resolvedItems[index])
- resolvedItems[index] = ResolvedItem { };
- resolvedItems[index]->slotSpace = max(resolvedItems[index]->slotSpace, slotSpace(slot, index));
- }
+ auto& column = columns[columnIndex];
+ column.setLogicalLeft(columnLogicalLeft);
+ column.setLogicalWidth(distributedHorizontalSpaces[columnIndex]);
+ columnLogicalLeft += distributedHorizontalSpaces[columnIndex] + grid.horizontalSpacing();
}
- // 2. Collect the spanning cells.
- struct SpanningCell {
- SlotPosition position;
- GridSpace unresolvedSpace;
- };
- Vector<SpanningCell> spanningCells;
+ // Rows second.
+ auto& rows = grid.rows().list();
for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
auto& slot = *grid.slot({ columnIndex, rowIndex });
- if (SpanType::hasSpan(slot))
- spanningCells.append({ { columnIndex, rowIndex }, slotSpace(slot, SpanType::index(columnIndex, rowIndex)) });
- }
- }
- // We need these spanning cells in the order of the number of columns/rows they span so that
- // we can resolve overlapping spans starting with the shorter ones e.g.
- // <td colspan=4>#a</td><td>#b</td>
- // <td colspan=2>#c</td><td colspan=3>#d</td>
- std::sort(spanningCells.begin(), spanningCells.end(), [&] (auto& a, auto& b) {
- return SpanType::spanCount(grid.slot(a.position)->cell()) < SpanType::spanCount(grid.slot(b.position)->cell());
- });
-
- // 3. Distribute the spanning cells' mimimum space across the columns/rows using the non-spanning spaces.
- // e.g. [ 1 ][ 5 ][ 1 ]
- // [ 9 ][ 1 ]
- // The initial widths are: [ 2 ][ 7 ][ 1 ]
- for (auto spanningCell : spanningCells) {
- auto& cell = grid.slot(spanningCell.position)->cell();
- auto unresolvedSpanningSpace = spanningCell.unresolvedSpace;
- if (!resolvedItems[SpanType::startSpan(cell)] || !resolvedItems[SpanType::endSpan(cell) - 1]) {
- // <td colspan=4>#a</td><td>#b</td>
- // <td colspan=2>#c</td><td colspan=3>#d</td>
- // Unresolved columns are: 1 2 3 4
- // 1. Take colspan=2 (shortest span) and resolve column 1 and 2
- // 2. Take colspan=3 and resolve column 3 and 4 (5 is resolved because it already has a non-spanning cell).
- // 3. colspan=4 needs no resolving because all the spanned columns (1 2 3 4) have already been resolved.
- auto unresolvedColumnCount = cell.columnSpan();
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
- if (!resolvedItems[spanIndex])
- continue;
- ASSERT(unresolvedColumnCount);
- --unresolvedColumnCount;
- unresolvedSpanningSpace -= resolvedItems[spanIndex]->slotSpace;
- }
- ASSERT(unresolvedColumnCount);
- auto equalSpaceForSpannedColumns = unresolvedSpanningSpace / unresolvedColumnCount;
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
- if (resolvedItems[spanIndex])
- continue;
- resolvedItems[spanIndex] = ResolvedItem { equalSpaceForSpannedColumns, false };
- }
- } else {
- // 1. Collect the non-spaning resolved spaces.
- // 2. Distribute the extra space among the spanned columns/rows based on the resolved space values.
- // e.g. spanning width: [ 9 ]. Resolved widths for the spanned columns: [ 1 ] [ 2 ]
- // New resolved widths: [ 3 ] [ 6 ].
- auto resolvedSpanningSpace = GridSpace { };
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
- resolvedSpanningSpace += resolvedItems[spanIndex]->slotSpace;
- if (resolvedSpanningSpace.value >= unresolvedSpanningSpace.value) {
- // The spanning cell fits the spanned columns/rows just fine. Nothing to distribute.
- continue;
- }
- auto spacing = SpanType::spacing(grid) * (SpanType::spanCount(cell) - 1);
- auto spaceToDistribute = unresolvedSpanningSpace - GridSpace { spacing, spacing } - resolvedSpanningSpace;
- if (!spaceToDistribute.isEmpty()) {
- auto distributionRatio = spaceToDistribute.distributionBase / resolvedSpanningSpace.distributionBase;
- for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
- resolvedItems[spanIndex]->slotSpace += GridSpace { resolvedItems[spanIndex]->slotSpace.value * distributionRatio, resolvedItems[spanIndex]->slotSpace.distributionBase * distributionRatio};
- }
- }
- }
- // 4. Distribute the extra space using the final resolved widths.
-#if ASSERT_ENABLED
- // We have to have all the spaces resolved at this point.
- for (auto& resolvedItem : resolvedItems)
- ASSERT(resolvedItem);
-#endif
- // Fixed size cells don't participate in available space distribution.
- auto adjustabledSpace = GridSpace { };
- for (auto& resolvedItem : resolvedItems) {
- if (resolvedItem->isFixed)
- continue;
- adjustabledSpace += resolvedItem->slotSpace;
- }
-
- DistributedSpaces distributedSpaces(resolvedItems.size());
- float spaceToDistribute = availableSpace - adjustabledSpace.value - ((resolvedItems.size() + 1) * SpanType::spacing(grid));
- // Essentially the remaining space to distribute should never be negative. LayoutUnit::epsilon() is required to compensate for LayoutUnit's low precision.
- ASSERT(spaceToDistribute >= -LayoutUnit::epsilon() * resolvedItems.size());
- // Distribute the extra space based on the resolved spaces.
- auto distributionRatio = spaceToDistribute / adjustabledSpace.distributionBase;
- for (size_t index = 0; index < resolvedItems.size(); ++index) {
- auto slotSpace = resolvedItems[index]->slotSpace.value;
- auto needsSpaceDistribution = spaceToDistribute && !resolvedItems[index]->isFixed;
- distributedSpaces[index] = slotSpace;
- if (!needsSpaceDistribution)
- continue;
- distributedSpaces[index] += resolvedItems[index]->slotSpace.distributionBase * distributionRatio;
- }
- return distributedSpaces;
-}
-
-void TableFormattingContext::computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace)
-{
- auto& grid = formattingState().tableGrid();
- auto& columns = grid.columns();
- auto tableWidthConstraints = *grid.widthConstraints();
-
- enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth };
- auto computeColumnWidths = [&] (auto columnWidthBalancingBase, auto extraHorizontalSpace) {
- auto distributedSpaces = distributeAvailableSpace<ColumnSpan>(grid, extraHorizontalSpace, [&] (const TableGrid::Slot& slot, size_t columnIndex) {
- auto& column = columns.list()[columnIndex];
- auto columnBoxFixedWidth = column.box() ? column.box()->columnWidth().valueOr(0_lu) : 0_lu;
- if (columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth) {
- auto minimumWidth = std::max<float>(slot.widthConstraints().minimum, columnBoxFixedWidth);
- return GridSpace { minimumWidth, minimumWidth };
- }
- // When the column has a fixed width cell, the maximum width balancing is based on the minimum width.
- auto minimumWidth = std::max<float>(slot.widthConstraints().minimum, columnBoxFixedWidth);
- auto maximumWidth = std::max<float>(slot.widthConstraints().maximum, columnBoxFixedWidth);
- if (column.isFixedWidth())
- return GridSpace { minimumWidth, maximumWidth };
- return GridSpace { maximumWidth, maximumWidth };
- });
- // Set final horizontal position and width.
- auto columnLogicalLeft = grid.horizontalSpacing();
- for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
- auto& column = columns.list()[columnIndex];
- auto columnWidth = LayoutUnit { distributedSpaces[columnIndex] };
-
- column.setLogicalLeft(columnLogicalLeft);
- column.setLogicalWidth(columnWidth);
- columnLogicalLeft += columnWidth + grid.horizontalSpacing();
- }
- };
- auto columnWidthBalancingBase = availableHorizontalSpace == tableWidthConstraints.maximum ? ColumnWidthBalancingBase::MaximumWidth : ColumnWidthBalancingBase::MinimumWidth;
- computeColumnWidths(columnWidthBalancingBase, availableHorizontalSpace);
-}
-
-void TableFormattingContext::computeAndDistributeExtraVerticalSpace(LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> availableVerticalSpace)
-{
- auto& grid = formattingState().tableGrid();
- auto& columns = grid.columns().list();
- auto& rows = grid.rows();
-
- struct RowHeight {
- InlineLayoutUnit height() const { return ascent + descent; }
-
- InlineLayoutUnit ascent { 0 };
- InlineLayoutUnit descent { 0 };
- };
- Vector<RowHeight> rowHeight(rows.size());
- Vector<SlotPosition> spanningRowPositionList;
- LayoutUnit tableUsedHeight;
- // 1. Collect initial, basline aligned row heights.
- for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
- auto maximumColumnAscent = InlineLayoutUnit { };
- auto maximumColumnDescent = InlineLayoutUnit { };
- for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
- auto& slot = *grid.slot({ columnIndex, rowIndex });
if (slot.isRowSpanned())
continue;
layoutCell(slot.cell(), availableHorizontalSpace);
@@ -611,37 +399,18 @@
// The minimum height of a row (without spanning-related height distribution) is defined as the height of an hypothetical
// linebox containing the cells originating in the row.
auto& cell = slot.cell();
- auto& cellBox = cell.box();
- cell.setBaselineOffset(geometry().usedBaselineForCell(cellBox));
- maximumColumnAscent = std::max(maximumColumnAscent, cell.baselineOffset());
- maximumColumnDescent = std::max(maximumColumnDescent, geometryForBox(cellBox).height() - cell.baselineOffset());
+ cell.setBaselineOffset(geometry().usedBaselineForCell(cell.box()));
}
- // <tr style="height: 10px"> is considered as min height.
- rowHeight[rowIndex] = { maximumColumnAscent, maximumColumnDescent };
- tableUsedHeight += maximumColumnAscent + maximumColumnDescent;
}
- // FIXME: Collect spanning row maximum heights.
- // Distribute extra space if the table is supposed to be taller than the sum of the row heights.
- tableUsedHeight += (rows.size() + 1) * grid.verticalSpacing();
- auto availableSpace = std::max(availableVerticalSpace.valueOr(0_lu), tableUsedHeight);
- auto distributedSpaces = distributeAvailableSpace<RowSpan>(grid, availableSpace, [&] (const TableGrid::Slot& slot, size_t rowIndex) {
- if (slot.hasRowSpan())
- return GridSpace { geometryForBox(slot.cell().box()).height(), geometryForBox(slot.cell().box()).height() };
- auto computedRowHeight = geometry().computedHeight(rows.list()[rowIndex].box(), { });
- auto height = std::max<float>(rowHeight[rowIndex].height(), computedRowHeight.valueOr(0_lu));
- return GridSpace { height, height };
- });
-
+ auto distributedVerticalSpaces = tableLayout.distributedVerticalSpace(availableVerticalSpace);
+ ASSERT(distributedVerticalSpaces.size() == rows.size());
auto rowLogicalTop = grid.verticalSpacing();
for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
- auto& row = grid.rows().list()[rowIndex];
- auto rowUsedHeight = LayoutUnit { distributedSpaces[rowIndex] };
-
- row.setLogicalHeight(rowUsedHeight);
- row.setBaselineOffset(rowHeight[rowIndex].ascent);
+ auto& row = rows[rowIndex];
+ row.setLogicalHeight(distributedVerticalSpaces[rowIndex]);
row.setLogicalTop(rowLogicalTop);
- rowLogicalTop += rowUsedHeight + grid.verticalSpacing();
+ rowLogicalTop += distributedVerticalSpaces[rowIndex] + grid.verticalSpacing();
}
}
Modified: trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.h (261744 => 261745)
--- trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.h 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/layout/tableformatting/TableFormattingContext.h 2020-05-15 14:44:52 UTC (rev 261745)
@@ -28,6 +28,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "FormattingContext.h"
+#include "TableFormattingState.h"
#include "TableGrid.h"
#include <wtf/IsoMalloc.h>
@@ -35,7 +36,6 @@
namespace Layout {
class InvalidationState;
-class TableFormattingState;
// This class implements the layout logic for table formatting contexts.
// https://www.w3.org/TR/CSS22/tables.html
class TableFormattingContext final : public FormattingContext {
@@ -45,6 +45,21 @@
void layoutInFlowContent(InvalidationState&, const ConstraintsForInFlowContent&) override;
private:
+ class TableLayout {
+ public:
+ TableLayout(const TableFormattingContext&, const TableGrid&);
+
+ using DistributedSpaces = Vector<LayoutUnit>;
+ DistributedSpaces distributedHorizontalSpace(LayoutUnit availableHorizontalSpace);
+ DistributedSpaces distributedVerticalSpace(Optional<LayoutUnit> availableVerticalSpace);
+
+ private:
+ const TableFormattingContext& formattingContext() const { return m_formattingContext; }
+
+ const TableFormattingContext& m_formattingContext;
+ const TableGrid& m_grid;
+ };
+
class Geometry : public FormattingContext::Geometry {
public:
LayoutUnit cellHeigh(const ContainerBox&) const;
@@ -59,6 +74,7 @@
const TableFormattingContext& formattingContext() const { return downcast<TableFormattingContext>(FormattingContext::Geometry::formattingContext()); }
};
TableFormattingContext::Geometry geometry() const { return Geometry(*this); }
+ TableFormattingContext::TableLayout tableLayout() const { return TableLayout(*this, formattingState().tableGrid()); }
IntrinsicWidthConstraints computedIntrinsicWidthConstraints() override;
void layoutCell(const TableGrid::Cell&, LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> usedCellHeight = WTF::nullopt);
@@ -68,8 +84,7 @@
void ensureTableGrid();
IntrinsicWidthConstraints computedPreferredWidthForColumns();
- void computeAndDistributeExtraHorizontalSpace(LayoutUnit availableHorizontalSpace);
- void computeAndDistributeExtraVerticalSpace(LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> availableVerticalSpace);
+ void computeAndDistributeExtraSpace(LayoutUnit availableHorizontalSpace, Optional<LayoutUnit> availableVerticalSpace);
const TableFormattingState& formattingState() const { return downcast<TableFormattingState>(FormattingContext::formattingState()); }
TableFormattingState& formattingState() { return downcast<TableFormattingState>(FormattingContext::formattingState()); }
Modified: trunk/Source/WebCore/layout/tableformatting/TableFormattingState.h (261744 => 261745)
--- trunk/Source/WebCore/layout/tableformatting/TableFormattingState.h 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/layout/tableformatting/TableFormattingState.h 2020-05-15 14:44:52 UTC (rev 261745)
@@ -42,6 +42,7 @@
~TableFormattingState();
TableGrid& tableGrid() { return m_tableGrid; }
+ const TableGrid& tableGrid() const { return m_tableGrid; }
private:
TableGrid m_tableGrid;
Modified: trunk/Source/WebCore/layout/tableformatting/TableGrid.h (261744 => 261745)
--- trunk/Source/WebCore/layout/tableformatting/TableGrid.h 2020-05-15 14:40:51 UTC (rev 261744)
+++ trunk/Source/WebCore/layout/tableformatting/TableGrid.h 2020-05-15 14:44:52 UTC (rev 261745)
@@ -56,7 +56,7 @@
LayoutUnit verticalSpacing() const { return m_verticalSpacing; }
void setWidthConstraints(FormattingContext::IntrinsicWidthConstraints intrinsicWidthConstraints) { m_intrinsicWidthConstraints = intrinsicWidthConstraints; }
- Optional<FormattingContext::IntrinsicWidthConstraints> widthConstraints() { return m_intrinsicWidthConstraints; }
+ Optional<FormattingContext::IntrinsicWidthConstraints> widthConstraints() const { return m_intrinsicWidthConstraints; }
// Column represents a vertical set of slots in the grid. A column has horizontal position and width.
class Column {
@@ -132,7 +132,7 @@
public:
using RowList = Vector<Row>;
RowList& list() { return m_rowList; }
- const RowList& rowList() const { return m_rowList; }
+ const RowList& list() const { return m_rowList; }
void addRow(const ContainerBox&);
Added: trunk/Source/WebCore/layout/tableformatting/TableLayout.cpp (0 => 261745)
--- trunk/Source/WebCore/layout/tableformatting/TableLayout.cpp (rev 0)
+++ trunk/Source/WebCore/layout/tableformatting/TableLayout.cpp 2020-05-15 14:44:52 UTC (rev 261745)
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TableFormattingContext.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "DisplayBox.h"
+#include "LayoutBox.h"
+
+namespace WebCore {
+namespace Layout {
+
+// https://www.w3.org/TR/css-tables-3/#table-layout-algorithm
+TableFormattingContext::TableLayout::TableLayout(const TableFormattingContext& formattingContext, const TableGrid& grid)
+ : m_formattingContext(formattingContext)
+ , m_grid(grid)
+{
+}
+
+struct ColumnSpan {
+ static size_t hasSpan(const TableGrid::Slot& slot) { return slot.hasColumnSpan(); }
+ static size_t isSpanned(const TableGrid::Slot& slot) { return slot.isColumnSpanned(); }
+
+ static size_t spanCount(const TableGrid::Cell& cell) { return cell.columnSpan(); }
+ static size_t startSpan(const TableGrid::Cell& cell) { return cell.startColumn(); }
+ static size_t endSpan(const TableGrid::Cell& cell) { return cell.endColumn(); }
+
+ static size_t index(size_t columnIndex, size_t /*rowIndex*/) { return columnIndex; }
+ static size_t size(const TableGrid& grid) { return grid.columns().size(); }
+
+ static LayoutUnit spacing(const TableGrid& grid) { return grid.horizontalSpacing(); }
+};
+
+struct RowSpan {
+ static size_t hasSpan(const TableGrid::Slot& slot) { return slot.hasRowSpan(); }
+ static size_t isSpanned(const TableGrid::Slot& slot) { return slot.isRowSpanned(); }
+
+ static size_t spanCount(const TableGrid::Cell& cell) { return cell.rowSpan(); }
+ static size_t startSpan(const TableGrid::Cell& cell) { return cell.startRow(); }
+ static size_t endSpan(const TableGrid::Cell& cell) { return cell.endRow(); }
+
+ static size_t index(size_t /*columnIndex*/, size_t rowIndex) { return rowIndex; }
+ static size_t size(const TableGrid& grid) { return grid.rows().size(); }
+
+ static LayoutUnit spacing(const TableGrid& grid) { return grid.verticalSpacing(); }
+};
+
+struct GridSpace {
+ bool isEmpty() const { return !value; }
+
+ // Initial width/height for column/row we start the distribution width (usually a minumum width).
+ float value { 0 };
+ // The base to compute the distribution ratio. It normally matches the [value] but in some cases we use the maximum value to distribute the extra space.
+ float distributionBase { 0 };
+};
+
+inline static GridSpace max(const GridSpace& a, const GridSpace& b)
+{
+ return { std::max(a.value, b.value), std::max(a.distributionBase, b.distributionBase) };
+}
+
+inline static GridSpace& operator-(GridSpace& a, const GridSpace& b)
+{
+ a.value = std::max(0.0f, a.value - b.value);
+ a.distributionBase = std::max(0.0f, a.distributionBase - b.distributionBase);
+ return a;
+}
+
+inline static GridSpace& operator+=(GridSpace& a, const GridSpace& b)
+{
+ a.value += b.value;
+ a.distributionBase += b.distributionBase;
+ return a;
+}
+
+inline static GridSpace& operator-=(GridSpace& a, const GridSpace& b)
+{
+ return a - b;
+}
+
+inline static GridSpace& operator/(GridSpace& a, unsigned value)
+{
+ a.value /= value;
+ a.distributionBase /= value;
+ return a;
+}
+
+template <typename SpanType>
+static Vector<LayoutUnit> distributeAvailableSpace(const TableGrid& grid, LayoutUnit availableSpace, const WTF::Function<GridSpace(const TableGrid::Slot&, size_t)>& slotSpace)
+{
+ struct ResolvedItem {
+ GridSpace slotSpace;
+ bool isFixed { false };
+ };
+
+ auto& columns = grid.columns();
+ auto& rows = grid.rows();
+ // 1. Collect the non-spanning spaces first. They are used for the final distribution as well as for distributing the spanning space.
+ Vector<Optional<ResolvedItem>> resolvedItems(SpanType::size(grid));
+ for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+ auto& slot = *grid.slot({ columnIndex, rowIndex });
+ if (SpanType::hasSpan(slot) || SpanType::isSpanned(slot))
+ continue;
+ auto index = SpanType::index(columnIndex, rowIndex);
+ if (!resolvedItems[index])
+ resolvedItems[index] = ResolvedItem { };
+ resolvedItems[index]->slotSpace = max(resolvedItems[index]->slotSpace, slotSpace(slot, index));
+ }
+ }
+
+ // 2. Collect the spanning cells.
+ struct SpanningCell {
+ SlotPosition position;
+ GridSpace unresolvedSpace;
+ };
+ Vector<SpanningCell> spanningCells;
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+ for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+ auto& slot = *grid.slot({ columnIndex, rowIndex });
+ if (SpanType::hasSpan(slot))
+ spanningCells.append({ { columnIndex, rowIndex }, slotSpace(slot, SpanType::index(columnIndex, rowIndex)) });
+ }
+ }
+ // We need these spanning cells in the order of the number of columns/rows they span so that
+ // we can resolve overlapping spans starting with the shorter ones e.g.
+ // <td colspan=4>#a</td><td>#b</td>
+ // <td colspan=2>#c</td><td colspan=3>#d</td>
+ std::sort(spanningCells.begin(), spanningCells.end(), [&] (auto& a, auto& b) {
+ return SpanType::spanCount(grid.slot(a.position)->cell()) < SpanType::spanCount(grid.slot(b.position)->cell());
+ });
+
+ // 3. Distribute the spanning cells' mimimum space across the columns/rows using the non-spanning spaces.
+ // e.g. [ 1 ][ 5 ][ 1 ]
+ // [ 9 ][ 1 ]
+ // The initial widths are: [ 2 ][ 7 ][ 1 ]
+ for (auto spanningCell : spanningCells) {
+ auto& cell = grid.slot(spanningCell.position)->cell();
+ auto unresolvedSpanningSpace = spanningCell.unresolvedSpace;
+ if (!resolvedItems[SpanType::startSpan(cell)] || !resolvedItems[SpanType::endSpan(cell) - 1]) {
+ // <td colspan=4>#a</td><td>#b</td>
+ // <td colspan=2>#c</td><td colspan=3>#d</td>
+ // Unresolved columns are: 1 2 3 4
+ // 1. Take colspan=2 (shortest span) and resolve column 1 and 2
+ // 2. Take colspan=3 and resolve column 3 and 4 (5 is resolved because it already has a non-spanning cell).
+ // 3. colspan=4 needs no resolving because all the spanned columns (1 2 3 4) have already been resolved.
+ auto unresolvedColumnCount = cell.columnSpan();
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
+ if (!resolvedItems[spanIndex])
+ continue;
+ ASSERT(unresolvedColumnCount);
+ --unresolvedColumnCount;
+ unresolvedSpanningSpace -= resolvedItems[spanIndex]->slotSpace;
+ }
+ ASSERT(unresolvedColumnCount);
+ auto equalSpaceForSpannedColumns = unresolvedSpanningSpace / unresolvedColumnCount;
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex) {
+ if (resolvedItems[spanIndex])
+ continue;
+ resolvedItems[spanIndex] = ResolvedItem { equalSpaceForSpannedColumns, false };
+ }
+ } else {
+ // 1. Collect the non-spaning resolved spaces.
+ // 2. Distribute the extra space among the spanned columns/rows based on the resolved space values.
+ // e.g. spanning width: [ 9 ]. Resolved widths for the spanned columns: [ 1 ] [ 2 ]
+ // New resolved widths: [ 3 ] [ 6 ].
+ auto resolvedSpanningSpace = GridSpace { };
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
+ resolvedSpanningSpace += resolvedItems[spanIndex]->slotSpace;
+ if (resolvedSpanningSpace.value >= unresolvedSpanningSpace.value) {
+ // The spanning cell fits the spanned columns/rows just fine. Nothing to distribute.
+ continue;
+ }
+ auto spacing = SpanType::spacing(grid) * (SpanType::spanCount(cell) - 1);
+ auto spaceToDistribute = unresolvedSpanningSpace - GridSpace { spacing, spacing } - resolvedSpanningSpace;
+ if (!spaceToDistribute.isEmpty()) {
+ auto distributionRatio = spaceToDistribute.distributionBase / resolvedSpanningSpace.distributionBase;
+ for (auto spanIndex = SpanType::startSpan(cell); spanIndex < SpanType::endSpan(cell); ++spanIndex)
+ resolvedItems[spanIndex]->slotSpace += GridSpace { resolvedItems[spanIndex]->slotSpace.value * distributionRatio, resolvedItems[spanIndex]->slotSpace.distributionBase * distributionRatio};
+ }
+ }
+ }
+ // 4. Distribute the extra space using the final resolved widths.
+#if ASSERT_ENABLED
+ // We have to have all the spaces resolved at this point.
+ for (auto& resolvedItem : resolvedItems)
+ ASSERT(resolvedItem);
+#endif
+ // Fixed size cells don't participate in available space distribution.
+ auto adjustabledSpace = GridSpace { };
+ for (auto& resolvedItem : resolvedItems) {
+ if (resolvedItem->isFixed)
+ continue;
+ adjustabledSpace += resolvedItem->slotSpace;
+ }
+
+ Vector<LayoutUnit> distributedSpaces(resolvedItems.size());
+ float spaceToDistribute = availableSpace - adjustabledSpace.value - ((resolvedItems.size() + 1) * SpanType::spacing(grid));
+ // Essentially the remaining space to distribute should never be negative. LayoutUnit::epsilon() is required to compensate for LayoutUnit's low precision.
+ ASSERT(spaceToDistribute >= -LayoutUnit::epsilon() * resolvedItems.size());
+ // Distribute the extra space based on the resolved spaces.
+ auto distributionRatio = spaceToDistribute / adjustabledSpace.distributionBase;
+ for (size_t index = 0; index < resolvedItems.size(); ++index) {
+ auto slotSpace = resolvedItems[index]->slotSpace.value;
+ auto needsSpaceDistribution = spaceToDistribute && !resolvedItems[index]->isFixed;
+ distributedSpaces[index] = LayoutUnit { slotSpace };
+ if (!needsSpaceDistribution)
+ continue;
+ distributedSpaces[index] += LayoutUnit { resolvedItems[index]->slotSpace.distributionBase * distributionRatio };
+ }
+ return distributedSpaces;
+}
+
+TableFormattingContext::TableLayout::DistributedSpaces TableFormattingContext::TableLayout::distributedHorizontalSpace(LayoutUnit availableHorizontalSpace)
+{
+ enum class ColumnWidthBalancingBase { MinimumWidth, MaximumWidth };
+ auto columnWidthBalancingBase = availableHorizontalSpace == m_grid.widthConstraints()->maximum ? ColumnWidthBalancingBase::MaximumWidth : ColumnWidthBalancingBase::MinimumWidth;
+ return distributeAvailableSpace<ColumnSpan>(m_grid, availableHorizontalSpace, [&] (const TableGrid::Slot& slot, size_t columnIndex) {
+ auto& column = m_grid.columns().list()[columnIndex];
+ auto columnBoxFixedWidth = column.box() ? column.box()->columnWidth().valueOr(0_lu) : 0_lu;
+ if (columnWidthBalancingBase == ColumnWidthBalancingBase::MinimumWidth) {
+ auto minimumWidth = std::max<float>(slot.widthConstraints().minimum, columnBoxFixedWidth);
+ return GridSpace { minimumWidth, minimumWidth };
+ }
+ // When the column has a fixed width cell, the maximum width balancing is based on the minimum width.
+ auto minimumWidth = std::max<float>(slot.widthConstraints().minimum, columnBoxFixedWidth);
+ auto maximumWidth = std::max<float>(slot.widthConstraints().maximum, columnBoxFixedWidth);
+ if (column.isFixedWidth())
+ return GridSpace { minimumWidth, maximumWidth };
+ return GridSpace { maximumWidth, maximumWidth };
+ });
+}
+
+TableFormattingContext::TableLayout::DistributedSpaces TableFormattingContext::TableLayout::distributedVerticalSpace(Optional<LayoutUnit> availableVerticalSpace)
+{
+ auto& rows = m_grid.rows();
+ auto& columns = m_grid.columns();
+
+ Vector<LayoutUnit> rowHeight(rows.size());
+ auto tableUsedHeight = LayoutUnit { };
+ // 2. Collect initial, baseline aligned row heights.
+ for (size_t rowIndex = 0; rowIndex < rows.size(); ++rowIndex) {
+ auto maximumColumnAscent = InlineLayoutUnit { };
+ auto maximumColumnDescent = InlineLayoutUnit { };
+ for (size_t columnIndex = 0; columnIndex < columns.size(); ++columnIndex) {
+ auto& slot = *m_grid.slot({ columnIndex, rowIndex });
+ if (slot.isRowSpanned())
+ continue;
+ if (slot.hasRowSpan())
+ continue;
+ // The minimum height of a row (without spanning-related height distribution) is defined as the height of an hypothetical
+ // linebox containing the cells originating in the row.
+ auto& cell = slot.cell();
+ maximumColumnAscent = std::max(maximumColumnAscent, cell.baselineOffset());
+ maximumColumnDescent = std::max(maximumColumnDescent, formattingContext().geometryForBox(cell.box()).height() - cell.baselineOffset());
+ }
+ // <tr style="height: 10px"> is considered as min height.
+ rowHeight[rowIndex] = maximumColumnAscent + maximumColumnDescent;
+ tableUsedHeight += rowHeight[rowIndex];
+ }
+ // FIXME: Collect spanning row maximum heights.
+
+ tableUsedHeight += (rows.size() + 1) * m_grid.verticalSpacing();
+ auto availableSpace = std::max(availableVerticalSpace.valueOr(0_lu), tableUsedHeight);
+ // Distribute extra space if the table is supposed to be taller than the sum of the row heights.
+ return distributeAvailableSpace<RowSpan>(m_grid, availableSpace, [&] (const TableGrid::Slot& slot, size_t rowIndex) {
+ if (slot.hasRowSpan())
+ return GridSpace { formattingContext().geometryForBox(slot.cell().box()).height(), formattingContext().geometryForBox(slot.cell().box()).height() };
+ auto& rows = m_grid.rows();
+ auto computedRowHeight = formattingContext().geometry().computedHeight(rows.list()[rowIndex].box(), { });
+ auto height = std::max<float>(rowHeight[rowIndex], computedRowHeight.valueOr(0_lu));
+ return GridSpace { height, height };
+ });
+}
+
+}
+}
+
+#endif