Jelajahi Sumber

LibWeb: Add Node.textContent

This requires moving remove_all_children() from ParentNode to
Node, which makes ParentNode.cpp empty, so remove it.

It also co-opts the existing Node::text_content() method and
tweaks it slightly to fit the semantics of Node.textContent.
Nico Weber 5 tahun lalu
induk
melakukan
e9b56b5b9c

+ 0 - 1
Libraries/LibWeb/CMakeLists.txt

@@ -33,7 +33,6 @@ set(SOURCES
     DOM/EventListener.cpp
     DOM/EventTarget.cpp
     DOM/Node.cpp
-    DOM/ParentNode.cpp
     DOM/Position.cpp
     DOM/TagNames.cpp
     DOM/Text.cpp

+ 21 - 3
Libraries/LibWeb/DOM/Node.cpp

@@ -84,14 +84,25 @@ String Node::text_content() const
         auto text = child->text_content();
         if (!text.is_empty()) {
             builder.append(child->text_content());
-            builder.append(' ');
         }
     }
-    if (builder.length() > 1)
-        builder.trim(1);
     return builder.to_string();
 }
 
+void Node::set_text_content(const String& content)
+{
+    if (is_text()) {
+        downcast<Text>(this)->set_data(content);
+    } else {
+        remove_all_children();
+        append_child(document().create_text_node(content));
+    }
+
+    set_needs_style_update(true);
+    document().schedule_style_update();
+    document().invalidate_layout();
+}
+
 RefPtr<LayoutNode> Node::create_layout_node(const CSS::StyleProperties*)
 {
     return nullptr;
@@ -197,6 +208,13 @@ RefPtr<Node> Node::insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, b
     return node;
 }
 
+void Node::remove_all_children()
+{
+    while (RefPtr<Node> child = first_child()) {
+        remove_child(*child);
+    }
+}
+
 void Node::set_document(Badge<Document>, Document& document)
 {
     m_document = &document;

+ 2 - 0
Libraries/LibWeb/DOM/Node.h

@@ -79,12 +79,14 @@ public:
 
     RefPtr<Node> append_child(NonnullRefPtr<Node>, bool notify = true);
     RefPtr<Node> insert_before(NonnullRefPtr<Node> node, RefPtr<Node> child, bool notify = true);
+    void remove_all_children();
 
     virtual RefPtr<LayoutNode> create_layout_node(const CSS::StyleProperties* parent_style);
 
     virtual FlyString node_name() const = 0;
 
     virtual String text_content() const;
+    void set_text_content(const String&);
 
     Document& document() { return *m_document; }
     const Document& document() const { return *m_document; }

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

@@ -7,6 +7,7 @@ interface Node : EventTarget {
     readonly attribute Node? nextSibling;
     readonly attribute Node? parentNode;
     readonly attribute Element? parentElement;
+    attribute DOMString textContent;
 
     Node appendChild(Node node);
     Node insertBefore(Node node, Node? child);

+ 0 - 38
Libraries/LibWeb/DOM/ParentNode.cpp

@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <LibWeb/DOM/ParentNode.h>
-
-namespace Web::DOM {
-
-void ParentNode::remove_all_children()
-{
-    while (RefPtr<Node> child = first_child()) {
-        remove_child(*child);
-    }
-}
-
-}

+ 0 - 2
Libraries/LibWeb/DOM/ParentNode.h

@@ -35,8 +35,6 @@ public:
     template<typename F> void for_each_child(F) const;
     template<typename F> void for_each_child(F);
 
-    void remove_all_children();
-
 protected:
     explicit ParentNode(Document& document, NodeType type)
         : Node(document, type)

+ 20 - 0
Libraries/LibWeb/Tests/DOM/Node.js

@@ -0,0 +1,20 @@
+loadPage("file:///res/html/misc/small.html");
+
+afterInitialPageLoad(() => {
+    test("Node.textContent", () => {
+        var p = document.getElementsByTagName("p")[0];
+        expect(p.textContent).toBe("This is a very small test page :^)");
+        expect(p.firstChild.textContent).toBe("This is a ");
+        expect(p.firstChild.firstChild).toBe(null);
+
+        p.firstChild.textContent = "foo";
+        expect(p.firstChild.textContent).toBe("foo");
+        expect(p.firstChild.firstChild).toBe(null);
+        expect(p.textContent).toBe("foovery small test page :^)");
+
+        p.textContent = "bar";
+        expect(p.textContent).toBe("bar");
+        expect(p.firstChild.textContent).toBe("bar");
+        expect(p.firstChild.firstChild).toBe(null);
+    });
+});