summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/table
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/table')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc99
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h51
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc34
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h6
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.cc18
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column.h4
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.cc21
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_row.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.cc21
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h12
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h93
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h8
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc470
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h19
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc166
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h9
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers_test.cc62
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc139
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h40
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc129
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h1
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.cc34
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_node.h13
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc6
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, &sections,
+ border_spacing, border_padding, captions_block_size,
+ is_fixed_layout, &rows, &cell_block_constraints, &sections,
&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,
- &section_available_size,
- &constraint_space_data](
- wtf_size_t section_index) {
+ &section_available_inline_size,
+ &constraint_space_data,
+ &sections](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);