diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/table')
26 files changed, 977 insertions, 502 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc index 2606614b302..910ad2efea2 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc @@ -46,6 +46,11 @@ wtf_size_t LayoutNGTable::ColumnCount() const { return cached_layout_result->TableColumnCount(); } +bool LayoutNGTable::HasCollapsedBorders() const { + NOT_DESTROYED(); + return cached_table_borders_ && cached_table_borders_->IsCollapsed(); +} + void LayoutNGTable::SetCachedTableBorders( scoped_refptr<const NGTableBorders> table_borders) { NOT_DESTROYED(); @@ -77,16 +82,36 @@ void LayoutNGTable::SetCachedTableColumnConstraints( void LayoutNGTable::GridBordersChanged() { NOT_DESTROYED(); InvalidateCachedTableBorders(); - // If borders change, table fragment must be regenerated. - if (StyleRef().BorderCollapse() == EBorderCollapse::kCollapse) + if (StyleRef().BorderCollapse() == EBorderCollapse::kCollapse) { + SetShouldDoFullPaintInvalidationWithoutGeometryChange( + PaintInvalidationReason::kStyle); + // If borders change, table fragment must be regenerated. SetNeedsLayout(layout_invalidation_reason::kTableChanged); + } } void LayoutNGTable::TableGridStructureChanged() { NOT_DESTROYED(); + // Callers must ensure table layout gets invalidated. InvalidateCachedTableBorders(); } +bool LayoutNGTable::HasBackgroundForPaint() const { + NOT_DESTROYED(); + if (StyleRef().HasBackground()) + return true; + DCHECK_GT(PhysicalFragmentCount(), 0u); + const NGTableFragmentData::ColumnGeometries* column_geometries = + GetPhysicalFragment(0)->TableColumnGeometries(); + if (column_geometries) { + for (const auto& column_geometry : *column_geometries) { + if (column_geometry.node.Style().HasBackground()) + return true; + } + } + return false; +} + void LayoutNGTable::UpdateBlockLayout(bool relayout_children) { NOT_DESTROYED(); LayoutAnalyzer::BlockScope analyzer(*this); @@ -101,6 +126,10 @@ void LayoutNGTable::UpdateBlockLayout(bool relayout_children) { void LayoutNGTable::AddChild(LayoutObject* child, LayoutObject* before_child) { NOT_DESTROYED(); TableGridStructureChanged(); + // Only TablesNG table parts are allowed. + DCHECK(child->IsLayoutNGObject() || + (!child->IsTableCaption() && !child->IsLayoutTableCol() && + !child->IsTableSection())); bool wrap_in_anonymous_section = !child->IsTableCaption() && !child->IsLayoutTableCol() && !child->IsTableSection(); @@ -225,17 +254,23 @@ void LayoutNGTable::AddVisualEffectOverflow() { if (const NGPhysicalBoxFragment* fragment = GetPhysicalFragment(0)) { DCHECK_EQ(PhysicalFragmentCount(), 1u); // Table's collapsed borders contribute to visual overflow. - // Table border side can be composed of multiple border segments. - // Inline visual overflow uses size of the largest border segment. - // Block visual overflow uses size of first border segment. + // In the inline direction, table's border box does not include + // visual border width (largest border), but does include + // layout border width (border of first cell). + // Expands border box to include visual border width. if (const NGTableBorders* collapsed_borders = fragment->TableCollapsedBorders()) { PhysicalRect borders_overflow = PhysicalBorderBoxRect(); NGBoxStrut table_borders = collapsed_borders->TableBorder(); auto visual_inline_strut = collapsed_borders->GetCollapsedBorderVisualInlineStrut(); - table_borders.inline_start = visual_inline_strut.first; - table_borders.inline_end = visual_inline_strut.second; + // Expand by difference between visual and layout border width. + table_borders.inline_start = + visual_inline_strut.first - table_borders.inline_start; + table_borders.inline_end = + visual_inline_strut.second - table_borders.inline_end; + table_borders.block_start = LayoutUnit(); + table_borders.block_end = LayoutUnit(); borders_overflow.Expand( table_borders.ConvertToPhysical(StyleRef().GetWritingDirection())); AddSelfVisualOverflow(borders_overflow); @@ -299,6 +334,34 @@ LayoutUnit LayoutNGTable::BorderBottom() const { return LayoutNGMixin<LayoutBlock>::BorderBottom(); } +LayoutUnit LayoutNGTable::PaddingTop() const { + NOT_DESTROYED(); + if (ShouldCollapseBorders()) + return LayoutUnit(); + return LayoutNGMixin<LayoutBlock>::PaddingTop(); +} + +LayoutUnit LayoutNGTable::PaddingBottom() const { + NOT_DESTROYED(); + if (ShouldCollapseBorders()) + return LayoutUnit(); + return LayoutNGMixin<LayoutBlock>::PaddingBottom(); +} + +LayoutUnit LayoutNGTable::PaddingLeft() const { + NOT_DESTROYED(); + if (ShouldCollapseBorders()) + return LayoutUnit(); + return LayoutNGMixin<LayoutBlock>::PaddingLeft(); +} + +LayoutUnit LayoutNGTable::PaddingRight() const { + NOT_DESTROYED(); + if (ShouldCollapseBorders()) + return LayoutUnit(); + return LayoutNGMixin<LayoutBlock>::PaddingRight(); +} + LayoutRectOutsets LayoutNGTable::BorderBoxOutsets() const { NOT_DESTROYED(); // DCHECK(cached_table_borders_.get()) @@ -310,6 +373,28 @@ LayoutRectOutsets LayoutNGTable::BorderBoxOutsets() const { return LayoutRectOutsets(); } +// Effective column index is index of columns with mergeable +// columns skipped. Used in a11y. +unsigned LayoutNGTable::AbsoluteColumnToEffectiveColumn( + unsigned absolute_column_index) const { + NOT_DESTROYED(); + if (!cached_table_columns_) { + NOTREACHED(); + return absolute_column_index; + } + unsigned effective_column_index = 0; + unsigned column_count = cached_table_columns_.get()->data.size(); + for (unsigned current_column_index = 0; current_column_index < column_count; + ++current_column_index) { + if (current_column_index != 0 && + !cached_table_columns_.get()->data[current_column_index].is_mergeable) + ++effective_column_index; + if (current_column_index == absolute_column_index) + return effective_column_index; + } + return effective_column_index; +} + bool LayoutNGTable::IsFirstCell(const LayoutNGTableCellInterface& cell) const { NOT_DESTROYED(); const LayoutNGTableRowInterface* row = cell.RowInterface(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h index 9dee22e0183..0bce95d4ed8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h @@ -66,6 +66,10 @@ class CORE_EXPORT LayoutNGTable : public LayoutNGMixin<LayoutBlock>, // might have changed. void TableGridStructureChanged(); + // Table paints column backgrounds. + // Returns true if table, or columns' style.HasBackground(). + bool HasBackgroundForPaint() const; + // LayoutBlock methods start. const char* GetName() const override { @@ -96,8 +100,22 @@ class CORE_EXPORT LayoutNGTable : public LayoutNGMixin<LayoutBlock>, LayoutUnit BorderRight() const override; + // The collapsing border model disallows paddings on table. + // See http://www.w3.org/TR/CSS2/tables.html#collapsing-borders. + LayoutUnit PaddingTop() const override; + + LayoutUnit PaddingBottom() const override; + + LayoutUnit PaddingLeft() const override; + + LayoutUnit PaddingRight() const override; + LayoutRectOutsets BorderBoxOutsets() const override; + // TODO(1151101) + // ClientLeft/Top are incorrect for tables, but cannot be fixed + // by subclassing ClientLeft/Top. + PhysicalRect OverflowClipRect(const PhysicalOffset&, OverlayScrollbarClipBehavior) const override; @@ -108,6 +126,17 @@ class CORE_EXPORT LayoutNGTable : public LayoutNGMixin<LayoutBlock>, return false; } + // Whether a table has opaque foreground depends on many factors, e.g. border + // spacing, missing cells, etc. For simplicity, just conservatively assume + // foreground of all tables are not opaque. + // Copied from LayoutTable. + bool ForegroundIsKnownToBeOpaqueInRect( + const PhysicalRect& local_rect, + unsigned max_depth_to_test) const override { + NOT_DESTROYED(); + return false; + } + // LayoutBlock methods end. // LayoutNGTableInterface methods start. @@ -133,11 +162,7 @@ class CORE_EXPORT LayoutNGTable : public LayoutNGMixin<LayoutBlock>, return StyleRef().BorderCollapse() == EBorderCollapse::kCollapse; } - // Used in table painting for invalidation. Should not be needed by NG. - bool HasCollapsedBorders() const final { - NOTREACHED(); - return false; - } + bool HasCollapsedBorders() const final; bool HasColElements() const final { NOTREACHED(); @@ -158,20 +183,14 @@ class CORE_EXPORT LayoutNGTable : public LayoutNGMixin<LayoutBlock>, return ShouldCollapseBorders() ? 0 : StyleRef().VerticalBorderSpacing(); } - // Legacy had a concept of colspan column compression. This is a legacy - // method to map between absolute and compressed columns. - // Because NG does not compress columns, absolute and effective are the same. unsigned AbsoluteColumnToEffectiveColumn( - unsigned absolute_column_index) const final { - NOT_DESTROYED(); - return absolute_column_index; - } + unsigned absolute_column_index) const final; - // Legacy caches sections. Might not be needed by NG. - void RecalcSectionsIfNeeded() const final { NOTIMPLEMENTED(); } + // NG does not need this method. Sections are not cached. + void RecalcSectionsIfNeeded() const final {} - // Legacy caches sections. Might not be needed by NG. - void ForceSectionsRecalc() final { NOTIMPLEMENTED(); } + // Not used by NG. Legacy caches sections. + void ForceSectionsRecalc() final { NOT_DESTROYED(); } // Used in paint for printing. Should not be needed by NG. LayoutUnit RowOffsetFromRepeatingFooter() const final { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h index ab444cfb134..7d8a907e54d 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h @@ -12,6 +12,7 @@ namespace blink { class LayoutNGTableInterface; +class NGPhysicalFragment; class CORE_EXPORT LayoutNGTableCaption final : public LayoutNGBlockFlowMixin<LayoutTableCaption> { diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc index 85d4c23a661..2968a2022ef 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc @@ -15,6 +15,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h" #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h" +#include "third_party/blink/renderer/core/paint/ng/ng_table_cell_paint_invalidator.h" namespace blink { @@ -37,8 +38,13 @@ void LayoutNGTableCell::InvalidateLayoutResultCacheAfterMeasure() const { LayoutRectOutsets LayoutNGTableCell::BorderBoxOutsets() const { NOT_DESTROYED(); - DCHECK_GE(PhysicalFragmentCount(), 0u); - return GetPhysicalFragment(0)->Borders().ToLayoutRectOutsets(); + // TODO(1061423) This function should not be called before layout. + // ScrollAnchor::Examine does. Example trigger: + // ScrollTimelineTest.TimelineInvalidationWhenScrollerDisplayPropertyChanges + // DCHECK_GE(PhysicalFragmentCount(), 0u); + if (PhysicalFragmentCount() > 0) + return GetPhysicalFragment(0)->Borders().ToLayoutRectOutsets(); + return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderBoxOutsets(); } LayoutUnit LayoutNGTableCell::BorderTop() const { @@ -115,12 +121,21 @@ void LayoutNGTableCell::StyleDidChange(StyleDifference diff, LayoutNGBlockFlowMixin<LayoutBlockFlow>::StyleDidChange(diff, old_style); } +void LayoutNGTableCell::WillBeRemovedFromTree() { + NOT_DESTROYED(); + if (LayoutNGTable* table = Table()) + table->TableGridStructureChanged(); + LayoutNGMixin<LayoutBlockFlow>::WillBeRemovedFromTree(); +} + void LayoutNGTableCell::ColSpanOrRowSpanChanged() { NOT_DESTROYED(); - // TODO(atotic) Invalidate layout? UpdateColAndRowSpanFlags(); - if (LayoutNGTable* table = Table()) + if (LayoutNGTable* table = Table()) { + table->SetNeedsLayoutAndIntrinsicWidthsRecalc( + layout_invalidation_reason::kTableChanged); table->TableGridStructureChanged(); + } } LayoutBox* LayoutNGTableCell::CreateAnonymousBoxWithSameTypeAs( @@ -129,6 +144,17 @@ LayoutBox* LayoutNGTableCell::CreateAnonymousBoxWithSameTypeAs( return LayoutObjectFactory::CreateAnonymousTableCellWithParent(*parent); } +LayoutBlock* LayoutNGTableCell::StickyContainer() const { + NOT_DESTROYED(); + return Table(); +} + +void LayoutNGTableCell::InvalidatePaint( + const PaintInvalidatorContext& context) const { + NOT_DESTROYED(); + NGTableCellPaintInvalidator(*this, context).InvalidatePaint(); +} + bool LayoutNGTableCell::BackgroundIsKnownToBeOpaqueInRect( const PhysicalRect& local_rect) const { NOT_DESTROYED(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h index a3e638bc05b..66f9c1a73c7 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h @@ -69,6 +69,8 @@ class CORE_EXPORT LayoutNGTableCell void StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) final; + void WillBeRemovedFromTree() override; + // TODO(atotic) Remove "New" from name. // Currently, LayoutNGTableCellLegacy is named LayoutNGTableCell for test // compat. @@ -87,6 +89,10 @@ class CORE_EXPORT LayoutNGTableCell LayoutBox* CreateAnonymousBoxWithSameTypeAs( const LayoutObject* parent) const override; + LayoutBlock* StickyContainer() const override; + + void InvalidatePaint(const PaintInvalidatorContext&) const override; + // LayoutBlockFlow methods end. // LayoutNGTableCellInterface methods start. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.cc index da8e67de7fd..4b9d4cda8cf 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.cc @@ -36,7 +36,7 @@ void LayoutNGTableColumn::StyleDidChange(StyleDifference diff, } } } - LayoutBoxModelObject::StyleDidChange(diff, old_style); + LayoutBox::StyleDidChange(diff, old_style); } void LayoutNGTableColumn::ImageChanged(WrappedImagePtr, CanDeferInvalidation) { @@ -47,6 +47,22 @@ void LayoutNGTableColumn::ImageChanged(WrappedImagePtr, CanDeferInvalidation) { } } +void LayoutNGTableColumn::InsertedIntoTree() { + NOT_DESTROYED(); + LayoutBox::InsertedIntoTree(); + DCHECK(Table()); + if (StyleRef().HasBackground()) + Table()->SetBackgroundNeedsFullPaintInvalidation(); +} + +void LayoutNGTableColumn::WillBeRemovedFromTree() { + NOT_DESTROYED(); + LayoutBox::WillBeRemovedFromTree(); + DCHECK(Table()); + if (StyleRef().HasBackground()) + Table()->SetBackgroundNeedsFullPaintInvalidation(); +} + bool LayoutNGTableColumn::IsChildAllowed(LayoutObject* child, const ComputedStyle& style) const { NOT_DESTROYED(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h index a1831292439..f0f56ccd725 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h @@ -57,6 +57,10 @@ class CORE_EXPORT LayoutNGTableColumn : public LayoutBox { void ImageChanged(WrappedImagePtr, CanDeferInvalidation) final; + void InsertedIntoTree() override; + + void WillBeRemovedFromTree() override; + bool VisualRectRespectsVisibility() const final { NOT_DESTROYED(); return false; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc index 98fe9be615d..02aed9710c8 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc @@ -86,6 +86,13 @@ void LayoutNGTableRow::RemoveChild(LayoutObject* child) { LayoutNGMixin<LayoutBlock>::RemoveChild(child); } +void LayoutNGTableRow::WillBeRemovedFromTree() { + NOT_DESTROYED(); + if (LayoutNGTable* table = Table()) + table->TableGridStructureChanged(); + LayoutNGMixin<LayoutBlock>::WillBeRemovedFromTree(); +} + void LayoutNGTableRow::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { NOT_DESTROYED(); @@ -105,6 +112,11 @@ LayoutBox* LayoutNGTableRow::CreateAnonymousBoxWithSameTypeAs( return LayoutObjectFactory::CreateAnonymousTableRowWithParent(*parent); } +LayoutBlock* LayoutNGTableRow::StickyContainer() const { + NOT_DESTROYED(); + return Table(); +} + // This is necessary because TableRow paints beyond border box if it contains // rowspanned cells. void LayoutNGTableRow::AddVisualOverflowFromBlockChildren() { @@ -122,6 +134,15 @@ void LayoutNGTableRow::AddVisualOverflowFromBlockChildren() { } } +PositionWithAffinity LayoutNGTableRow::PositionForPoint( + const PhysicalOffset& offset) const { + NOT_DESTROYED(); + DCHECK_GE(GetDocument().Lifecycle().GetState(), + DocumentLifecycle::kPrePaintClean); + // LayoutBlock::PositionForPoint is wrong for rows. + return LayoutBox::PositionForPoint(offset); +} + unsigned LayoutNGTableRow::RowIndex() const { NOT_DESTROYED(); unsigned index = 0; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h index 3e24e3815a0..4fd22a2bdf2 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h @@ -40,16 +40,26 @@ class CORE_EXPORT LayoutNGTableRow : public LayoutNGMixin<LayoutBlock>, void RemoveChild(LayoutObject*) override; + void WillBeRemovedFromTree() override; + void StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) override; LayoutBox* CreateAnonymousBoxWithSameTypeAs( const LayoutObject* parent) const override; + LayoutBlock* StickyContainer() const override; + // Whether a row has opaque background depends on many factors, e.g. border // spacing, border collapsing, missing cells, etc. // For simplicity, just conservatively assume all table rows are not opaque. // Copied from Legacy's LayoutTableRow + bool ForegroundIsKnownToBeOpaqueInRect(const PhysicalRect&, + unsigned) const override { + NOT_DESTROYED(); + return false; + } + bool BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&) const override { NOT_DESTROYED(); return false; @@ -67,6 +77,8 @@ class CORE_EXPORT LayoutNGTableRow : public LayoutNGMixin<LayoutBlock>, return false; } + PositionWithAffinity PositionForPoint(const PhysicalOffset&) const override; + // LayoutBlock methods end. // LayoutNGTableRowInterface methods start. diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc index ad4dfba9498..e675fd2bc15 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc @@ -16,12 +16,7 @@ LayoutNGTableSection::LayoutNGTableSection(Element* element) bool LayoutNGTableSection::IsEmpty() const { NOT_DESTROYED(); - for (LayoutObject* child = FirstChild(); child; - child = child->NextSibling()) { - if (!To<LayoutNGTableRow>(child)->IsEmpty()) - return false; - } - return true; + return !FirstChild(); } LayoutNGTable* LayoutNGTableSection::Table() const { @@ -87,6 +82,13 @@ void LayoutNGTableSection::RemoveChild(LayoutObject* child) { LayoutNGMixin<LayoutBlock>::RemoveChild(child); } +void LayoutNGTableSection::WillBeRemovedFromTree() { + NOT_DESTROYED(); + if (LayoutNGTable* table = Table()) + table->TableGridStructureChanged(); + LayoutNGMixin<LayoutBlock>::WillBeRemovedFromTree(); +} + void LayoutNGTableSection::StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) { NOT_DESTROYED(); @@ -130,7 +132,12 @@ LayoutNGTableRowInterface* LayoutNGTableSection::LastRowInterface() const { // behaviour is correct. Consider removing these methods. unsigned LayoutNGTableSection::NumEffectiveColumns() const { NOT_DESTROYED(); - return To<LayoutNGTable>(TableInterface()->ToLayoutObject())->ColumnCount(); + const LayoutNGTable* table = Table(); + DCHECK(table); + wtf_size_t column_count = table->ColumnCount(); + if (column_count == 0) + return 0; + return table->AbsoluteColumnToEffectiveColumn(column_count - 1) + 1; } // TODO(crbug.com/1079133): Used by AXLayoutObject::IsDataTable/ColumnCount, diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h index abc4f94c2d6..a80d5599b7a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h @@ -39,6 +39,8 @@ class CORE_EXPORT LayoutNGTableSection : public LayoutNGMixin<LayoutBlock>, void RemoveChild(LayoutObject*) override; + void WillBeRemovedFromTree() override; + void StyleDidChange(StyleDifference diff, const ComputedStyle* old_style) override; @@ -50,6 +52,16 @@ class CORE_EXPORT LayoutNGTableSection : public LayoutNGMixin<LayoutBlock>, return false; } + // Whether a section has opaque background depends on many factors, e.g. + // border spacing, border collapsing, missing cells, etc. For simplicity, + // just conservatively assume all table sections are not opaque. + // Copied from LayoutTableSection, + bool ForegroundIsKnownToBeOpaqueInRect(const PhysicalRect&, + unsigned) const override { + NOT_DESTROYED(); + return false; + } + bool BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&) const override { NOT_DESTROYED(); return false; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc index bebd174f4f3..12c20eefe14 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc @@ -197,7 +197,6 @@ scoped_refptr<NGTableBorders> NGTableBorders::ComputeTableBorders( wtf_size_t box_order = 0; wtf_size_t table_column_count = 0; wtf_size_t table_row_index = 0; - // Mark cell borders. bool found_multispan_cells = false; for (const NGBlockNode section : grouped_children) { @@ -209,11 +208,6 @@ scoped_refptr<NGTableBorders> NGTableBorders::ComputeTableBorders( for (NGBlockNode cell = To<NGBlockNode>(row.FirstChild()); cell; cell = To<NGBlockNode>(cell.NextSibling())) { tabulator.FindNextFreeColumn(); - // https://stackoverflow.com/questions/18758373/why-do-the-css-property-border-collapse-and-empty-cells-conflict - if (hide_empty_cells && !To<NGBlockNode>(cell).FirstChild()) { - tabulator.ProcessCell(cell); - continue; - } wtf_size_t cell_colspan = cell.TableCellColspan(); found_multispan_cells |= cell.TableCellRowspan() > 1 || cell_colspan > 1; @@ -224,6 +218,11 @@ scoped_refptr<NGTableBorders> NGTableBorders::ComputeTableBorders( table_column_count, NGTableAlgorithmHelpers::ComputeMaxColumn( tabulator.CurrentColumn(), cell_colspan, table.Style().IsFixedTableLayout())); + // https://stackoverflow.com/questions/18758373/why-do-the-css-property-border-collapse-and-empty-cells-conflict + if (hide_empty_cells && !To<NGBlockNode>(cell).FirstChild()) { + tabulator.ProcessCell(cell); + continue; + } if (!found_multispan_cells) { table_borders->MergeBorders( table_row_index, tabulator.CurrentColumn(), diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h index fb6613cdb57..3872dee5f68 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h @@ -40,11 +40,13 @@ class NGTableConstraintSpaceData struct Section { Section(wtf_size_t start_row_index, wtf_size_t rowspan) : start_row_index(start_row_index), rowspan(rowspan) {} - bool operator==(const Section& other) const { - return start_row_index == other.start_row_index && - rowspan == other.rowspan; + + bool MaySkipLayout(const Section& other) const { + // We don't compare |start_row_index| as this is allowed to change. + return rowspan == other.rowspan; } - wtf_size_t start_row_index; // first section row in table grid. + + wtf_size_t start_row_index; // First section row in table grid. wtf_size_t rowspan; }; @@ -63,15 +65,16 @@ class NGTableConstraintSpaceData has_baseline_aligned_percentage_block_size_descendants( has_baseline_aligned_percentage_block_size_descendants), is_collapsed(is_collapsed) {} - bool operator==(const Row& other) const { + + bool MaySkipLayout(const Row& other) const { + // We don't compare |start_cell_index| as this is allowed to change. return baseline == other.baseline && block_size == other.block_size && - start_cell_index == other.start_cell_index && cell_count == other.cell_count && has_baseline_aligned_percentage_block_size_descendants == other.has_baseline_aligned_percentage_block_size_descendants && is_collapsed == other.is_collapsed; } - bool operator!=(const Row& other) const { return !(*this == other); } + LayoutUnit baseline; LayoutUnit block_size; wtf_size_t start_cell_index; @@ -93,6 +96,7 @@ class NGTableConstraintSpaceData bool operator==(const Cell& other) const { return border_box_borders == other.border_box_borders && block_size == other.block_size && + start_column == other.start_column && is_constrained == other.is_constrained; } bool operator!=(const Cell& other) const { return !(*this == other); } @@ -115,34 +119,67 @@ class NGTableConstraintSpaceData } bool MaySkipRowLayout(const NGTableConstraintSpaceData& other, - wtf_size_t row_index) const { - if (other.rows.size() <= row_index) - return false; - if (rows[row_index] != other.rows[row_index]) + wtf_size_t new_row_index, + wtf_size_t old_row_index) const { + DCHECK_LT(new_row_index, rows.size()); + DCHECK_LT(old_row_index, other.rows.size()); + + const Row& new_row = rows[new_row_index]; + const Row& old_row = other.rows[old_row_index]; + if (!new_row.MaySkipLayout(old_row)) return false; - DCHECK_LT(row_index, rows.size()); - wtf_size_t end_index = - rows[row_index].start_cell_index + rows[row_index].cell_count; - for (wtf_size_t cell_index = rows[row_index].start_cell_index; - cell_index < end_index; ++cell_index) { - if (cells[cell_index] != other.cells[cell_index]) + + DCHECK_EQ(new_row.cell_count, old_row.cell_count); + + const wtf_size_t new_start_cell_index = new_row.start_cell_index; + const wtf_size_t old_start_cell_index = old_row.start_cell_index; + + const wtf_size_t new_end_cell_index = + new_start_cell_index + new_row.cell_count; + const wtf_size_t old_end_cell_index = + old_start_cell_index + old_row.cell_count; + + for (wtf_size_t new_cell_index = new_start_cell_index, + old_cell_index = old_start_cell_index; + new_cell_index < new_end_cell_index && + old_cell_index < old_end_cell_index; + ++new_cell_index, ++old_cell_index) { + if (cells[new_cell_index] != other.cells[old_cell_index]) return false; } + return true; } bool MaySkipSectionLayout(const NGTableConstraintSpaceData& other, - wtf_size_t section_index) const { - if (other.sections.size() <= section_index) + wtf_size_t new_section_index, + wtf_size_t old_section_index) const { + DCHECK_LE(new_section_index, sections.size()); + DCHECK_LE(old_section_index, other.sections.size()); + + const Section& new_section = sections[new_section_index]; + const Section& old_section = other.sections[old_section_index]; + if (!new_section.MaySkipLayout(old_section)) return false; - DCHECK_LT(section_index, sections.size()); - wtf_size_t end_index = sections[section_index].start_row_index + - sections[section_index].rowspan; - for (wtf_size_t row_index = sections[section_index].start_row_index; - row_index < end_index; ++row_index) { - if (!MaySkipRowLayout(other, row_index)) + + DCHECK_EQ(new_section.rowspan, old_section.rowspan); + + const wtf_size_t new_start_row_index = new_section.start_row_index; + const wtf_size_t old_start_row_index = old_section.start_row_index; + + const wtf_size_t new_end_row_index = + new_start_row_index + new_section.rowspan; + const wtf_size_t old_end_row_index = + old_start_row_index + old_section.rowspan; + + for (wtf_size_t new_row_index = new_start_row_index, + old_row_index = old_start_row_index; + new_row_index < new_end_row_index && old_row_index < old_end_row_index; + ++new_row_index, ++old_row_index) { + if (!MaySkipRowLayout(other, new_row_index, old_row_index)) return false; } + return true; } @@ -163,13 +200,13 @@ class NGTableConstraintSpaceData } // namespace blink -WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS( blink::NGTableConstraintSpaceData::ColumnLocation) WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( blink::NGTableConstraintSpaceData::Section) -WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS( blink::NGTableConstraintSpaceData::Row) -WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( +WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS( blink::NGTableConstraintSpaceData::Cell) #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_CONSTRAINT_SPACE_DATA_H_ diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h index 08f34ce4e89..a4be953ae6f 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h @@ -41,6 +41,14 @@ class NGTableFragmentData { struct CollapsedBordersGeometry { Vector<LayoutUnit> columns; // Column offsets from table grid border. Vector<LayoutUnit> rows; // Row offsets from table grid border. + +#if DCHECK_IS_ON() + void CheckSameForSimplifiedLayout( + const CollapsedBordersGeometry& other) const { + DCHECK(columns == other.columns); + DCHECK(rows == other.rows); + } +#endif }; }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc index 6e635dd7501..1b76c2c5479 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc @@ -31,20 +31,6 @@ namespace blink { namespace { -// TODO(atotic, ikilpatrick) -// Copy of ShouldScaleColumnsForParent from table_layout_algorithm_auto.cc -// The real fix would be for containers to understand that -// Table max really means: max should be trimmed to available inline size. -bool ShouldIgnorePercentagesForMinMax(const LayoutBox& table) { - return false; -} - -// Another MinMax variant of ShouldIgnorePercentagesForMinMax -// This one is only for flexbox/grid. CSS size should be ignored. -bool ShouldIgnoreCssSizesForMinMax(const LayoutBlock& table) { - return false; -} - NGTableTypes::Caption ComputeCaptionConstraint( const ComputedStyle& table_style, const NGTableGroupedChildren& grouped_children) { @@ -69,51 +55,19 @@ LayoutUnit ComputeUndistributableTableSpace( const NGTableTypes::Columns& column_constraints, LayoutUnit inline_table_border_padding, LayoutUnit inline_border_spacing) { - unsigned inline_space_count = 2 + (column_constraints.data.size() > 1 - ? column_constraints.data.size() - 1 - : 0); - return inline_table_border_padding + - inline_space_count * inline_border_spacing; -} - -void ComputeTableCssInlineSizes( - const ComputedStyle& table_style, - const NGConstraintSpace& constraint_space, - const NGBoxStrut& table_border_padding, - const MinMaxSizes& grid_min_max, - LayoutUnit* table_css_min_inline_size, - base::Optional<LayoutUnit>* table_css_inline_size, - base::Optional<LayoutUnit>* table_css_max_inline_size) { - const Length& table_min_length = table_style.LogicalMinWidth(); - *table_css_min_inline_size = - table_min_length.IsSpecified() - ? ResolveMinInlineLength<base::Optional<MinMaxSizes>>( - constraint_space, table_style, table_border_padding, - base::nullopt, table_min_length, LengthResolvePhase::kLayout) - : LayoutUnit(); - - // Compute standard "used width of a table". - const Length& table_length = table_style.LogicalWidth(); - if (!table_length.IsAuto()) { - *table_css_inline_size = ResolveMainInlineLength( - constraint_space, table_style, table_border_padding, - [grid_min_max](MinMaxSizesType) { - return MinMaxSizesResult{ - grid_min_max, - /* depends_on_percentage_block_size */ false}; - }, - table_length); + unsigned inline_space_count = 2; + bool is_first_column = true; + for (const NGTableTypes::Column& column : column_constraints.data) { + if (!column.is_mergeable) { + if (is_first_column) + is_first_column = false; + else + inline_space_count++; + } } - const Length& table_max_length = table_style.LogicalMaxWidth(); - if (table_max_length.IsSpecified()) { - *table_css_max_inline_size = - ResolveMaxInlineLength<base::Optional<MinMaxSizes>>( - constraint_space, table_style, table_border_padding, base::nullopt, - table_max_length, LengthResolvePhase::kLayout); - *table_css_max_inline_size = - std::max(**table_css_max_inline_size, *table_css_min_inline_size); - } + return inline_table_border_padding + + inline_space_count * inline_border_spacing; } // Empty table sizes have been a source of many inconsistencies @@ -145,7 +99,7 @@ LayoutUnit ComputeEmptyTableInlineSize( // standard: https://www.w3.org/TR/css-tables-3/#computing-the-table-width LayoutUnit ComputeAssignableTableInlineSize( - const NGBlockNode& table, + const NGTableNode& table, const NGConstraintSpace& space, const NGTableTypes::Columns& column_constraints, const NGTableTypes::Caption& caption_constraint, @@ -156,45 +110,27 @@ LayoutUnit ComputeAssignableTableInlineSize( return (space.AvailableSize().inline_size - undistributable_space) .ClampNegativeToZero(); } - MinMaxSizes grid_min_max = NGTableAlgorithmHelpers::ComputeGridInlineMinMax( - column_constraints, undistributable_space, is_fixed_layout, - /* containing_block_expects_minmax_without_percentages */ false, - /* skip_collapsed_columns */ false); - - LayoutUnit table_css_min_inline_size; - base::Optional<LayoutUnit> table_css_inline_size; - base::Optional<LayoutUnit> table_css_max_inline_size; - ComputeTableCssInlineSizes(table.Style(), space, table_border_padding, - grid_min_max, &table_css_min_inline_size, - &table_css_inline_size, - &table_css_max_inline_size); - - LayoutUnit table_min_inline_size = - std::max({table_css_min_inline_size, caption_constraint.min_size, - grid_min_max.min_size}); + + const MinMaxSizes grid_min_max = + NGTableAlgorithmHelpers::ComputeGridInlineMinMax( + table, column_constraints, undistributable_space, is_fixed_layout, + /* is_layout_pass */ true, + /* skip_collapsed_columns */ false); // Standard: "used width of the table". - LayoutUnit used_inline_size_of_the_table; - if (table_css_inline_size) { - used_inline_size_of_the_table = *table_css_inline_size; - } else { - NGBoxStrut margins = ComputeMarginsForSelf(space, table.Style()); - used_inline_size_of_the_table = - std::min(grid_min_max.max_size, - (space.AvailableSize().inline_size - margins.InlineSum()) - .ClampNegativeToZero()); - } - if (table_css_max_inline_size) { - used_inline_size_of_the_table = - std::min(used_inline_size_of_the_table, *table_css_max_inline_size); - } - used_inline_size_of_the_table = - std::max(used_inline_size_of_the_table, table_min_inline_size); + LayoutUnit used_table_inline_size = ComputeUsedInlineSizeForTableFragment( + space, table, table_border_padding, grid_min_max); + + // Don't allow the inline-size to go below the grid, or caption min-size. + used_table_inline_size = + std::max({used_table_inline_size, caption_constraint.min_size, + grid_min_max.min_size}); // Standard: The assignable table width is the "used width of the table" // minus the total horizontal border spacing. - LayoutUnit assignable_table_inline_size = - used_inline_size_of_the_table - undistributable_space; + const LayoutUnit assignable_table_inline_size = + used_table_inline_size - undistributable_space; + DCHECK_GE(assignable_table_inline_size, LayoutUnit()); return assignable_table_inline_size; } @@ -218,7 +154,13 @@ void ComputeLocationsFromColumns( *has_collapsed_columns = *has_collapsed_columns || column_constraint.is_collapsed; column_location.offset = column_offset; - if (shrink_collapsed && column_constraint.is_collapsed) { + if (column_constraints.data[i].is_mergeable && + (column_sizes[i] == kIndefiniteSize || + column_sizes[i] == LayoutUnit())) { + // Empty mergeable columns are treated as collapsed. + column_location.size = LayoutUnit(); + column_location.is_collapsed = true; + } else if (shrink_collapsed && column_constraint.is_collapsed) { column_location.is_collapsed = true; column_location.size = LayoutUnit(); } else { @@ -230,7 +172,7 @@ void ComputeLocationsFromColumns( } } -scoped_refptr<NGTableConstraintSpaceData> CreateConstraintSpaceData( +scoped_refptr<const NGTableConstraintSpaceData> CreateConstraintSpaceData( const ComputedStyle& style, const NGTableTypes::ColumnLocations& column_locations, const NGTableTypes::Sections& sections, @@ -309,19 +251,15 @@ class ColumnGeometriesBuilder { wtf_size_t span) { wtf_size_t end_column_index = start_column_index + span - 1; DCHECK_LE(end_column_index, column_locations.size() - 1); - LayoutUnit column_width = column_locations[end_column_index].offset + - column_locations[end_column_index].size - - column_locations[start_column_index].offset; - col.GetLayoutBox()->SetLogicalWidth(column_width); + LayoutUnit column_inline_size = column_locations[end_column_index].offset + + column_locations[end_column_index].size - + column_locations[start_column_index].offset; + col.GetLayoutBox()->SetLogicalWidth(column_inline_size); col.GetLayoutBox()->SetLogicalHeight(table_grid_block_size); - for (unsigned i = 0; i < span; ++i) { - wtf_size_t current_column_index = start_column_index + i; - column_geometries.emplace_back( - current_column_index, /* span */ 1, - column_locations[current_column_index].offset - - border_spacing.inline_size, - column_locations[current_column_index].size, col); - } + column_geometries.emplace_back(start_column_index, span, + column_locations[start_column_index].offset - + border_spacing.inline_size, + column_inline_size, col); } void EnterColgroup(const NGLayoutInputNode& colgroup, @@ -333,28 +271,16 @@ class ColumnGeometriesBuilder { bool has_children) { if (span == 0) return; - if (has_children) { - wtf_size_t last_column_index = start_column_index + span - 1; - LayoutUnit colgroup_size = column_locations[last_column_index].offset + - column_locations[last_column_index].size - - column_locations[start_column_index].offset; - colgroup.GetLayoutBox()->SetLogicalWidth(colgroup_size); - colgroup.GetLayoutBox()->SetLogicalHeight(table_grid_block_size); - column_geometries.emplace_back( - start_column_index, span, - column_locations[start_column_index].offset - - border_spacing.inline_size, - colgroup_size, colgroup); - } else { - for (unsigned i = 0; i < span; ++i) { - wtf_size_t current_column_index = start_column_index + i; - column_geometries.emplace_back( - current_column_index, /* span */ 1, - column_locations[current_column_index].offset - - border_spacing.inline_size, - column_locations[current_column_index].size, colgroup); - } - } + wtf_size_t last_column_index = start_column_index + span - 1; + LayoutUnit colgroup_size = column_locations[last_column_index].offset + + column_locations[last_column_index].size - + column_locations[start_column_index].offset; + colgroup.GetLayoutBox()->SetLogicalWidth(colgroup_size); + colgroup.GetLayoutBox()->SetLogicalHeight(table_grid_block_size); + column_geometries.emplace_back(start_column_index, span, + column_locations[start_column_index].offset - + border_spacing.inline_size, + colgroup_size, colgroup); } void Sort() { @@ -368,6 +294,8 @@ class ColumnGeometriesBuilder { return a.start_column < b.start_column; } if (a.node.IsTableColgroup()) { + if (b.node.IsTableColgroup()) + return a.start_column < b.start_column; if (a.start_column <= b.start_column && (a.start_column + a.span) > b.start_column) { return true; @@ -454,24 +382,15 @@ LayoutUnit NGTableLayoutAlgorithm::ComputeTableInlineSize( ComputeLocationsFromColumns( *column_constraints, column_sizes, border_spacing.inline_size, /* collapse_columns */ true, &column_locations, &has_collapsed_columns); - return ComputeTableSizeFromColumns(column_locations, table_border_padding, - border_spacing); + return std::max(ComputeTableSizeFromColumns( + column_locations, table_border_padding, border_spacing), + caption_constraint.min_size); } scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::Layout() { DCHECK(!BreakToken()); - const bool is_fixed_layout = Style().IsFixedTableLayout(); - - // TODO(atotic) review autosizer usage in TablesNG. - // Legacy has: - // LayoutTable::UpdateLayout - // TextAutosizer::LayoutScope - // TableLayoutAlgorithmAuto::ComputeIntrinsicLogicalWidths - // TextAutosizer::TableLayoutScope - base::Optional<TextAutosizer::TableLayoutScope> text_autosizer; - if (!is_fixed_layout) - text_autosizer.emplace(To<LayoutNGTable>(Node().GetLayoutBox())); + const bool is_fixed_layout = Style().IsFixedTableLayout(); const LogicalSize border_spacing = Style().TableBorderSpacing(); NGTableGroupedChildren grouped_children(Node()); const scoped_refptr<const NGTableBorders> table_borders = @@ -524,14 +443,24 @@ scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::Layout() { column_locations, border_padding, border_spacing); } + // Before we can determine the block-size of the sections/rows, we need to + // layout all of our captions. + // + // The block-size taken by the captions, *subtracts* from the available + // block-size given to the table-grid. + Vector<CaptionResult> captions; + LayoutUnit captions_block_size; + ComputeCaptionFragments(grouped_children, container_builder_.InlineSize(), + captions, captions_block_size); + NGTableTypes::Rows rows; NGTableTypes::CellBlockConstraints cell_block_constraints; NGTableTypes::Sections sections; LayoutUnit minimal_table_grid_block_size; ComputeRows(table_inline_size_before_collapse - border_padding.InlineSum(), grouped_children, column_locations, *table_borders, - border_spacing, border_padding, is_fixed_layout, &rows, - &cell_block_constraints, §ions, + border_spacing, border_padding, captions_block_size, + is_fixed_layout, &rows, &cell_block_constraints, §ions, &minimal_table_grid_block_size); if (has_collapsed_columns) { @@ -540,35 +469,43 @@ scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::Layout() { /* shrink_collapsed */ true, &column_locations, &has_collapsed_columns); } #if DCHECK_IS_ON() - LayoutUnit table_inline_size; - if (has_collapsed_columns) { - table_inline_size = ComputeTableSizeFromColumns( - column_locations, border_padding, border_spacing); - table_inline_size = - std::max(table_inline_size, caption_constraint.min_size); + if (!has_collapsed_columns) { + // Colums define table whose inline size equals InitialFragmentGeometry. + DCHECK_EQ(table_inline_size_before_collapse, + container_builder_.InlineSize()); + } else if (ConstraintSpace().IsFixedInlineSize()) { + // Collapsed columns + fixed inline size: columns define table whose + // inline size is less or equal InitialFragmentGeometry. + LayoutUnit table_inline_size = + std::max(ComputeTableSizeFromColumns(column_locations, border_padding, + border_spacing), + caption_constraint.min_size); + DCHECK_LE(table_inline_size, container_builder_.InlineSize()); } else { - table_inline_size = table_inline_size_before_collapse; + LayoutUnit table_inline_size = + std::max(ComputeTableSizeFromColumns(column_locations, border_padding, + border_spacing), + caption_constraint.min_size); + DCHECK_EQ(table_inline_size, container_builder_.InlineSize()); } - DCHECK_EQ(table_inline_size, container_builder_.InlineSize()); #endif - return GenerateFragment( - container_builder_.InlineSize(), minimal_table_grid_block_size, - grouped_children, column_locations, rows, cell_block_constraints, - sections, *table_borders, is_grid_empty ? LogicalSize() : border_spacing); + return GenerateFragment(container_builder_.InlineSize(), + minimal_table_grid_block_size, grouped_children, + column_locations, rows, cell_block_constraints, + sections, captions, *table_borders, + is_grid_empty ? LogicalSize() : border_spacing); } MinMaxSizesResult NGTableLayoutAlgorithm::ComputeMinMaxSizes( const MinMaxSizesInput& input) const { - LayoutNGTable* layout_table = To<LayoutNGTable>(Node().GetLayoutBox()); const bool is_fixed_layout = Style().IsFixedTableLayout(); // Tables need autosizer. base::Optional<TextAutosizer::TableLayoutScope> text_autosizer; if (!is_fixed_layout) - text_autosizer.emplace(layout_table); + text_autosizer.emplace(To<LayoutNGTable>(Node().GetLayoutBox())); const LogicalSize border_spacing = Style().TableBorderSpacing(); - const auto table_writing_direction = Style().GetWritingDirection(); NGTableGroupedChildren grouped_children(Node()); const scoped_refptr<const NGTableBorders> table_borders = Node().GetTableBorders(); @@ -585,66 +522,17 @@ MinMaxSizesResult NGTableLayoutAlgorithm::ComputeMinMaxSizes( const MinMaxSizes grid_min_max = NGTableAlgorithmHelpers::ComputeGridInlineMinMax( - *column_constraints, undistributable_space, is_fixed_layout, - ShouldIgnorePercentagesForMinMax(*layout_table), - /* skip_collapsed_columns */ true); + Node(), *column_constraints, undistributable_space, is_fixed_layout, + /* is_layout_pass */ false, + /* skip_collapsed_columns */ false); MinMaxSizes min_max{ std::max(grid_min_max.min_size, caption_constraint.min_size), std::max(grid_min_max.max_size, caption_constraint.min_size)}; - if (ShouldIgnoreCssSizesForMinMax(*layout_table)) { - return MinMaxSizesResult{min_max, - /* depends_on_percentage_block_size */ false}; - } - - NGConstraintSpaceBuilder min_space_builder( - ConstraintSpace(), table_writing_direction, /* is_new_fc */ true); - min_space_builder.SetAvailableSize({LayoutUnit(), kIndefiniteSize}); - LayoutUnit min_measure_table_css_min_inline_size; - base::Optional<LayoutUnit> min_measure_table_css_inline_size; - base::Optional<LayoutUnit> measure_table_css_max_inline_size; - - ComputeTableCssInlineSizes( - Style(), min_space_builder.ToConstraintSpace(), border_padding, - grid_min_max, &min_measure_table_css_min_inline_size, - &min_measure_table_css_inline_size, &measure_table_css_max_inline_size); - - // Table min/max sizes are unusual in how the specified sizes affects them. - // If table_css_inline_size is defined: - // min_max_sizes is std::max( - // table_css_inline_size, - // grid_min_max.min_size, - // caption_constraint.min_size) - // (min_size and max_size are the same value). - // - // If table_css_inline_size is not defined: - // min_max_sizes.min_size is std::max( - // grid_min_max.min_size, caption_constraint.min_size) - // min_max_sizes.max_size is std::max( - // grid_min_max.max_size, caption_constraint.min_size) - if (min_measure_table_css_inline_size) { - NGConstraintSpaceBuilder max_space_builder( - ConstraintSpace(), table_writing_direction, /* is_new_fc */ true); - max_space_builder.SetAvailableSize( - {grid_min_max.max_size, kIndefiniteSize}); - LayoutUnit max_measure_table_css_min_inline_size; - base::Optional<LayoutUnit> max_measure_table_css_inline_size; - ComputeTableCssInlineSizes( - Style(), max_space_builder.ToConstraintSpace(), border_padding, - grid_min_max, &max_measure_table_css_min_inline_size, - &max_measure_table_css_inline_size, &measure_table_css_max_inline_size); - // Compute minimum. - min_max.min_size = - std::max({min_max.min_size, min_measure_table_css_min_inline_size, - *min_measure_table_css_inline_size}); - // Compute maximum. - min_max.max_size = - std::max({max_measure_table_css_min_inline_size, - *max_measure_table_css_inline_size, grid_min_max.min_size, - caption_constraint.min_size}); - if (is_fixed_layout && Style().LogicalWidth().IsPercentOrCalc()) - min_max.max_size = NGTableTypes::kTableMaxInlineSize; + if (is_fixed_layout && Style().LogicalWidth().IsPercentOrCalc() && + Node().AllowsInfiniteMaxInlineSize()) { + min_max.max_size = NGTableTypes::kTableMaxInlineSize; } DCHECK_LE(min_max.min_size, min_max.max_size); return MinMaxSizesResult{min_max, @@ -658,6 +546,7 @@ void NGTableLayoutAlgorithm::ComputeRows( const NGTableBorders& table_borders, const LogicalSize& border_spacing, const NGBoxStrut& table_border_padding, + const LayoutUnit captions_block_size, bool is_fixed, NGTableTypes::Rows* rows, NGTableTypes::CellBlockConstraints* cell_block_constraints, @@ -666,35 +555,36 @@ void NGTableLayoutAlgorithm::ComputeRows( DCHECK_EQ(rows->size(), 0u); DCHECK_EQ(cell_block_constraints->size(), 0u); - // Initially resolve the table's block-size with an indefinite size. - LayoutUnit css_table_block_size = ComputeBlockSizeForFragment( - ConstraintSpace(), Style(), table_border_padding, kIndefiniteSize, - table_grid_inline_size); - const bool is_table_block_size_specified = !Style().LogicalHeight().IsAuto(); LayoutUnit total_table_block_size; wtf_size_t section_index = 0; - for (const NGBlockNode& section : grouped_children) { + for (auto it = grouped_children.begin(); it != grouped_children.end(); ++it) { NGTableAlgorithmUtils::ComputeSectionMinimumRowBlockSizes( - section, table_grid_inline_size, is_table_block_size_specified, + *it, table_grid_inline_size, is_table_block_size_specified, column_locations, table_borders, border_spacing.block_size, - section_index++, sections, rows, cell_block_constraints); + section_index++, it.TreatAsTBody(), sections, rows, + cell_block_constraints); total_table_block_size += sections->back().block_size; } - // Re-resolve the block-size if we have a min block-size which is resolvable. - // We'll redistribute sections/rows into this space. - if (!BlockLengthUnresolvable(ConstraintSpace(), Style().LogicalMinHeight(), - LengthResolvePhase::kLayout)) { - css_table_block_size = ComputeBlockSizeForFragment( - ConstraintSpace(), Style(), table_border_padding, - table_border_padding.BlockSum(), table_grid_inline_size); - } - - // In quirks mode, empty tables ignore css block size. - bool is_empty_quirks_mode_table = + // If we can correctly resolve our min-block-size we want to distribute + // sections/rows into this space. Pass a definite intrinsic block-size into + // |ComputeBlockSizeForFragment| to force it to resolve. + const LayoutUnit intrinsic_block_size = + BlockLengthUnresolvable(ConstraintSpace(), Style().LogicalMinHeight()) + ? kIndefiniteSize + : table_border_padding.BlockSum(); + + const LayoutUnit css_table_block_size = ComputeBlockSizeForFragment( + ConstraintSpace(), Style(), table_border_padding, intrinsic_block_size, + table_grid_inline_size, Node().ShouldBeConsideredAsReplaced(), + /* available_block_size_adjustment */ captions_block_size); + + // In quirks mode, empty tables ignore any specified block-size. + const bool is_empty_quirks_mode_table = Node().GetDocument().InQuirksMode() && grouped_children.begin() == grouped_children.end(); + // Redistribute CSS table block size if necessary. if (css_table_block_size != kIndefiniteSize && !is_empty_quirks_mode_table) { *minimal_table_grid_block_size = css_table_block_size; @@ -729,8 +619,6 @@ void NGTableLayoutAlgorithm::ComputeTableSpecificFragmentData( const PhysicalRect& table_grid_rect, const LogicalSize& border_spacing, const LayoutUnit table_grid_block_size) { - // TODO(atotic) SetHasNonCollapsedBorderDecoration should be a fragment - // property, not a flag on LayoutObject. container_builder_.SetTableGridRect(table_grid_rect); container_builder_.SetTableColumnCount(column_locations.size()); container_builder_.SetHasCollapsedBorders(table_borders.IsCollapsed()); @@ -741,7 +629,7 @@ void NGTableLayoutAlgorithm::ComputeTableSpecificFragmentData( VisitLayoutNGTableColumn(grouped_children.columns, column_locations.size(), &geometry_builder); geometry_builder.Sort(); - container_builder_.SetTableColumnGeometry( + container_builder_.SetTableColumnGeometries( geometry_builder.column_geometries); } // Collapsed borders. @@ -754,6 +642,7 @@ void NGTableLayoutAlgorithm::ComputeTableSpecificFragmentData( fragment_borders_geometry->columns.push_back(column.offset + grid_inline_start); } + DCHECK_NE(column_locations.size(), 0u); fragment_borders_geometry->columns.push_back( column_locations.back().offset + column_locations.back().size + grid_inline_start); @@ -769,16 +658,14 @@ void NGTableLayoutAlgorithm::ComputeTableSpecificFragmentData( } } -void NGTableLayoutAlgorithm::GenerateCaptionFragments( +void NGTableLayoutAlgorithm::ComputeCaptionFragments( const NGTableGroupedChildren& grouped_children, const LayoutUnit table_inline_size, - ECaptionSide caption_side, - LayoutUnit* table_block_offset) { + Vector<CaptionResult>& captions, + LayoutUnit& captions_block_size) { const LogicalSize available_size = {table_inline_size, kIndefiniteSize}; for (NGBlockNode caption : grouped_children.captions) { const auto& caption_style = caption.Style(); - if (caption_style.CaptionSide() != caption_side) - continue; NGConstraintSpaceBuilder builder(ConstraintSpace(), caption_style.GetWritingDirection(), @@ -786,6 +673,7 @@ void NGTableLayoutAlgorithm::GenerateCaptionFragments( SetOrthogonalFallbackInlineSizeIfNeeded(Style(), caption, &builder); builder.SetAvailableSize(available_size); builder.SetPercentageResolutionSize(available_size); + builder.SetStretchInlineSizeIfAuto(true); NGConstraintSpace caption_constraint_space = builder.ToConstraintSpace(); scoped_refptr<const NGLayoutResult> caption_result = @@ -796,14 +684,10 @@ void NGTableLayoutAlgorithm::GenerateCaptionFragments( caption_style, ConstraintSpace()); ResolveInlineMargins(caption_style, Style(), table_inline_size, fragment.InlineSize(), &margins); - caption.StoreMargins( - margins.ConvertToPhysical(ConstraintSpace().GetWritingDirection())); - *table_block_offset += margins.block_start; - container_builder_.AddResult( - *caption_result, - LogicalOffset(margins.inline_start, *table_block_offset)); - *table_block_offset += fragment.BlockSize() + margins.block_end; + captions.push_back( + CaptionResult{caption, std::move(caption_result), margins}); + captions_block_size += fragment.BlockSize() + margins.BlockSum(); } } @@ -827,38 +711,60 @@ scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::GenerateFragment( const NGTableTypes::Rows& rows, const NGTableTypes::CellBlockConstraints& cell_block_constraints, const NGTableTypes::Sections& sections, + const Vector<CaptionResult>& captions, const NGTableBorders& table_borders, const LogicalSize& border_spacing) { const auto table_writing_direction = Style().GetWritingDirection(); - scoped_refptr<NGTableConstraintSpaceData> constraint_space_data = + scoped_refptr<const NGTableConstraintSpaceData> constraint_space_data = CreateConstraintSpaceData(Style(), column_locations, sections, rows, cell_block_constraints, table_inline_size, border_spacing); const NGBoxStrut border_padding = container_builder_.BorderPadding(); - LayoutUnit table_block_end; + LayoutUnit block_offset; - // Top caption fragments. - GenerateCaptionFragments(grouped_children, table_inline_size, - ECaptionSide::kTop, &table_block_end); + auto AddCaptionResult = [&](const auto& caption, + LayoutUnit* block_offset) -> void { + NGBlockNode node = caption.node; + node.StoreMargins( + caption.margins.ConvertToPhysical(table_writing_direction)); + + *block_offset += caption.margins.block_start; + container_builder_.AddResult( + *caption.layout_result, + LogicalOffset(caption.margins.inline_start, *block_offset)); + + *block_offset += NGFragment(table_writing_direction, + caption.layout_result->PhysicalFragment()) + .BlockSize() + + caption.margins.block_end; + }; + + // Add all the top captions. + for (const auto& caption : captions) { + if (caption.node.Style().CaptionSide() == ECaptionSide::kTop) + AddCaptionResult(caption, &block_offset); + } // Section setup. - const LogicalSize section_available_size{table_inline_size - - border_padding.InlineSum() - - border_spacing.inline_size * 2, - kIndefiniteSize}; - DCHECK_GE(section_available_size.inline_size, LayoutUnit()); + const LayoutUnit section_available_inline_size = + (table_inline_size - border_padding.InlineSum() - + border_spacing.inline_size * 2) + .ClampNegativeToZero(); + auto CreateSectionConstraintSpace = [&table_writing_direction, - §ion_available_size, - &constraint_space_data]( - wtf_size_t section_index) { + §ion_available_inline_size, + &constraint_space_data, + §ions](wtf_size_t section_index) { NGConstraintSpaceBuilder section_space_builder( table_writing_direction.GetWritingMode(), table_writing_direction, /* is_new_fc */ true); - section_space_builder.SetAvailableSize(section_available_size); + section_space_builder.SetAvailableSize( + {section_available_inline_size, sections[section_index].block_size}); section_space_builder.SetIsFixedInlineSize(true); - section_space_builder.SetPercentageResolutionSize(section_available_size); - section_space_builder.SetNeedsBaseline(true); + section_space_builder.SetIsFixedBlockSize(true); + section_space_builder.SetPercentageResolutionSize( + {section_available_inline_size, kIndefiniteSize}); section_space_builder.SetTableSectionData(constraint_space_data, section_index); return section_space_builder.ToConstraintSpace(); @@ -868,50 +774,56 @@ scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::GenerateFragment( LogicalOffset section_offset; section_offset.inline_offset = border_padding.inline_start + border_spacing.inline_size; - section_offset.block_offset = table_block_end + border_padding.block_start; + section_offset.block_offset = block_offset + border_padding.block_start; base::Optional<LayoutUnit> table_baseline; wtf_size_t section_index = 0; - bool has_section = false; + bool needs_end_border_spacing = false; for (NGBlockNode section : grouped_children) { scoped_refptr<const NGLayoutResult> section_result = section.Layout(CreateSectionConstraintSpace(section_index++)); const NGPhysicalBoxFragment& physical_fragment = To<NGPhysicalBoxFragment>(section_result->PhysicalFragment()); NGBoxFragment fragment(table_writing_direction, physical_fragment); - if (fragment.BlockSize() != LayoutUnit() || !has_section) + if (fragment.HasDescendantsForTablePart()) { section_offset.block_offset += border_spacing.block_size; + needs_end_border_spacing = true; + } container_builder_.AddResult(*section_result, section_offset); if (!table_baseline) { if (const auto& section_baseline = fragment.Baseline()) table_baseline = *section_baseline + section_offset.block_offset; } section_offset.block_offset += fragment.BlockSize(); - has_section = true; } - if (has_section) + if (needs_end_border_spacing) section_offset.block_offset += border_spacing.block_size; LayoutUnit column_block_size = section_offset.block_offset - border_padding.block_start; - if (has_section) + if (needs_end_border_spacing) column_block_size -= border_spacing.block_size * 2; - LayoutUnit grid_block_size = std::max( - section_offset.block_offset - table_block_end + border_padding.block_end, - minimal_table_grid_block_size); - LogicalRect table_grid_rect(LayoutUnit(), table_block_end, - container_builder_.InlineSize(), grid_block_size); - table_block_end += grid_block_size; - GenerateCaptionFragments(grouped_children, table_inline_size, - ECaptionSide::kBottom, &table_block_end); + const LayoutUnit grid_block_size = std::max( + section_offset.block_offset - block_offset + border_padding.block_end, + minimal_table_grid_block_size); + const LogicalRect table_grid_rect(LayoutUnit(), block_offset, + container_builder_.InlineSize(), + grid_block_size); + block_offset += grid_block_size; + + // Add all the bottom captions. + for (const auto& caption : captions) { + if (caption.node.Style().CaptionSide() == ECaptionSide::kBottom) + AddCaptionResult(caption, &block_offset); + } if (ConstraintSpace().IsFixedBlockSize()) { container_builder_.SetFragmentBlockSize( ConstraintSpace().AvailableSize().block_size); } else { - container_builder_.SetFragmentBlockSize(table_block_end); + container_builder_.SetFragmentBlockSize(block_offset); } - container_builder_.SetIntrinsicBlockSize(table_block_end); + container_builder_.SetIntrinsicBlockSize(block_offset); const WritingModeConverter grid_converter( Style().GetWritingDirection(), diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h index 60682fd159e..a525aeb7c83 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h @@ -38,16 +38,26 @@ class CORE_EXPORT NGTableLayoutAlgorithm const NGTableBorders& table_borders, const LogicalSize& border_spacing, const NGBoxStrut& table_border_padding, + const LayoutUnit captions_block_size, bool is_fixed_layout, NGTableTypes::Rows* rows, NGTableTypes::CellBlockConstraints* cell_block_constraints, NGTableTypes::Sections* sections, LayoutUnit* minimal_table_grid_block_size); - void GenerateCaptionFragments(const NGTableGroupedChildren& grouped_children, - LayoutUnit table_inline_size, - ECaptionSide caption_side, - LayoutUnit* table_block_offset); + // In order to correctly determine the available block-size given to the + // table-grid, we need to layout all the captions ahead of time. This struct + // stores the necessary information to add them to the fragment later. + struct CaptionResult { + NGBlockNode node; + scoped_refptr<const NGLayoutResult> layout_result; + const NGBoxStrut margins; + }; + + void ComputeCaptionFragments(const NGTableGroupedChildren& grouped_children, + LayoutUnit table_inline_size, + Vector<CaptionResult>& captions, + LayoutUnit& captions_block_size); void ComputeTableSpecificFragmentData( const NGTableGroupedChildren& grouped_children, @@ -66,6 +76,7 @@ class CORE_EXPORT NGTableLayoutAlgorithm const NGTableTypes::Rows& rows, const NGTableTypes::CellBlockConstraints& cell_block_constraints, const NGTableTypes::Sections& sections, + const Vector<CaptionResult>& captions, const NGTableBorders& table_borders, const LogicalSize& border_spacing); }; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc index e1f6ceb2d0f..d7e735e271a 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc @@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h" #include "third_party/blink/renderer/core/layout/geometry/logical_size.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_node.h" namespace blink { @@ -21,7 +22,6 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( unsigned percent_columns_count = 0; unsigned fixed_columns_count = 0; unsigned auto_columns_count = 0; - // What guesses mean is described in table specification. // https://www.w3.org/TR/css-tables-3/#width-distribution-algorithm enum { kMinGuess, kPercentageGuess, kSpecifiedGuess, kMaxGuess, kAboveMax }; @@ -37,7 +37,10 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( all_columns_count++; DCHECK(column->min_inline_size); DCHECK(column->max_inline_size); - if (column->percent) { + + if (column->is_mergeable) { + ; // Mergeable columns are ignored. + } else if (column->percent) { percent_columns_count++; total_percent += *column->percent; LayoutUnit percent_inline_size = @@ -91,6 +94,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* computed_size = computed_sizes.begin(); for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; *computed_size = column->min_inline_size.value_or(LayoutUnit()); } } break; @@ -105,6 +110,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* last_computed_size = nullptr; for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; if (column->percent) { last_computed_size = computed_size; LayoutUnit percent_inline_size = @@ -143,6 +150,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* computed_size = computed_sizes.begin(); for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; if (column->percent) { *computed_size = column->ResolvePercentInlineSize(target_inline_size); } else if (column->is_constrained) { @@ -189,6 +198,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* computed_size = computed_sizes.begin(); for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; if (column->percent) { *computed_size = column->ResolvePercentInlineSize(target_inline_size); } else if (column->is_constrained || is_exact_match) { @@ -225,6 +236,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* computed_size = computed_sizes.begin(); for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; if (column->percent) { *computed_size = column->ResolvePercentInlineSize(target_inline_size); @@ -255,6 +268,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* computed_size = computed_sizes.begin(); for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; if (column->percent) { *computed_size = column->ResolvePercentInlineSize(target_inline_size); @@ -285,6 +300,8 @@ Vector<LayoutUnit> DistributeInlineSizeToComputedInlineSizeAuto( LayoutUnit* computed_size = computed_sizes.begin(); for (const NGTableTypes::Column* column = start_column; column != end_column; ++column, ++computed_size) { + if (column->is_mergeable) + continue; DCHECK(column->percent); last_computed_size = computed_size; if (total_percent > 0.0f) { @@ -446,18 +463,32 @@ void DistributeColspanCellToColumnsFixed( NGTableTypes::Column* end_column = start_column + colspan_cell.span; DCHECK_NE(start_column, end_column); + // Inline sizes for redistribution exclude border spacing. + LayoutUnit total_inner_border_spacing; + unsigned effective_span = 0; + bool is_first_column = true; + for (NGTableTypes::Column* column = start_column; column != end_column; + ++column) { + if (column->is_mergeable) + continue; + ++effective_span; + if (!is_first_column) + total_inner_border_spacing += inline_border_spacing; + else + is_first_column = false; + } LayoutUnit colspan_cell_min_inline_size; LayoutUnit colspan_cell_max_inline_size; // Colspanned cells only distribute min inline size if constrained. if (colspan_cell.cell_inline_constraint.is_constrained) { colspan_cell_min_inline_size = (colspan_cell.cell_inline_constraint.min_inline_size - - (colspan_cell.span - 1) * inline_border_spacing) + total_inner_border_spacing) .ClampNegativeToZero(); } colspan_cell_max_inline_size = (colspan_cell.cell_inline_constraint.max_inline_size - - (colspan_cell.span - 1) * inline_border_spacing) + total_inner_border_spacing) .ClampNegativeToZero(); // Distribute min/max/percentage evenly between all cells. @@ -467,18 +498,19 @@ void DistributeColspanCellToColumnsFixed( colspan_cell.cell_inline_constraint.percent.value_or(0.0f); LayoutUnit new_min_size = LayoutUnit(colspan_cell_min_inline_size / - static_cast<float>(colspan_cell.span)); + static_cast<float>(effective_span)); LayoutUnit new_max_size = LayoutUnit(colspan_cell_max_inline_size / - static_cast<float>(colspan_cell.span)); + static_cast<float>(effective_span)); base::Optional<float> new_percent; if (colspan_cell.cell_inline_constraint.percent) { - new_percent = - *colspan_cell.cell_inline_constraint.percent / colspan_cell.span; + new_percent = *colspan_cell.cell_inline_constraint.percent / effective_span; } NGTableTypes::Column* last_column; for (NGTableTypes::Column* column = start_column; column < end_column; ++column) { + if (column->is_mergeable) + continue; last_column = column; rounding_error_min_inline_size -= new_min_size; rounding_error_max_inline_size -= new_max_size; @@ -520,13 +552,25 @@ void DistributeColspanCellToColumnsAuto( NGTableTypes::Column* end_column = start_column + effective_span; // Inline sizes for redistribution exclude border spacing. + LayoutUnit total_inner_border_spacing; + bool is_first_column = true; + for (NGTableTypes::Column* column = start_column; column != end_column; + ++column) { + if (!column->is_mergeable) { + if (!is_first_column) + total_inner_border_spacing += inline_border_spacing; + else + is_first_column = false; + } + } + LayoutUnit colspan_cell_min_inline_size = (colspan_cell.cell_inline_constraint.min_inline_size - - (effective_span - 1) * inline_border_spacing) + total_inner_border_spacing) .ClampNegativeToZero(); LayoutUnit colspan_cell_max_inline_size = (colspan_cell.cell_inline_constraint.max_inline_size - - (effective_span - 1) * inline_border_spacing) + total_inner_border_spacing) .ClampNegativeToZero(); base::Optional<float> colspan_cell_percent = colspan_cell.cell_inline_constraint.percent; @@ -543,6 +587,8 @@ void DistributeColspanCellToColumnsAuto( column->max_inline_size = LayoutUnit(); if (!column->min_inline_size) column->min_inline_size = LayoutUnit(); + if (column->is_mergeable) + continue; all_columns_count++; if (column->percent) { percent_columns_count++; @@ -558,7 +604,7 @@ void DistributeColspanCellToColumnsAuto( // max_inline_size. for (NGTableTypes::Column* column = start_column; column != end_column; ++column) { - if (column->percent) + if (column->percent || column->is_mergeable) continue; float column_percent; if (nonpercent_columns_max_inline_size != LayoutUnit()) { @@ -608,6 +654,10 @@ void DistributeColspanCellToColumnsAuto( } } +// Handles distribution of excess block size from: table, sections, +// rows, and rowspanned cells, to rows. +// Rowspanned cells distribute with slight differences from +// general distribution algorithm. void DistributeExcessBlockSizeToRows( const wtf_size_t start_row_index, const wtf_size_t row_count, @@ -627,7 +677,7 @@ void DistributeExcessBlockSizeToRows( std::next(rows->begin(), start_row_index + row_count); auto RowBlockSizeDeficit = [&percentage_resolution_block_size]( - NGTableTypes::Row* row) { + const NGTableTypes::Row* row) { if (percentage_resolution_block_size == kIndefiniteSize) return LayoutUnit(); DCHECK(row->percent); @@ -637,7 +687,7 @@ void DistributeExcessBlockSizeToRows( }; auto IsUnconstrainedNonEmptyRow = - [&percentage_resolution_block_size](NGTableTypes::Row* row) { + [&percentage_resolution_block_size](const NGTableTypes::Row* row) { if (row->block_size == LayoutUnit()) return false; if (row->percent && percentage_resolution_block_size == kIndefiniteSize) @@ -646,17 +696,24 @@ void DistributeExcessBlockSizeToRows( }; auto IsRowWithOriginatingRowspan = - [&start_row, &desired_block_size_is_rowspan](NGTableTypes::Row* row) { + [&start_row, + &desired_block_size_is_rowspan](const NGTableTypes::Row* row) { // Rowspans are treated specially only during rowspan distribution. return desired_block_size_is_rowspan && row != start_row && row->has_rowspan_start; }; + auto IsEmptyRow = [](const NGTableTypes::Row* row) { + return row->block_size == LayoutUnit() && + (!row->percent || *row->percent == 0); + }; + unsigned percent_rows_with_deficit_count = 0; unsigned rows_with_originating_rowspan = 0; unsigned unconstrained_non_empty_row_count = 0; unsigned constrained_non_empty_row_count = 0; unsigned empty_row_count = 0; + unsigned unconstrained_empty_row_count = 0; LayoutUnit total_block_size; LayoutUnit percentage_block_size_deficit; @@ -677,11 +734,14 @@ void DistributeExcessBlockSizeToRows( if (IsUnconstrainedNonEmptyRow(row)) { unconstrained_non_empty_row_count++; unconstrained_non_empty_row_block_size += row->block_size; - } else if (row->is_constrained && row->block_size != LayoutUnit()) { + } else if (row->is_constrained && !IsEmptyRow(row)) { constrained_non_empty_row_count++; } - if (row->block_size == LayoutUnit()) + if (IsEmptyRow(row)) { empty_row_count++; + if (!row->is_constrained) + unconstrained_empty_row_count++; + } } LayoutUnit distributable_block_size = @@ -753,14 +813,14 @@ void DistributeExcessBlockSizeToRows( } // Step 4: Empty row distribution - // Table distributes evenly between all rows. - // If there are any empty rows except start row, last row takes all the - // excess block size. + // At this point all rows are empty and/or constrained. if (empty_row_count > 0) { if (desired_block_size_is_rowspan) { NGTableTypes::Row* last_row = nullptr; NGTableTypes::Row* row = start_row; - if (empty_row_count != row_count) // skip initial row. + // Rowspan distribution skips initial empty row if possible, + // and distributes everything to the last empty row. + if (empty_row_count != row_count) ++row; for (; row != end_row; ++row) { if (row->block_size != LayoutUnit()) @@ -774,23 +834,29 @@ void DistributeExcessBlockSizeToRows( } else if (empty_row_count == row_count || (empty_row_count + constrained_non_empty_row_count == row_count)) { - // Grow empty rows if one of these is true: + // Grow empty rows if either of these is true: // - all rows are empty. // - non-empty rows are all constrained. - // Different browsers disagree on when to grow empty rows. - NGTableTypes::Row* last_row; + NGTableTypes::Row* last_row = nullptr; LayoutUnit remaining_deficit = distributable_block_size; + // If there are constrained and unconstrained empty rows, + // only unconstrained rows grow. + bool grow_only_unconstrained = unconstrained_empty_row_count > 0; + unsigned growing_rows_count = grow_only_unconstrained + ? unconstrained_empty_row_count + : empty_row_count; for (NGTableTypes::Row* row = start_row; row != end_row; ++row) { if (row->block_size != LayoutUnit()) continue; + if (grow_only_unconstrained && row->is_constrained) + continue; last_row = row; - // Table block size distributes equally, while rowspan distributes to - // last row. LayoutUnit delta = - LayoutUnit(distributable_block_size.ToFloat() / empty_row_count); + LayoutUnit(distributable_block_size.ToFloat() / growing_rows_count); row->block_size = delta; remaining_deficit -= delta; } + DCHECK(last_row); last_row->block_size += remaining_deficit; return; } @@ -816,12 +882,13 @@ void DistributeExcessBlockSizeToRows( } // namespace MinMaxSizes NGTableAlgorithmHelpers::ComputeGridInlineMinMax( + const NGTableNode& node, const NGTableTypes::Columns& column_constraints, LayoutUnit undistributable_space, bool is_fixed_layout, - bool containing_block_expects_minmax_without_percentages, + bool is_layout_pass, bool skip_collapsed_columns) { - MinMaxSizes minmax; + MinMaxSizes min_max; // https://www.w3.org/TR/css-tables-3/#computing-the-table-width // Compute standard GRID_MIN/GRID_MAX. They are sum of column_constraints. // @@ -840,9 +907,9 @@ MinMaxSizes NGTableAlgorithmHelpers::ComputeGridInlineMinMax( // T% * MINSUM + M = MINSUM. // Minimum total size estimate based on column's min_inline_size and percent. - LayoutUnit percent_maxsize_estimate; + LayoutUnit percent_max_size_estimate; // Sum of max_inline_sizes of non-percentage columns. - LayoutUnit non_percent_maxsize_sum; + LayoutUnit non_percent_max_size_sum; float percent_sum = 0; for (const NGTableTypes::Column& column : column_constraints.data) { if (skip_collapsed_columns && column.is_collapsed) @@ -851,50 +918,49 @@ MinMaxSizes NGTableAlgorithmHelpers::ComputeGridInlineMinMax( // In fixed layout, constrained cells minimum inline size is their // maximum. if (is_fixed_layout && column.IsFixed()) { - minmax.min_size += *column.max_inline_size; + min_max.min_size += *column.max_inline_size; } else { - minmax.min_size += *column.min_inline_size; + min_max.min_size += *column.min_inline_size; } - if (column.percent) { - if (*column.max_inline_size > LayoutUnit() && *column.percent > 0) { + if (column.percent && *column.percent > 0) { + if (*column.max_inline_size > LayoutUnit()) { LayoutUnit estimate = LayoutUnit( 100 / *column.percent * (*column.max_inline_size - column.percent_border_padding)); - percent_maxsize_estimate = - std::max(percent_maxsize_estimate, estimate); + percent_max_size_estimate = + std::max(percent_max_size_estimate, estimate); } } else { - non_percent_maxsize_sum += *column.max_inline_size; + non_percent_max_size_sum += *column.max_inline_size; } } if (column.max_inline_size) - minmax.max_size += *column.max_inline_size; + min_max.max_size += *column.max_inline_size; if (column.percent) percent_sum += *column.percent; } DCHECK_LE(percent_sum, 100.0f); - // Table max inline size constraint can be computed from: - // total column percentage combined with max_inline_size of nonpercent - // columns. - if (percent_sum > 0 && !containing_block_expects_minmax_without_percentages) { + // Table max inline size constraint can be computed from the total column + // percentage combined with max_inline_size of non-percent columns. + if (percent_sum > 0 && node.AllowColumnPercentages(is_layout_pass)) { LayoutUnit size_from_percent_and_fixed; DCHECK_GE(percent_sum, 0.0f); - if (non_percent_maxsize_sum != LayoutUnit()) { + if (non_percent_max_size_sum != LayoutUnit()) { if (percent_sum == 100.0f) { size_from_percent_and_fixed = NGTableTypes::kTableMaxInlineSize; } else { size_from_percent_and_fixed = - LayoutUnit((100 / (100 - percent_sum)) * non_percent_maxsize_sum); + LayoutUnit((100 / (100 - percent_sum)) * non_percent_max_size_sum); } } - minmax.max_size = std::max(minmax.max_size, size_from_percent_and_fixed); - minmax.max_size = std::max(minmax.max_size, percent_maxsize_estimate); + min_max.max_size = std::max(min_max.max_size, size_from_percent_and_fixed); + min_max.max_size = std::max(min_max.max_size, percent_max_size_estimate); } - minmax.max_size = std::max(minmax.min_size, minmax.max_size); - minmax += undistributable_space; - return minmax; + min_max.max_size = std::max(min_max.min_size, min_max.max_size); + min_max += undistributable_space; + return min_max; } void NGTableAlgorithmHelpers::DistributeColspanCellsToColumns( @@ -1101,7 +1167,7 @@ void NGTableAlgorithmHelpers::DistributeTableBlockSizeToSections( group_index = 0; LayoutUnit remaining_deficit = excess_block_size; - NGTableTypes::Section* last_section; + NGTableTypes::Section* last_section = nullptr; for (NGTableTypes::Section& section : *sections) { if (group_index == 2 && !is_group_2(section)) continue; diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h index cf41e4a5f61..1213933e849 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h @@ -10,6 +10,8 @@ namespace blink { +class NGTableNode; + // Table size distribution algorithms. class CORE_EXPORT NGTableAlgorithmHelpers { public: @@ -24,17 +26,14 @@ class CORE_EXPORT NGTableAlgorithmHelpers { return current_column + 1; } - // Flex/grid containing blocks need Table minmax size to be computed without - // using percentages. - // |containing_block_expects_minmax_without_percentages| is used to do - // this. // |undistributable_space| is size of space not occupied by cells // (borders, border spacing). static MinMaxSizes ComputeGridInlineMinMax( + const NGTableNode& node, const NGTableTypes::Columns& column_constraints, LayoutUnit undistributable_space, bool is_fixed_layout, - bool containing_block_expects_minmax_without_percentages, + bool is_layout_pass, bool skip_collapsed_columns); static void DistributeColspanCellsToColumns( diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc index 117d5a2ad3a..cc9c95a178b 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc @@ -5,12 +5,12 @@ #include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_node.h" +#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" namespace blink { -class NGTableAlgorithmHelpersTest : public testing::Test { - void SetUp() override {} - +class NGTableAlgorithmHelpersTest : public RenderingTest { public: NGTableTypes::Column MakeColumn(int min_width, int max_width, @@ -21,7 +21,9 @@ class NGTableAlgorithmHelpersTest : public testing::Test { percent, /* border_padding */ LayoutUnit(), is_constrained, - /* is_collapsed */ false}; + /* is_collapsed */ false, + /* is_table_fixed */ false, + /* is_mergeable */ false}; } NGTableTypes::Row MakeRow(int block_size, @@ -155,16 +157,16 @@ TEST_F(NGTableAlgorithmHelpersTest, DistributeColspanAutoExactMaxSize) { column_constraints->data.Shrink(0); column_constraints->data.push_back( NGTableTypes::Column{LayoutUnit(0), column_widths[0], base::nullopt, - LayoutUnit(), false, false}); + LayoutUnit(), false, false, false, false}); column_constraints->data.push_back( NGTableTypes::Column{LayoutUnit(3.33333), column_widths[1], base::nullopt, - LayoutUnit(), false, false}); + LayoutUnit(), false, false, false, false}); column_constraints->data.push_back( NGTableTypes::Column{LayoutUnit(3.33333), column_widths[2], base::nullopt, - LayoutUnit(), false, false}); + LayoutUnit(), false, false, false, false}); column_constraints->data.push_back( NGTableTypes::Column{LayoutUnit(0), column_widths[3], base::nullopt, - LayoutUnit(), false, false}); + LayoutUnit(), false, false, false, false}); LayoutUnit assignable_table_inline_size = column_widths[0] + column_widths[1] + column_widths[2] + column_widths[3]; @@ -178,13 +180,20 @@ TEST_F(NGTableAlgorithmHelpersTest, DistributeColspanAutoExactMaxSize) { EXPECT_EQ(column_sizes[3], column_widths[3]); } -TEST_F(NGTableAlgorithmHelpersTest, ComputeGridInlineMinmax) { +TEST_F(NGTableAlgorithmHelpersTest, ComputeGridInlineMinMax) { + SetBodyInnerHTML(R"HTML( + <div style="display: flex;"> + <table id=target></table> + <div> + )HTML"); + NGTableNode node(To<LayoutBox>(GetLayoutObjectByElementId("target"))); + scoped_refptr<NGTableTypes::Columns> column_constraints = base::MakeRefCounted<NGTableTypes::Columns>(); LayoutUnit undistributable_space; bool is_fixed_layout = false; - bool containing_block_expects_minmax_without_percentages = false; + bool is_layout_pass = true; bool skip_collapsed_columns = false; // No percentages, just sums up min/max. @@ -192,10 +201,9 @@ TEST_F(NGTableAlgorithmHelpersTest, ComputeGridInlineMinmax) { column_constraints->data.push_back(MakeColumn(20, 200)); column_constraints->data.push_back(MakeColumn(30, 300)); - MinMaxSizes minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinmax( - *column_constraints, undistributable_space, is_fixed_layout, - containing_block_expects_minmax_without_percentages, - skip_collapsed_columns); + MinMaxSizes minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax( + node, *column_constraints, undistributable_space, is_fixed_layout, + is_layout_pass, skip_collapsed_columns); EXPECT_EQ(minmax.min_size, LayoutUnit(60)); EXPECT_EQ(minmax.max_size, LayoutUnit(600)); @@ -205,33 +213,29 @@ TEST_F(NGTableAlgorithmHelpersTest, ComputeGridInlineMinmax) { column_constraints->data.push_back(MakeColumn(10, 99, 10)); column_constraints->data.push_back(MakeColumn(10, 10)); column_constraints->data.push_back(MakeColumn(10, 10)); - minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinmax( - *column_constraints, undistributable_space, is_fixed_layout, - containing_block_expects_minmax_without_percentages, - skip_collapsed_columns); + minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax( + node, *column_constraints, undistributable_space, is_fixed_layout, + is_layout_pass, skip_collapsed_columns); EXPECT_EQ(minmax.min_size, LayoutUnit(30)); EXPECT_EQ(minmax.max_size, LayoutUnit(990)); - // Without percent, minmax ignores percent - containing_block_expects_minmax_without_percentages = true; - minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinmax( - *column_constraints, undistributable_space, is_fixed_layout, - containing_block_expects_minmax_without_percentages, - skip_collapsed_columns); + is_layout_pass = false; + minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax( + node, *column_constraints, undistributable_space, is_fixed_layout, + is_layout_pass, skip_collapsed_columns); EXPECT_EQ(minmax.min_size, LayoutUnit(30)); EXPECT_EQ(minmax.max_size, LayoutUnit(119)); // Percentage: total percentage of 20%, and non-percent width of 800 => // table max size of 800 + (20% * 800/80%) = 1000 - containing_block_expects_minmax_without_percentages = false; + is_layout_pass = true; column_constraints->data.Shrink(0); column_constraints->data.push_back(MakeColumn(10, 100, 10)); column_constraints->data.push_back(MakeColumn(10, 10, 10)); column_constraints->data.push_back(MakeColumn(10, 800)); - minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinmax( - *column_constraints, undistributable_space, is_fixed_layout, - containing_block_expects_minmax_without_percentages, - skip_collapsed_columns); + minmax = NGTableAlgorithmHelpers::ComputeGridInlineMinMax( + node, *column_constraints, undistributable_space, is_fixed_layout, + is_layout_pass, skip_collapsed_columns); EXPECT_EQ(minmax.min_size, LayoutUnit(30)); EXPECT_EQ(minmax.max_size, LayoutUnit(1000)); } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc index aea92d5ccbf..3c80c8775fa 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h" #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h" #include "third_party/blink/renderer/core/style/computed_style.h" +#include "third_party/blink/renderer/platform/geometry/calculation_value.h" namespace blink { @@ -51,8 +52,15 @@ inline void InlineSizesFromStyle( if (*min_inline_size) *max_inline_size = std::max(**min_inline_size, **max_inline_size); } - if (length.IsPercent()) + if (length.IsPercent()) { *percentage_inline_size = length.Percent(); + } else if (length.IsCalculated() && + !length.GetCalculationValue().IsExpression()) { + // crbug.com/1154376 Style engine should handle %+0px case automatically. + PixelsAndPercent pixels_and_percent = length.GetPixelsAndPercent(); + if (pixels_and_percent.pixels == 0.0f) + *percentage_inline_size = pixels_and_percent.percent; + } if (*percentage_inline_size && max_length.IsPercent()) { *percentage_inline_size = @@ -70,7 +78,8 @@ constexpr LayoutUnit NGTableTypes::kTableMaxInlineSize; // "outer min-content and outer max-content widths for colgroups" NGTableTypes::Column NGTableTypes::CreateColumn( const ComputedStyle& style, - base::Optional<LayoutUnit> default_inline_size) { + base::Optional<LayoutUnit> default_inline_size, + bool is_table_fixed) { base::Optional<LayoutUnit> inline_size; base::Optional<LayoutUnit> min_inline_size; base::Optional<LayoutUnit> max_inline_size; @@ -78,6 +87,7 @@ NGTableTypes::Column NGTableTypes::CreateColumn( InlineSizesFromStyle(style, /* inline_border_padding */ LayoutUnit(), /* is_parallel */ true, &inline_size, &min_inline_size, &max_inline_size, &percentage_inline_size); + bool is_mergeable; if (!inline_size) inline_size = default_inline_size; if (min_inline_size && inline_size) @@ -86,12 +96,16 @@ NGTableTypes::Column NGTableTypes::CreateColumn( if (percentage_inline_size && *percentage_inline_size == 0.0f) percentage_inline_size.reset(); bool is_collapsed = style.Visibility() == EVisibility::kCollapse; - return Column{min_inline_size.value_or(LayoutUnit()), - inline_size, + if (is_table_fixed) { + is_mergeable = false; + } else { + is_mergeable = (inline_size.value_or(LayoutUnit()) == LayoutUnit()) && + (percentage_inline_size.value_or(0.0f) == 0.0f); + } + return Column(min_inline_size.value_or(LayoutUnit()), inline_size, percentage_inline_size, - LayoutUnit() /* percent_border_padding */, - is_constrained, - is_collapsed}; + LayoutUnit() /* percent_border_padding */, is_constrained, + is_collapsed, is_table_fixed, is_mergeable); } // Implements https://www.w3.org/TR/css-tables-3/#computing-cell-measures @@ -134,8 +148,7 @@ NGTableTypes::CellInlineConstraint NGTableTypes::CreateCellInlineConstraint( builder.SetOrthogonalFallbackInlineSize( IsHorizontalWritingMode(table_writing_mode) ? icb_size.height : icb_size.width); - - builder.SetIsShrinkToFit(style.LogicalWidth().IsAuto()); + builder.SetAvailableSize({kIndefiniteSize, kIndefiniteSize}); } NGConstraintSpace space = builder.ToConstraintSpace(); // It'd be nice to avoid computing minmax if not needed, but the criteria @@ -199,7 +212,8 @@ NGTableTypes::Section NGTableTypes::CreateSection( const NGLayoutInputNode& section, wtf_size_t start_row, wtf_size_t rows, - LayoutUnit block_size) { + LayoutUnit block_size, + bool treat_as_tbody) { const Length& section_css_block_size = section.Style().LogicalHeight(); // TODO(crbug.com/1105272): Decide what to do with |Length::IsCalculated()|. bool is_constrained = @@ -207,14 +221,12 @@ NGTableTypes::Section NGTableTypes::CreateSection( base::Optional<float> percent; if (section_css_block_size.IsPercent()) percent = section_css_block_size.Percent(); - bool is_tbody = - section.GetDOMNode()->HasTagName(html_names::kTbodyTag); return Section{start_row, rows, block_size, percent, is_constrained, - is_tbody, + treat_as_tbody, /* needs_redistribution */ false}; } @@ -267,8 +279,6 @@ void NGTableTypes::CellInlineConstraint::Encompass( max_inline_size = std::max(min_inline_size, other.max_inline_size); } is_constrained = is_constrained || other.is_constrained; - max_inline_size = std::max(max_inline_size, other.max_inline_size); - percent = std::max(percent, other.percent); if (other.percent > percent) { percent = other.percent; percent_border_padding = other.percent_border_padding; @@ -280,6 +290,11 @@ void NGTableTypes::Column::Encompass( if (!cell) return; + // Constrained columns in fixed tables take precedence over cells. + if (is_constrained && is_table_fixed) + return; + if (!is_table_fixed) + is_mergeable = false; if (min_inline_size) { if (min_inline_size < cell->min_inline_size) { min_inline_size = cell->min_inline_size; @@ -308,7 +323,8 @@ void NGTableTypes::Column::Encompass( is_constrained |= cell->is_constrained; } -NGTableGroupedChildren::NGTableGroupedChildren(const NGBlockNode& table) { +NGTableGroupedChildren::NGTableGroupedChildren(const NGBlockNode& table) + : header(NGBlockNode(nullptr)), footer(NGBlockNode(nullptr)) { for (NGLayoutInputNode child = table.FirstChild(); child; child = child.NextSibling()) { NGBlockNode block_child = To<NGBlockNode>(child); @@ -321,13 +337,19 @@ NGTableGroupedChildren::NGTableGroupedChildren(const NGBlockNode& table) { columns.push_back(block_child); break; case EDisplay::kTableHeaderGroup: - headers.push_back(block_child); + if (!header) + header = block_child; + else + bodies.push_back(block_child); break; case EDisplay::kTableRowGroup: bodies.push_back(block_child); break; case EDisplay::kTableFooterGroup: - footers.push_back(block_child); + if (!footer) + footer = block_child; + else + bodies.push_back(block_child); break; default: NOTREACHED() << "unexpected table child"; @@ -347,30 +369,57 @@ NGTableGroupedChildrenIterator NGTableGroupedChildren::end() const { NGTableGroupedChildrenIterator::NGTableGroupedChildrenIterator( const NGTableGroupedChildren& grouped_children, bool is_end) - : grouped_children_(grouped_children), current_vector_(nullptr) { + : grouped_children_(grouped_children) { if (is_end) { - current_vector_ = &grouped_children_.footers; - current_iterator_ = current_vector_->end(); + current_section_ = kEnd; return; } + current_section_ = kNone; AdvanceToNonEmptySection(); } NGTableGroupedChildrenIterator& NGTableGroupedChildrenIterator::operator++() { - ++current_iterator_; - if (current_iterator_ == current_vector_->end()) - AdvanceToNonEmptySection(); + switch (current_section_) { + case kHead: + case kFoot: + AdvanceToNonEmptySection(); + break; + case kBody: + ++body_iterator_; + if (body_iterator_ == grouped_children_.bodies.end()) + AdvanceToNonEmptySection(); + break; + case kEnd: + break; + case kNone: + NOTREACHED(); + break; + } return *this; } NGBlockNode NGTableGroupedChildrenIterator::operator*() const { - return *current_iterator_; + switch (current_section_) { + case kHead: + return grouped_children_.header; + case kFoot: + return grouped_children_.footer; + case kBody: + return *body_iterator_; + case kEnd: + case kNone: + NOTREACHED(); + return NGBlockNode(nullptr); + } } bool NGTableGroupedChildrenIterator::operator==( const NGTableGroupedChildrenIterator& rhs) const { - return rhs.current_vector_ == current_vector_ && - rhs.current_iterator_ == current_iterator_; + if (current_section_ != rhs.current_section_) + return false; + if (current_section_ == kBody) + return rhs.body_iterator_ == body_iterator_; + return true; } bool NGTableGroupedChildrenIterator::operator!=( @@ -379,19 +428,29 @@ bool NGTableGroupedChildrenIterator::operator!=( } void NGTableGroupedChildrenIterator::AdvanceToNonEmptySection() { - if (current_vector_ == &grouped_children_.footers) - return; - if (!current_vector_) { - current_vector_ = &grouped_children_.headers; - } else if (current_vector_ == &grouped_children_.headers) { - current_vector_ = &grouped_children_.bodies; - } else if (current_vector_ == &grouped_children_.bodies) { - current_vector_ = &grouped_children_.footers; - } - current_iterator_ = current_vector_->begin(); - // If new group is empty, recursively advance. - if (current_iterator_ == current_vector_->end()) { - AdvanceToNonEmptySection(); + switch (current_section_) { + case kNone: + current_section_ = kHead; + if (!grouped_children_.header) + AdvanceToNonEmptySection(); + break; + case kHead: + current_section_ = kBody; + body_iterator_ = grouped_children_.bodies.begin(); + if (body_iterator_ == grouped_children_.bodies.end()) + AdvanceToNonEmptySection(); + break; + case kBody: + current_section_ = kFoot; + if (!grouped_children_.footer) + AdvanceToNonEmptySection(); + break; + case kFoot: + current_section_ = kEnd; + break; + case kEnd: + NOTREACHED(); + break; } } diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h index e63d160801d..b892c3f2014 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h @@ -9,6 +9,7 @@ #include "base/optional.h" #include "third_party/blink/renderer/core/layout/min_max_sizes.h" #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" +#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" #include "third_party/blink/renderer/platform/geometry/length.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -56,6 +57,23 @@ class CORE_EXPORT NGTableTypes { // Constraint for a column. struct Column { DISALLOW_NEW(); + Column(const base::Optional<LayoutUnit>& min_inline_size, + const base::Optional<LayoutUnit>& max_inline_size, + const base::Optional<float>& percent, + LayoutUnit percent_border_padding, + bool is_constrained, + bool is_collapsed, + bool is_table_fixed, + bool is_mergeable) + : min_inline_size(min_inline_size), + max_inline_size(max_inline_size), + percent(percent), + percent_border_padding(percent_border_padding), + is_constrained(is_constrained), + is_collapsed(is_collapsed), + is_table_fixed(is_table_fixed), + is_mergeable(is_mergeable) {} + Column() = default; // These members are initialized from <col> and <colgroup>, then they // accumulate data from |CellInlineConstraint|s. base::Optional<LayoutUnit> min_inline_size; @@ -66,6 +84,8 @@ class CORE_EXPORT NGTableTypes { // True if any cell for this column is constrained. bool is_constrained = false; bool is_collapsed = false; + bool is_table_fixed = false; + bool is_mergeable = false; void Encompass(const base::Optional<NGTableTypes::CellInlineConstraint>&); LayoutUnit ResolvePercentInlineSize( @@ -180,7 +200,8 @@ class CORE_EXPORT NGTableTypes { }; static Column CreateColumn(const ComputedStyle&, - base::Optional<LayoutUnit> default_inline_size); + base::Optional<LayoutUnit> default_inline_size, + bool is_table_fixed); static CellInlineConstraint CreateCellInlineConstraint( const NGBlockNode&, @@ -193,7 +214,8 @@ class CORE_EXPORT NGTableTypes { static Section CreateSection(const NGLayoutInputNode&, wtf_size_t start_row, wtf_size_t rowspan, - LayoutUnit block_size); + LayoutUnit block_size, + bool treat_as_tbody); static CellBlockConstraint CreateCellBlockConstraint( const NGLayoutInputNode&, @@ -237,9 +259,9 @@ struct NGTableGroupedChildren { Vector<NGBlockNode> captions; // CAPTION Vector<NGBlockNode> columns; // COLGROUP, COL - Vector<NGBlockNode> headers; // THEAD - Vector<NGBlockNode> bodies; // TBODY - Vector<NGBlockNode> footers; // TFOOT + NGBlockNode header; // first THEAD + Vector<NGBlockNode> bodies; // TBODY/multiple THEAD/TFOOT + NGBlockNode footer; // first TFOOT // Default iterators iterate over tbody-like (THEAD/TBODY/TFOOT) elements. NGTableGroupedChildrenIterator begin() const; @@ -249,6 +271,8 @@ struct NGTableGroupedChildren { // Iterates table's sections in order: // thead, tbody, tfoot class NGTableGroupedChildrenIterator { + enum CurrentSection { kNone, kHead, kBody, kFoot, kEnd }; + public: explicit NGTableGroupedChildrenIterator( const NGTableGroupedChildren& grouped_children, @@ -258,12 +282,14 @@ class NGTableGroupedChildrenIterator { NGBlockNode operator*() const; bool operator==(const NGTableGroupedChildrenIterator& rhs) const; bool operator!=(const NGTableGroupedChildrenIterator& rhs) const; + // True if section should be treated as tbody + bool TreatAsTBody() const { return current_section_ == kBody; } private: void AdvanceToNonEmptySection(); const NGTableGroupedChildren& grouped_children_; - const Vector<NGBlockNode>* current_vector_; - Vector<NGBlockNode>::const_iterator current_iterator_; + Vector<NGBlockNode>::const_iterator body_iterator_; + CurrentSection current_section_{kNone}; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc index ddf04a7f5aa..22a29c1b457 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc @@ -24,6 +24,55 @@ namespace blink { namespace { +// Mergeable columns cannot be distributed to. +// Make at least one spanned column is distributable. +void EnsureDistributableColumnExists( + wtf_size_t start_column_index, + wtf_size_t span, + NGTableTypes::Columns* column_constraints) { + if (span == 0) + return; + DCHECK_LT(start_column_index, column_constraints->data.size()); + wtf_size_t effective_span = + std::min(span, column_constraints->data.size() - start_column_index); + if (effective_span == 0) + return; + NGTableTypes::Column* start_column = + &column_constraints->data[start_column_index]; + NGTableTypes::Column* end_column = start_column + effective_span; + + NGTableTypes::Column* first_mergeable_column = nullptr; + for (NGTableTypes::Column* column = start_column; column != end_column; + ++column) { + if (!column->is_collapsed) { + if (!column->is_mergeable) { + // Found non-collapsed, non mergeable column, nothing to do. + return; + } else if (!first_mergeable_column) { + // Found first non-collapsed, mergeable column. + first_mergeable_column = column; + } + } + } + // The interesting problem being solved here is interaction between + // collapsed and mergeable columns. + // All columns that are created by colspanned cell are mergeable by + // default. Without collapsing, the first column would always be + // marked as !mergeable. + // What to do if the first column collapses? If that was the only + // non-mergeable column, the entire cell would merge into first column, + // and collapse. + // To prevent "whole cell hidden if 1st cell is collapsed", + // we try to make first non-collapsed column mergeable. + // If all columns collapse, first cell is marked as meargable. + if (first_mergeable_column) { + // Some columns were not collapsed, mark first as mergeable. + first_mergeable_column->is_mergeable = false; + } else { + start_column->is_mergeable = false; + } +} + // Applies cell/wide cell constraints to columns. // Guarantees columns min/max widths have non-empty values. void ApplyCellConstraintsToColumnConstraints( @@ -32,7 +81,37 @@ void ApplyCellConstraintsToColumnConstraints( bool is_fixed_layout, NGTableTypes::ColspanCells* colspan_cell_constraints, NGTableTypes::Columns* column_constraints) { - column_constraints->data.resize(cell_constraints.size()); + // Satisfy prerequisites for cell merging: + + if (column_constraints->data.size() < cell_constraints.size()) { + // Column constraint must exist for each cell. + NGTableTypes::Column default_column; + default_column.is_table_fixed = is_fixed_layout; + default_column.is_mergeable = !is_fixed_layout; + wtf_size_t column_count = + cell_constraints.size() - column_constraints->data.size(); + // Must loop because WTF::Vector does not support resize with default value. + for (wtf_size_t i = 0; i < column_count; ++i) + column_constraints->data.push_back(default_column); + DCHECK_EQ(column_constraints->data.size(), cell_constraints.size()); + + } else if (column_constraints->data.size() > cell_constraints.size()) { + // Trim mergeable columns off the end. + wtf_size_t last_non_merged_column = column_constraints->data.size() - 1; + while (last_non_merged_column + 1 > cell_constraints.size() && + column_constraints->data[last_non_merged_column].is_mergeable) { + --last_non_merged_column; + } + column_constraints->data.resize(last_non_merged_column + 1); + DCHECK_GE(column_constraints->data.size(), cell_constraints.size()); + } + // Make sure there exists a non-mergeable column for each colspanned cell. + for (const NGTableTypes::ColspanCell& colspan_cell : + *colspan_cell_constraints) { + EnsureDistributableColumnExists(colspan_cell.start_column, + colspan_cell.span, column_constraints); + } + // Distribute cell constraints to column constraints. for (wtf_size_t i = 0; i < cell_constraints.size(); ++i) { column_constraints->data[i].Encompass(cell_constraints[i]); @@ -52,14 +131,16 @@ void ApplyCellConstraintsToColumnConstraints( *colspan_cell_constraints, inline_border_spacing, is_fixed_layout, column_constraints); - // Clamp column percentages. Standard: "100% minus the sum of the intrinsic - // percentage width of all prior columns in the table." + // Column total percentage inline-size is clamped to 100%. + // Auto tables: max(0, 100% minus the sum of percentages of all + // prior columns in the table) + // Fixed tables: scale all percentage columns so that total percentage + // is 100%. float total_percentage = 0; for (NGTableTypes::Column& column : column_constraints->data) { if (column.percent) { - if (*column.percent + total_percentage > 100.0) { + if (!is_fixed_layout && (*column.percent + total_percentage > 100.0)) column.percent = 100 - total_percentage; - } total_percentage += *column.percent; } // A column may have no min/max inline-sizes if there are no cells in this @@ -67,6 +148,13 @@ void ApplyCellConstraintsToColumnConstraints( column.min_inline_size = column.min_inline_size.value_or(LayoutUnit()); column.max_inline_size = column.max_inline_size.value_or(LayoutUnit()); } + + if (is_fixed_layout && total_percentage > 100.0) { + for (NGTableTypes::Column& column : column_constraints->data) { + if (column.percent) + column.percent = *column.percent * 100 / total_percentage; + } + } } NGTableTypes::Row ComputeMinimumRowBlockSize( @@ -93,6 +181,7 @@ NGTableTypes::Row ComputeMinimumRowBlockSize( wtf_size_t start_column_index, const NGBoxStrut& cell_borders) { const wtf_size_t start_column = start_column_index; + DCHECK_LT(start_column, column_locations.size()); const wtf_size_t end_column = std::min(start_column + cell.TableCellColspan() - 1, column_locations.size() - 1); @@ -243,10 +332,12 @@ class ColumnConstraintsBuilder { wtf_size_t span) { // COL creates SPAN constraints. Its width is col css width, or enclosing // colgroup css width. - NGTableTypes::Column col_constraint = NGTableTypes::CreateColumn( - column.Style(), !is_fixed_layout_ && colgroup_constraint_ - ? colgroup_constraint_->max_inline_size - : base::nullopt); + NGTableTypes::Column col_constraint = + NGTableTypes::CreateColumn(column.Style(), + !is_fixed_layout_ && colgroup_constraint_ + ? colgroup_constraint_->max_inline_size + : base::nullopt, + is_fixed_layout_); for (wtf_size_t i = 0; i < span; ++i) column_constraints_->data.push_back(col_constraint); column.GetLayoutBox()->ClearNeedsLayout(); @@ -254,8 +345,8 @@ class ColumnConstraintsBuilder { void EnterColgroup(const NGLayoutInputNode& colgroup, wtf_size_t start_column_index) { - colgroup_constraint_ = - NGTableTypes::CreateColumn(colgroup.Style(), base::nullopt); + colgroup_constraint_ = NGTableTypes::CreateColumn( + colgroup.Style(), base::nullopt, is_fixed_layout_); } void LeaveColgroup(const NGLayoutInputNode& colgroup, @@ -387,7 +478,6 @@ NGConstraintSpace NGTableAlgorithmUtils::CreateTableCellConstraintSpace( builder.SetOrthogonalFallbackInlineSize( table_writing_direction.IsHorizontal() ? icb_size.height : icb_size.width); - builder.SetIsShrinkToFit(cell.Style().LogicalWidth().IsAuto()); } builder.SetAvailableSize(cell_size); @@ -411,7 +501,6 @@ NGConstraintSpace NGTableAlgorithmUtils::CreateTableCellConstraintSpace( builder.SetIsTableCellWithCollapsedBorders(has_collapsed_borders); builder.SetHideTableCellIfEmpty( !has_collapsed_borders && cell_style.EmptyCells() == EEmptyCells::kHide); - builder.SetNeedsBaseline(true); builder.SetCacheSlot(cache_slot); return builder.ToConstraintSpace(); @@ -464,6 +553,7 @@ void NGTableAlgorithmUtils::ComputeSectionMinimumRowBlockSizes( const NGTableBorders& table_borders, const LayoutUnit block_border_spacing, wtf_size_t section_index, + bool treat_section_as_tbody, NGTableTypes::Sections* sections, NGTableTypes::Rows* rows, NGTableTypes::CellBlockConstraints* cell_block_constraints) { @@ -527,8 +617,9 @@ void NGTableAlgorithmUtils::ComputeSectionMinimumRowBlockSizes( section_block_size = section_fixed_block_size; } } - sections->push_back(NGTableTypes::CreateSection( - section, start_row, current_row - start_row, section_block_size)); + sections->push_back( + NGTableTypes::CreateSection(section, start_row, current_row - start_row, + section_block_size, treat_section_as_tbody)); } void NGColspanCellTabulator::StartRow() { @@ -544,6 +635,11 @@ void NGColspanCellTabulator::EndRow() { else ++i; } + std::sort(colspanned_cells_.begin(), colspanned_cells_.end(), + [](const NGColspanCellTabulator::Cell& a, + const NGColspanCellTabulator::Cell& b) { + return a.column_start < b.column_start; + }); } // Advance current column to position not occupied by colspanned cells. @@ -570,7 +666,8 @@ void NGRowBaselineTabulator::ProcessCell( const bool is_baseline_aligned, const bool is_parallel, const bool descendant_depends_on_percentage_block_size) { - if (is_parallel && is_baseline_aligned) { + if (is_parallel && is_baseline_aligned && + fragment.HasDescendantsForTablePart()) { max_cell_baseline_depends_on_percentage_block_descendant_ |= descendant_depends_on_percentage_block_size; const LayoutUnit cell_baseline = fragment.FirstBaselineOrSynthesize(); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h index e1ea7fde64e..521845a4ab6 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h @@ -61,6 +61,7 @@ class NGTableAlgorithmUtils { const NGTableBorders& table_borders, const LayoutUnit block_border_spacing, wtf_size_t section_index, + bool treat_section_as_tbody, NGTableTypes::Sections* sections, NGTableTypes::Rows* rows, NGTableTypes::CellBlockConstraints* cell_block_constraints); diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc index 18b8ccd5d2b..ed0458c9a52 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc @@ -46,4 +46,38 @@ LayoutUnit NGTableNode::ComputeTableInlineSize( border_padding); } +bool NGTableNode::AllowColumnPercentages(bool is_layout_pass) const { + if (Style().LogicalWidth().IsMaxContent()) + return false; + if (is_layout_pass) + return true; + // TODO(layout-dev): This function breaks the rule of "no tree-walks". + // However for this specific case it adds a lot of overhead for little gain. + // In the future, we could have a bit on a LayoutObject which indicates if we + // should allow column percentages, and maintain this when adding/removing + // from the tree. + const LayoutBlock* block = box_->ContainingBlock(); + while (!block->IsLayoutView()) { + if (block->IsTableCell() || block->IsFlexibleBoxIncludingNG() || + block->IsLayoutGridIncludingNG()) + return false; + + block = block->ContainingBlock(); + } + return true; +} + +// True if table's intrinsic max size can be infinite. +// Infinite intrinsic sizes are ok inside block layout, but cannot work +// inside flex and grid layouts. +bool NGTableNode::AllowsInfiniteMaxInlineSize() const { + const LayoutBlock* block = box_->ContainingBlock(); + while (!block->IsLayoutView()) { + if (block->IsFlexibleBoxIncludingNG() || block->IsLayoutGridIncludingNG()) + return false; + block = block->ContainingBlock(); + } + return true; +} + } // namespace blink diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h index 01a7eff7997..950263cfb67 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h @@ -27,6 +27,19 @@ class CORE_EXPORT NGTableNode final : public NGBlockNode { LayoutUnit ComputeTableInlineSize(const NGConstraintSpace&, const NGBoxStrut& border_padding) const; + + // Tables are special in that their max intrinsic-size can be "infinite" + // (they should consume as much space as possible). However a lot of layout + // modes (flex/grid) don't deal well with "infinite" max intrinsic-size, so + // we disable this behaviour whenever we are an arbitrary descendant of one + // of these layout modes. + // + // TODO(layout-dev): This isn't ideal, as we may have a fixed inline-size + // parent where an "infinite" size would be fine. + bool AllowColumnPercentages(bool is_layout_pass) const; + + // True if table's intrinsic max size can be infinite. + bool AllowsInfiniteMaxInlineSize() const; }; template <> diff --git a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc index f4ac3ec7608..4e294d227ef 100644 --- a/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc +++ b/chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc @@ -50,12 +50,12 @@ scoped_refptr<const NGLayoutResult> NGTableSectionLayoutAlgorithm::Layout() { table_data.table_writing_direction.GetWritingMode(), table_data.table_writing_direction, /* is_new_fc */ true); - row_space_builder.SetAvailableSize( - {container_builder_.InlineSize(), kIndefiniteSize}); + row_space_builder.SetAvailableSize({container_builder_.InlineSize(), + table_data.rows[row_index].block_size}); row_space_builder.SetIsFixedInlineSize(true); + row_space_builder.SetIsFixedBlockSize(true); row_space_builder.SetPercentageResolutionSize( {container_builder_.InlineSize(), kIndefiniteSize}); - row_space_builder.SetNeedsBaseline(true); row_space_builder.SetTableRowData(&table_data, row_index); NGConstraintSpace row_space = row_space_builder.ToConstraintSpace(); scoped_refptr<const NGLayoutResult> row_result = row.Layout(row_space); |