From 769f11f9ae6c55ee426470e5b791c371000461ff Mon Sep 17 00:00:00 2001 From: Andi Gallo Date: Sat, 5 Aug 2023 05:50:07 +0000 Subject: [PATCH] LibWeb: Implement table missing cells fixup Fixes #19936. --- .../table/missing-cells-with-span.txt | 57 +++++++++++++++++++ .../Layout/expected/table/missing-cells.txt | 53 +++++++++++++++++ .../input/table/missing-cells-with-span.html | 16 ++++++ .../Layout/input/table/missing-cells.html | 16 ++++++ .../LibWeb/Layout/TableFormattingContext.cpp | 20 ------- .../LibWeb/Layout/TableFormattingContext.h | 1 - .../Libraries/LibWeb/Layout/TreeBuilder.cpp | 57 ++++++++++++++++++- .../Libraries/LibWeb/Layout/TreeBuilder.h | 3 +- 8 files changed, 199 insertions(+), 24 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/table/missing-cells-with-span.txt create mode 100644 Tests/LibWeb/Layout/expected/table/missing-cells.txt create mode 100644 Tests/LibWeb/Layout/input/table/missing-cells-with-span.html create mode 100644 Tests/LibWeb/Layout/input/table/missing-cells.html diff --git a/Tests/LibWeb/Layout/expected/table/missing-cells-with-span.txt b/Tests/LibWeb/Layout/expected/table/missing-cells-with-span.txt new file mode 100644 index 0000000000..f70bc120ec --- /dev/null +++ b/Tests/LibWeb/Layout/expected/table/missing-cells-with-span.txt @@ -0,0 +1,57 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x120.40625 children: not-inline + Box
at (8,8) content-size 784x120.40625 [GFC] children: not-inline + TableWrapper <(anonymous)> at (8,8) content-size 100x120.40625 [BFC] children: not-inline + Box at (9,9) content-size 98x118.40625 table-box [TFC] children: not-inline + Box at (9,9) content-size 98x118.40625 table-row-group children: not-inline + Box at (9,9) content-size 98x39.46875 table-row children: not-inline + BlockContainer at (9,48.46875) content-size 98x39.46875 table-row children: not-inline + BlockContainer at (9,87.9375) content-size 98x39.46875 table-row children: not-inline + BlockContainer
at (20,20) content-size 30.5625x17.46875 table-cell [BFC] children: inline + line 0 width: 14.265625, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [20,20 14.265625x17.46875] + "A" + TextNode <#text> + BlockContainer at (72.5625,20) content-size 23.4375x17.46875 table-cell [BFC] children: inline + line 0 width: 9.34375, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [72.5625,20 9.34375x17.46875] + "B" + TextNode <#text> + Box
at (20,59.46875) content-size 30.5625x17.46875 table-cell [BFC] children: inline + line 0 width: 14.265625, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [20,59.46875 14.265625x17.46875] + "A" + TextNode <#text> + BlockContainer <(anonymous)> at (62.5625,68.703125) content-size 21.71875x0 table-cell [BFC] children: not-inline + BlockContainer <(anonymous)> at (84.28125,68.203125) content-size 21.71875x0 table-cell [BFC] children: not-inline + Box
at (20,98.9375) content-size 30.5625x17.46875 table-cell [BFC] children: inline + line 0 width: 14.265625, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [20,98.9375 14.265625x17.46875] + "A" + TextNode <#text> + BlockContainer <(anonymous)> at (62.5625,107.171875) content-size 21.71875x0 table-cell [BFC] children: not-inline + BlockContainer <(anonymous)> at (84.28125,107.171875) content-size 21.71875x0 table-cell [BFC] children: not-inline + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x600] + PaintableWithLines (BlockContainer) [8,8 784x120.40625] + PaintableBox (Box
) [8,8 784x120.40625] + PaintableWithLines (TableWrapper(anonymous)) [8,8 100x120.40625] + PaintableBox (Box) [8,8 100x120.40625] + PaintableBox (Box) [9,9 98x118.40625] + PaintableBox (Box) [9,9 98x39.46875] + PaintableWithLines (BlockContainer) [9,48.46875 98x39.46875] + PaintableWithLines (BlockContainer) [9,87.9375 98x39.46875] + PaintableWithLines (BlockContainer
) [9,9 52.5625x39.46875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer) [61.5625,9 45.4375x39.46875] + TextPaintable (TextNode<#text>) + PaintableBox (Box
) [9,48.46875 52.5625x39.46875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [61.5625,48.46875 22.71875x39.46875] + PaintableWithLines (BlockContainer(anonymous)) [84.28125,48.46875 22.71875x39.46875] + PaintableBox (Box
) [9,87.9375 52.5625x39.46875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [61.5625,87.9375 22.71875x39.46875] + PaintableWithLines (BlockContainer(anonymous)) [84.28125,87.9375 22.71875x39.46875] diff --git a/Tests/LibWeb/Layout/expected/table/missing-cells.txt b/Tests/LibWeb/Layout/expected/table/missing-cells.txt new file mode 100644 index 0000000000..15e66f9750 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/table/missing-cells.txt @@ -0,0 +1,53 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x120.40625 children: not-inline + Box
at (8,8) content-size 784x120.40625 [GFC] children: not-inline + TableWrapper <(anonymous)> at (8,8) content-size 100x120.40625 [BFC] children: not-inline + Box at (9,9) content-size 98x118.40625 table-box [TFC] children: not-inline + Box at (9,9) content-size 98x118.40625 table-row-group children: not-inline + Box at (9,9) content-size 98x39.46875 table-row children: not-inline + BlockContainer at (9,48.46875) content-size 98x39.46875 table-row children: not-inline + BlockContainer at (9,87.9375) content-size 98x39.46875 table-row children: not-inline + BlockContainer
at (20,20) content-size 30.5625x17.46875 table-cell [BFC] children: inline + line 0 width: 14.265625, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [20,20 14.265625x17.46875] + "A" + TextNode <#text> + BlockContainer at (72.5625,20) content-size 23.4375x17.46875 table-cell [BFC] children: inline + line 0 width: 9.34375, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [72.5625,20 9.34375x17.46875] + "B" + TextNode <#text> + Box
at (20,59.46875) content-size 30.5625x17.46875 table-cell [BFC] children: inline + line 0 width: 14.265625, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [20,59.46875 14.265625x17.46875] + "A" + TextNode <#text> + BlockContainer <(anonymous)> at (62.5625,68.703125) content-size 43.4375x0 table-cell [BFC] children: not-inline + Box
at (20,98.9375) content-size 30.5625x17.46875 table-cell [BFC] children: inline + line 0 width: 14.265625, height: 17.46875, bottom: 17.46875, baseline: 13.53125 + frag 0 from TextNode start: 0, length: 1, rect: [20,98.9375 14.265625x17.46875] + "A" + TextNode <#text> + BlockContainer <(anonymous)> at (62.5625,107.171875) content-size 43.4375x0 table-cell [BFC] children: not-inline + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x600] + PaintableWithLines (BlockContainer) [8,8 784x120.40625] + PaintableBox (Box
) [8,8 784x120.40625] + PaintableWithLines (TableWrapper(anonymous)) [8,8 100x120.40625] + PaintableBox (Box) [8,8 100x120.40625] + PaintableBox (Box) [9,9 98x118.40625] + PaintableBox (Box) [9,9 98x39.46875] + PaintableWithLines (BlockContainer) [9,48.46875 98x39.46875] + PaintableWithLines (BlockContainer) [9,87.9375 98x39.46875] + PaintableWithLines (BlockContainer
) [9,9 52.5625x39.46875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer) [61.5625,9 45.4375x39.46875] + TextPaintable (TextNode<#text>) + PaintableBox (Box
) [9,48.46875 52.5625x39.46875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [61.5625,48.46875 45.4375x39.46875] + PaintableBox (Box
) [9,87.9375 52.5625x39.46875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [61.5625,87.9375 45.4375x39.46875] diff --git a/Tests/LibWeb/Layout/input/table/missing-cells-with-span.html b/Tests/LibWeb/Layout/input/table/missing-cells-with-span.html new file mode 100644 index 0000000000..08def4751b --- /dev/null +++ b/Tests/LibWeb/Layout/input/table/missing-cells-with-span.html @@ -0,0 +1,16 @@ +
AB
A
A
\ No newline at end of file diff --git a/Tests/LibWeb/Layout/input/table/missing-cells.html b/Tests/LibWeb/Layout/input/table/missing-cells.html new file mode 100644 index 0000000000..11c76298a6 --- /dev/null +++ b/Tests/LibWeb/Layout/input/table/missing-cells.html @@ -0,0 +1,16 @@ +
AB
A
A
\ No newline at end of file diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp index 472bf94784..3721933887 100644 --- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp @@ -12,26 +12,6 @@ #include #include -struct GridPosition { - size_t x; - size_t y; -}; - -inline bool operator==(GridPosition const& a, GridPosition const& b) -{ - return a.x == b.x && a.y == b.y; -} - -namespace AK { -template<> -struct Traits : public GenericTraits { - static unsigned hash(GridPosition const& key) - { - return pair_int_hash(key.x, key.y); - } -}; -} - namespace Web::Layout { TableFormattingContext::TableFormattingContext(LayoutState& state, Box const& root, FormattingContext* parent) diff --git a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h index 0512799217..7c4c04584b 100644 --- a/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/TableFormattingContext.h @@ -38,7 +38,6 @@ public: private: CSSPixels run_caption_layout(LayoutMode, CSS::CaptionSide); CSSPixels compute_capmin(); - void calculate_row_column_grid(Box const&); void compute_constrainedness(); void compute_cell_measures(); void compute_outer_content_sizes(); diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index 43b707cc4b..aca6b2caa3 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -451,7 +452,8 @@ void TreeBuilder::fixup_tables(NodeWithStyle& root) { remove_irrelevant_boxes(root); generate_missing_child_wrappers(root); - generate_missing_parents(root); + auto table_root_boxes = generate_missing_parents(root); + missing_cells_fixup(table_root_boxes); } void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root) @@ -621,7 +623,7 @@ void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root) }); } -void TreeBuilder::generate_missing_parents(NodeWithStyle& root) +Vector> TreeBuilder::generate_missing_parents(NodeWithStyle& root) { Vector> table_roots_to_wrap; root.for_each_in_inclusive_subtree_of_type([&](auto& parent) { @@ -671,6 +673,57 @@ void TreeBuilder::generate_missing_parents(NodeWithStyle& root) else parent.append_child(*wrapper); } + + return table_roots_to_wrap; } +template +static void for_each_child_box_matching(Box& parent, Matcher matcher, Callback callback) +{ + parent.for_each_child_of_type([&](Box& child_box) { + if (matcher(child_box)) + callback(child_box); + }); +} + +static void fixup_row(Box& row_box, TableGrid const& table_grid, size_t row_index) +{ + bool missing_cells_run_has_started = false; + for (size_t column_index = 0; column_index < table_grid.column_count(); ++column_index) { + if (table_grid.occupancy_grid().contains({ column_index, row_index })) { + VERIFY(!missing_cells_run_has_started); + continue; + } + missing_cells_run_has_started = true; + auto row_computed_values = row_box.computed_values().clone_inherited_values(); + auto& cell_computed_values = static_cast(row_computed_values); + cell_computed_values.set_display(Web::CSS::Display { CSS::Display::Internal::TableCell }); + // Ensure that the cell (with zero content height) will have the same height as the row by setting vertical-align to middle. + cell_computed_values.set_vertical_align(CSS::VerticalAlign::Middle); + auto cell_box = row_box.heap().template allocate_without_realm(row_box.document(), nullptr, cell_computed_values); + row_box.append_child(cell_box); + } +} + +void TreeBuilder::missing_cells_fixup(Vector> const& table_root_boxes) +{ + // Implements https://www.w3.org/TR/css-tables-3/#missing-cells-fixup. + for (auto& table_box : table_root_boxes) { + auto table_grid = TableGrid::calculate_row_column_grid(*table_box); + size_t row_index = 0; + for_each_child_box_matching(*table_box, TableGrid::is_table_row_group, [&](auto& row_group_box) { + for_each_child_box_matching(row_group_box, is_table_row, [&](auto& row_box) { + fixup_row(row_box, table_grid, row_index); + ++row_index; + return IterationDecision::Continue; + }); + }); + + for_each_child_box_matching(*table_box, is_table_row, [&](auto& row_box) { + fixup_row(row_box, table_grid, row_index); + ++row_index; + return IterationDecision::Continue; + }); + } +} } diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h index e68a4e692b..0c40bf5d5d 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h @@ -39,7 +39,8 @@ private: void fixup_tables(NodeWithStyle& root); void remove_irrelevant_boxes(NodeWithStyle& root); void generate_missing_child_wrappers(NodeWithStyle& root); - void generate_missing_parents(NodeWithStyle& root); + Vector> generate_missing_parents(NodeWithStyle& root); + void missing_cells_fixup(Vector> const&); enum class AppendOrPrepend { Append,