瀏覽代碼

LibWeb: Invalidate layout-transformed text on DOM text node change

This fixes an issue where programmatically changing the value of an
input element wasn't reflected visually.
Andreas Kling 1 年之前
父節點
當前提交
1c47695bae

+ 22 - 0
Tests/LibWeb/Layout/expected/input-text-node-invalidation-on-value-change.txt

@@ -0,0 +1,22 @@
+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 784x21.46875 children: inline
+      line 0 width: 120, height: 21.46875, bottom: 21.46875, baseline: 21.46875
+        frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 118x19.46875]
+      BlockContainer <input#foo> at (9,9) content-size 118x19.46875 inline-block [BFC] children: not-inline
+        Box <div> at (11,10) content-size 114x17.46875 flex-container(row) [FFC] children: not-inline
+          BlockContainer <div> at (11,10) content-size 49.734375x17.46875 flex-item [BFC] children: inline
+            line 0 width: 49.734375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+              frag 0 from TextNode start: 0, length: 4, rect: [11,10 49.734375x17.46875]
+                "PASS"
+            TextNode <#text>
+      TextNode <#text>
+      TextNode <#text>
+
+PaintableWithLines (Viewport<#document>) [0,0 800x600]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x21.46875]
+      PaintableWithLines (BlockContainer<INPUT>#foo) [8,8 120x21.46875]
+        PaintableBox (Box<DIV>) [9,9 118x19.46875]
+          PaintableWithLines (BlockContainer<DIV>) [11,10 49.734375x17.46875]
+            TextPaintable (TextNode<#text>)

+ 9 - 0
Tests/LibWeb/Layout/input/input-text-node-invalidation-on-value-change.html

@@ -0,0 +1,9 @@
+<input id="foo">
+<script>
+    let foo = document.getElementById("foo");
+    foo.value = "FAIL";
+    document.body.offsetWidth; // Force a layout
+    document.addEventListener("DOMContentLoaded", function() {
+        foo.value = "PASS";
+    });
+</script>

+ 7 - 0
Userland/Libraries/LibWeb/DOM/CharacterData.cpp

@@ -10,6 +10,7 @@
 #include <LibWeb/DOM/MutationType.h>
 #include <LibWeb/DOM/Range.h>
 #include <LibWeb/DOM/StaticNodeList.h>
+#include <LibWeb/Layout/TextNode.h>
 
 namespace Web::DOM {
 
@@ -108,6 +109,12 @@ WebIDL::ExceptionOr<void> CharacterData::replace_data(size_t offset, size_t coun
     if (parent())
         parent()->children_changed();
 
+    // NOTE: Since the text node's data has changed, we need to invalidate the text for rendering.
+    //       This ensures that the new text is reflected in layout, even if we don't end up
+    //       doing a full layout tree rebuild.
+    if (auto* layout_node = this->layout_node(); layout_node && layout_node->is_text_node())
+        static_cast<Layout::TextNode&>(*layout_node).invalidate_text_for_rendering();
+
     set_needs_style_update(true);
     document().set_needs_layout();
     return {};

+ 5 - 0
Userland/Libraries/LibWeb/Layout/TextNode.cpp

@@ -41,6 +41,11 @@ static ErrorOr<DeprecatedString> apply_text_transform(DeprecatedString const& st
     return string;
 }
 
+void TextNode::invalidate_text_for_rendering()
+{
+    m_text_for_rendering = {};
+}
+
 DeprecatedString const& TextNode::text_for_rendering() const
 {
     if (m_text_for_rendering.is_null())

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

@@ -47,6 +47,7 @@ public:
         Utf8View::Iterator m_iterator;
     };
 
+    void invalidate_text_for_rendering();
     void compute_text_for_rendering();
 
     virtual JS::GCPtr<Painting::Paintable> create_paintable() const override;