Jelajahi Sumber

LibWeb: Implement Element.innerText

Reading the property has a few warts (see FIXMEs in the included
tests), but with this the timestamps on http://45.33.8.238/
get localized :^)

Since the Date() constructor currently ignores all arguments,
they don't get localized correctly but are all set to the current
time, but hey, it's still progress from a certain point of view.
Nico Weber 5 tahun lalu
induk
melakukan
2460980d2c

+ 15 - 0
Base/res/html/misc/innertext_textcontent.html

@@ -0,0 +1,15 @@
+<html>
+    <head><title>Small test page</title></head>
+    <body bgcolor="#408080" text="#ffffff">
+        <h1 title="This is a heading" >Hello friends!</h1>
+        <p>This is a <b>very small</b> test page :^)</p>
+        <p>Visit the <a title="This is a link" href="http://www.serenityos.org/">SerenityOS home page</a> today!</p>
+
+        <p id="source">
+            <style>#source { color: red;  } #text { text-transform: uppercase; }</style>
+            <span id=text>Take   a look at<br>how this text<br>is interpreted
+                   below.</span>
+            <span style="display:none">HIDDEN TEXT</span>
+        </p>
+    </body>
+</html>

+ 34 - 0
Libraries/LibWeb/DOM/Element.cpp

@@ -41,6 +41,7 @@
 #include <LibWeb/Layout/LayoutTableCell.h>
 #include <LibWeb/Layout/LayoutTableRow.h>
 #include <LibWeb/Layout/LayoutTableRowGroup.h>
+#include <LibWeb/Layout/LayoutText.h>
 #include <LibWeb/Layout/LayoutTreeBuilder.h>
 
 namespace Web::DOM {
@@ -295,6 +296,39 @@ String Element::inner_html() const
     return builder.to_string();
 }
 
+void Element::set_inner_text(StringView text)
+{
+    remove_all_children();
+    append_child(document().create_text_node(text));
+
+    set_needs_style_update(true);
+    document().schedule_style_update();
+    document().invalidate_layout();
+}
+
+String Element::inner_text()
+{
+    StringBuilder builder;
+
+    // innerText for element being rendered takes visibility into account, so force a layout and then walk the layout tree.
+    document().layout();
+    if (!layout_node())
+        return text_content();
+
+    Function<void(const LayoutNode&)> recurse = [&](auto& node) {
+        for (auto* child = node.first_child(); child; child = child->next_sibling()) {
+            if (child->is_text())
+                builder.append(downcast<LayoutText>(*child).text_for_rendering());
+            if (child->is_break())
+                builder.append('\n');
+            recurse(*child);
+        }
+    };
+    recurse(*layout_node());
+
+    return builder.to_string();
+}
+
 bool Element::is_focused() const
 {
     return document().focused_element() == this;

+ 3 - 0
Libraries/LibWeb/DOM/Element.h

@@ -87,6 +87,9 @@ public:
     String inner_html() const;
     void set_inner_html(StringView);
 
+    String inner_text();
+    void set_inner_text(StringView);
+
     bool is_focused() const;
     virtual bool is_focusable() const { return false; }
 

+ 1 - 0
Libraries/LibWeb/DOM/Element.idl

@@ -9,6 +9,7 @@ interface Element : Node {
     ArrayFromVector querySelectorAll(DOMString selectors);
 
     attribute DOMString innerHTML;
+    attribute DOMString innerText;
     [Reflect] attribute DOMString id;
     [Reflect=class] attribute DOMString className;
 

+ 24 - 0
Libraries/LibWeb/Tests/DOM/Element.js

@@ -0,0 +1,24 @@
+loadPage("file:///res/html/misc/innertext_textcontent.html");
+
+afterInitialPageLoad(() => {
+    test("Element.innerText", () => {
+        var p = document.getElementsByTagName("p")[0];
+        expect(p.innerText).toBe("This is a very small test page :^)");
+
+        // FIXME: Call this on p once that's supported.
+        var b = document.getElementsByTagName("b")[0];
+        b.innerText = "foo";
+        expect(b.innerText).toBe("foo");
+        expect(p.innerText).toBe("This is a foo test page :^)");
+
+        p.innerText = "bar";
+        expect(p.innerText).toBe("bar");
+
+        var p = document.getElementById("source");
+        // FIXME: The leading and trailing two spaces each are wrong.
+        // FIXME: The text should be affected by the text-transform:uppercase.
+        expect(p.innerText).toBe(`  Take a look at
+how this text
+is interpreted below.  `);
+    });
+});

+ 9 - 1
Libraries/LibWeb/Tests/DOM/Node.js

@@ -1,4 +1,4 @@
-loadPage("file:///res/html/misc/small.html");
+loadPage("file:///res/html/misc/innertext_textcontent.html");
 
 afterInitialPageLoad(() => {
     test("Node.textContent", () => {
@@ -16,5 +16,13 @@ afterInitialPageLoad(() => {
         expect(p.textContent).toBe("bar");
         expect(p.firstChild.textContent).toBe("bar");
         expect(p.firstChild.firstChild).toBe(null);
+
+        var p = document.getElementById("source");
+        expect(p.textContent).toBe(`
+            #source { color: red;  } #text { text-transform: uppercase; }
+            Take   a look athow this textis interpreted
+                   below.
+            HIDDEN TEXT
+        `);
     });
 });