소스 검색

LibWeb: Implement table missing cells fixup

Fixes #19936.
Andi Gallo 1 년 전
부모
커밋
769f11f9ae

+ 57 - 0
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 <html> at (0,0) content-size 800x600 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x120.40625 children: not-inline
+      Box <div> 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 <table> at (9,9) content-size 98x118.40625 table-box [TFC] children: not-inline
+            Box <tbody> at (9,9) content-size 98x118.40625 table-row-group children: not-inline
+              Box <tr> at (9,9) content-size 98x39.46875 table-row children: not-inline
+                BlockContainer <td> 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 <td> 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 <tr> at (9,48.46875) content-size 98x39.46875 table-row children: not-inline
+                BlockContainer <td> 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 <tr> at (9,87.9375) content-size 98x39.46875 table-row children: not-inline
+                BlockContainer <td> 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<HTML>) [0,0 800x600]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x120.40625]
+      PaintableBox (Box<DIV>) [8,8 784x120.40625]
+        PaintableWithLines (TableWrapper(anonymous)) [8,8 100x120.40625]
+          PaintableBox (Box<TABLE>) [8,8 100x120.40625]
+            PaintableBox (Box<TBODY>) [9,9 98x118.40625]
+              PaintableBox (Box<TR>) [9,9 98x39.46875]
+                PaintableWithLines (BlockContainer<TD>) [9,9 52.5625x39.46875]
+                  TextPaintable (TextNode<#text>)
+                PaintableWithLines (BlockContainer<TD>) [61.5625,9 45.4375x39.46875]
+                  TextPaintable (TextNode<#text>)
+              PaintableBox (Box<TR>) [9,48.46875 98x39.46875]
+                PaintableWithLines (BlockContainer<TD>) [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<TR>) [9,87.9375 98x39.46875]
+                PaintableWithLines (BlockContainer<TD>) [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]

+ 53 - 0
Tests/LibWeb/Layout/expected/table/missing-cells.txt

@@ -0,0 +1,53 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x120.40625 children: not-inline
+      Box <div> 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 <table> at (9,9) content-size 98x118.40625 table-box [TFC] children: not-inline
+            Box <tbody> at (9,9) content-size 98x118.40625 table-row-group children: not-inline
+              Box <tr> at (9,9) content-size 98x39.46875 table-row children: not-inline
+                BlockContainer <td> 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 <td> 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 <tr> at (9,48.46875) content-size 98x39.46875 table-row children: not-inline
+                BlockContainer <td> 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 <tr> at (9,87.9375) content-size 98x39.46875 table-row children: not-inline
+                BlockContainer <td> 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<HTML>) [0,0 800x600]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x120.40625]
+      PaintableBox (Box<DIV>) [8,8 784x120.40625]
+        PaintableWithLines (TableWrapper(anonymous)) [8,8 100x120.40625]
+          PaintableBox (Box<TABLE>) [8,8 100x120.40625]
+            PaintableBox (Box<TBODY>) [9,9 98x118.40625]
+              PaintableBox (Box<TR>) [9,9 98x39.46875]
+                PaintableWithLines (BlockContainer<TD>) [9,9 52.5625x39.46875]
+                  TextPaintable (TextNode<#text>)
+                PaintableWithLines (BlockContainer<TD>) [61.5625,9 45.4375x39.46875]
+                  TextPaintable (TextNode<#text>)
+              PaintableBox (Box<TR>) [9,48.46875 98x39.46875]
+                PaintableWithLines (BlockContainer<TD>) [9,48.46875 52.5625x39.46875]
+                  TextPaintable (TextNode<#text>)
+                PaintableWithLines (BlockContainer(anonymous)) [61.5625,48.46875 45.4375x39.46875]
+              PaintableBox (Box<TR>) [9,87.9375 98x39.46875]
+                PaintableWithLines (BlockContainer<TD>) [9,87.9375 52.5625x39.46875]
+                  TextPaintable (TextNode<#text>)
+                PaintableWithLines (BlockContainer(anonymous)) [61.5625,87.9375 45.4375x39.46875]

+ 16 - 0
Tests/LibWeb/Layout/input/table/missing-cells-with-span.html

@@ -0,0 +1,16 @@
+<style>
+    div {
+        display: grid;
+    }
+
+    table {
+        border: 1px solid black;
+        border-collapse: collapse;
+        width: 100px;
+    }
+
+    td {
+        border: 1px solid black;
+        padding: 10px;
+    }
+</style><div><table><tr><td>A</td><td colspan="2">B</td></tr><tr><td>A</td></tr><tr><td>A</td></tr></table></div>

+ 16 - 0
Tests/LibWeb/Layout/input/table/missing-cells.html

@@ -0,0 +1,16 @@
+<style>
+    div {
+        display: grid;
+    }
+
+    table {
+        border: 1px solid black;
+        border-collapse: collapse;
+        width: 100px;
+    }
+
+    td {
+        border: 1px solid black;
+        padding: 10px;
+    }
+</style><div><table><tr><td>A</td><td>B</td></tr><tr><td>A</td></tr><tr><td>A</td></tr></table></div>

+ 0 - 20
Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp

@@ -12,26 +12,6 @@
 #include <LibWeb/Layout/InlineFormattingContext.h>
 #include <LibWeb/Layout/InlineFormattingContext.h>
 #include <LibWeb/Layout/TableFormattingContext.h>
 #include <LibWeb/Layout/TableFormattingContext.h>
 
 
-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<GridPosition> : public GenericTraits<GridPosition> {
-    static unsigned hash(GridPosition const& key)
-    {
-        return pair_int_hash(key.x, key.y);
-    }
-};
-}
-
 namespace Web::Layout {
 namespace Web::Layout {
 
 
 TableFormattingContext::TableFormattingContext(LayoutState& state, Box const& root, FormattingContext* parent)
 TableFormattingContext::TableFormattingContext(LayoutState& state, Box const& root, FormattingContext* parent)

+ 0 - 1
Userland/Libraries/LibWeb/Layout/TableFormattingContext.h

@@ -38,7 +38,6 @@ public:
 private:
 private:
     CSSPixels run_caption_layout(LayoutMode, CSS::CaptionSide);
     CSSPixels run_caption_layout(LayoutMode, CSS::CaptionSide);
     CSSPixels compute_capmin();
     CSSPixels compute_capmin();
-    void calculate_row_column_grid(Box const&);
     void compute_constrainedness();
     void compute_constrainedness();
     void compute_cell_measures();
     void compute_cell_measures();
     void compute_outer_content_sizes();
     void compute_outer_content_sizes();

+ 55 - 2
Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp

@@ -25,6 +25,7 @@
 #include <LibWeb/Layout/ListItemMarkerBox.h>
 #include <LibWeb/Layout/ListItemMarkerBox.h>
 #include <LibWeb/Layout/Node.h>
 #include <LibWeb/Layout/Node.h>
 #include <LibWeb/Layout/Progress.h>
 #include <LibWeb/Layout/Progress.h>
+#include <LibWeb/Layout/TableGrid.h>
 #include <LibWeb/Layout/TableWrapper.h>
 #include <LibWeb/Layout/TableWrapper.h>
 #include <LibWeb/Layout/TextNode.h>
 #include <LibWeb/Layout/TextNode.h>
 #include <LibWeb/Layout/TreeBuilder.h>
 #include <LibWeb/Layout/TreeBuilder.h>
@@ -451,7 +452,8 @@ void TreeBuilder::fixup_tables(NodeWithStyle& root)
 {
 {
     remove_irrelevant_boxes(root);
     remove_irrelevant_boxes(root);
     generate_missing_child_wrappers(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)
 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<JS::Handle<Box>> TreeBuilder::generate_missing_parents(NodeWithStyle& root)
 {
 {
     Vector<JS::Handle<Box>> table_roots_to_wrap;
     Vector<JS::Handle<Box>> table_roots_to_wrap;
     root.for_each_in_inclusive_subtree_of_type<Box>([&](auto& parent) {
     root.for_each_in_inclusive_subtree_of_type<Box>([&](auto& parent) {
@@ -671,6 +673,57 @@ void TreeBuilder::generate_missing_parents(NodeWithStyle& root)
         else
         else
             parent.append_child(*wrapper);
             parent.append_child(*wrapper);
     }
     }
+
+    return table_roots_to_wrap;
+}
+
+template<typename Matcher, typename Callback>
+static void for_each_child_box_matching(Box& parent, Matcher matcher, Callback callback)
+{
+    parent.for_each_child_of_type<Box>([&](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<CSS::MutableComputedValues&>(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<BlockContainer>(row_box.document(), nullptr, cell_computed_values);
+        row_box.append_child(cell_box);
+    }
+}
+
+void TreeBuilder::missing_cells_fixup(Vector<JS::Handle<Box>> 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;
+        });
+    }
+}
 }
 }

+ 2 - 1
Userland/Libraries/LibWeb/Layout/TreeBuilder.h

@@ -39,7 +39,8 @@ private:
     void fixup_tables(NodeWithStyle& root);
     void fixup_tables(NodeWithStyle& root);
     void remove_irrelevant_boxes(NodeWithStyle& root);
     void remove_irrelevant_boxes(NodeWithStyle& root);
     void generate_missing_child_wrappers(NodeWithStyle& root);
     void generate_missing_child_wrappers(NodeWithStyle& root);
-    void generate_missing_parents(NodeWithStyle& root);
+    Vector<JS::Handle<Box>> generate_missing_parents(NodeWithStyle& root);
+    void missing_cells_fixup(Vector<JS::Handle<Box>> const&);
 
 
     enum class AppendOrPrepend {
     enum class AppendOrPrepend {
         Append,
         Append,