Przeglądaj źródła

LibWeb: Make Layout::TextNode::text_for_rendering() lazily computed

As it turns out, Layout::TreeBuilder never managed to wrap text within
table boxes in anonymous wrapper boxes, since it relied on checking
text_for_rendering(), and that was never initialized during that early
stage of tree building.

This patch fixes the issue by making text_for_rendering() compute the
(potentially collapsed) text lazily when called.

Note that the test included with this patch is still totally wrong,
but that is now a TFC problem rather than a TreeBuilder problem. :^)
Andreas Kling 2 lat temu
rodzic
commit
b918ce4022

+ 18 - 0
Tests/LibWeb/Layout/expected/table/table-header-and-footer-groups.txt

@@ -0,0 +1,18 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (1,1) content-size 798x216 [BFC] children: not-inline
+    TableWrapper <(anonymous)> at (9,9) content-size 300x200 [BFC] children: not-inline
+      Box <body.table> at (10,10) content-size 298x198 table-box [TFC] children: not-inline
+        Box <div.bottom> at (10,10) content-size 298x0 table-footer-group children: inline
+          Box <(anonymous)> at (10,10) content-size 298x0 table-row children: inline
+            BlockContainer <(anonymous)> at (10,10) content-size 298x0 table-cell [BFC] children: inline
+              line 0 width: 56.109375, height: 0, bottom: 0, baseline: 4.796875
+                frag 0 from TextNode start: 0, length: 6, rect: [10,10 56.109375x0]
+                  "bottom"
+              TextNode <#text>
+        Box <div.top> at (10,10) content-size 298x0 table-header-group children: inline
+          Box <(anonymous)> at (10,10) content-size 298x0 table-row children: inline
+            BlockContainer <(anonymous)> at (10,10) content-size 298x0 table-cell [BFC] children: inline
+              line 0 width: 26.640625, height: 0, bottom: 0, baseline: 4.796875
+                frag 0 from TextNode start: 0, length: 3, rect: [10,10 26.640625x0]
+                  "top"
+              TextNode <#text>

+ 24 - 0
Tests/LibWeb/Layout/input/table/table-header-and-footer-groups.html

@@ -0,0 +1,24 @@
+<!doctype html><style>
+* {
+    border: 1px solid black;
+}
+html {
+    background: white;
+}
+.table {
+    display: table;
+    background: pink;
+    width: 300px;
+    height: 200px;
+}
+.top {
+    display: table-header-group;
+    background: orange;
+    height: 50px;
+}
+.bottom {
+    display: table-footer-group;
+    background: magenta;
+    height: 50px;
+}
+</style><body class=table><div class=bottom>bottom</div><div class=top>top</div>

+ 0 - 3
Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp

@@ -258,9 +258,6 @@ void InlineLevelIterator::enter_text_node(Layout::TextNode const& text_node)
     if (text_node.dom_node().is_editable() && !text_node.dom_node().is_uninteresting_whitespace_node())
         do_collapse = false;
 
-    // FIXME: The const_cast here is gross.
-    const_cast<TextNode&>(text_node).compute_text_for_rendering(do_collapse);
-
     m_text_node_context = TextNodeContext {
         .do_collapse = do_collapse,
         .do_wrap_lines = do_wrap_lines,

+ 25 - 2
Userland/Libraries/LibWeb/Layout/TextNode.cpp

@@ -41,9 +41,32 @@ static ErrorOr<DeprecatedString> apply_text_transform(DeprecatedString const& st
     return string;
 }
 
-// NOTE: This collapses whitespace into a single ASCII space if collapse is true.
-void TextNode::compute_text_for_rendering(bool collapse)
+DeprecatedString const& TextNode::text_for_rendering() const
 {
+    if (m_text_for_rendering.is_null())
+        const_cast<TextNode*>(this)->compute_text_for_rendering();
+    return m_text_for_rendering;
+}
+
+// NOTE: This collapses whitespace into a single ASCII space if the CSS white-space property tells us to.
+void TextNode::compute_text_for_rendering()
+{
+    bool collapse = [](CSS::WhiteSpace white_space) {
+        switch (white_space) {
+        case CSS::WhiteSpace::Normal:
+        case CSS::WhiteSpace::Nowrap:
+        case CSS::WhiteSpace::PreLine:
+            return true;
+        case CSS::WhiteSpace::Pre:
+        case CSS::WhiteSpace::PreWrap:
+            return false;
+        }
+        VERIFY_NOT_REACHED();
+    }(computed_values().white_space());
+
+    if (dom_node().is_editable() && !dom_node().is_uninteresting_whitespace_node())
+        collapse = false;
+
     auto data = apply_text_transform(dom_node().data(), computed_values().text_transform()).release_value_but_fixme_should_propagate_errors();
 
     if (dom_node().is_password_input()) {

+ 2 - 2
Userland/Libraries/LibWeb/Layout/TextNode.h

@@ -23,7 +23,7 @@ public:
 
     const DOM::Text& dom_node() const { return static_cast<const DOM::Text&>(*Node::dom_node()); }
 
-    DeprecatedString const& text_for_rendering() const { return m_text_for_rendering; }
+    DeprecatedString const& text_for_rendering() const;
 
     struct Chunk {
         Utf8View view;
@@ -48,7 +48,7 @@ public:
         Utf8View::Iterator m_iterator;
     };
 
-    void compute_text_for_rendering(bool collapse);
+    void compute_text_for_rendering();
 
     virtual JS::GCPtr<Painting::Paintable> create_paintable() const override;