Ver código fonte

LibWeb: Update DOM cloning algorithm for declarative shadow DOM

Andreas Kling 1 ano atrás
pai
commit
c7d9c1c0b2

+ 26 - 7
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -817,7 +817,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> Node::replace_child(JS::NonnullGCPtr
 }
 
 // https://dom.spec.whatwg.org/#concept-node-clone
-JS::NonnullGCPtr<Node> Node::clone_node(Document* document, bool clone_children)
+WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> Node::clone_node(Document* document, bool clone_children)
 {
     // 1. If document is not given, let document be node’s node document.
     if (!document)
@@ -898,18 +898,37 @@ JS::NonnullGCPtr<Node> Node::clone_node(Document* document, bool clone_children)
     // FIXME: 4. Set copy’s node document and document to copy, if copy is a document, and set copy’s node document to document otherwise.
 
     // 5. Run any cloning steps defined for node in other applicable specifications and pass copy, node, document and the clone children flag if set, as parameters.
-    cloned(*copy, clone_children);
+    TRY(cloned(*copy, clone_children));
 
     // 6. If the clone children flag is set, clone all the children of node and append them to copy, with document as specified and the clone children flag being set.
     if (clone_children) {
-        for_each_child([&](auto& child) {
-            MUST(copy->append_child(child.clone_node(document, true)));
-            return IterationDecision::Continue;
-        });
+        for (auto child = first_child(); child; child = child->next_sibling()) {
+            TRY(copy->append_child(TRY(child->clone_node(document, true))));
+        }
+    }
+
+    // 7. If node is a shadow host whose shadow root’s clonable is true:
+    if (is_element() && static_cast<Element const&>(*this).is_shadow_host() && static_cast<Element const&>(*this).shadow_root()->clonable()) {
+        // 1. Assert: copy is not a shadow host.
+        VERIFY(!copy->is_element() || !static_cast<Element const&>(*copy).is_shadow_host());
+
+        // 2. Run attach a shadow root with copy, node’s shadow root’s mode, true, node’s shadow root’s serializable,
+        //    node’s shadow root’s delegates focus, and node’s shadow root’s slot assignment.
+        auto& node_shadow_root = *static_cast<Element const&>(*this).shadow_root();
+        TRY(static_cast<Element&>(*copy).attach_a_shadow_root(node_shadow_root.mode(), true, node_shadow_root.serializable(), node_shadow_root.delegates_focus(), node_shadow_root.slot_assignment()));
+
+        // 3. Set copy’s shadow root’s declarative to node’s shadow root’s declarative.
+        static_cast<Element&>(*copy).shadow_root()->set_declarative(node_shadow_root.declarative());
+
+        // 4. For each child child of node’s shadow root, in tree order:
+        //    append the result of cloning child with document and the clone children flag set, to copy’s shadow root.
+        for (auto child = node_shadow_root.first_child(); child; child = child->next_sibling()) {
+            TRY(static_cast<Element&>(*copy).shadow_root()->append_child(TRY(child->clone_node(document, true))));
+        }
     }
 
     // 7. Return copy.
-    return *copy;
+    return JS::NonnullGCPtr { *copy };
 }
 
 // https://dom.spec.whatwg.org/#dom-node-clonenode

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

@@ -140,7 +140,7 @@ public:
 
     WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> replace_child(JS::NonnullGCPtr<Node> node, JS::NonnullGCPtr<Node> child);
 
-    JS::NonnullGCPtr<Node> clone_node(Document* document = nullptr, bool clone_children = false);
+    WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> clone_node(Document* document = nullptr, bool clone_children = false);
     WebIDL::ExceptionOr<JS::NonnullGCPtr<Node>> clone_node_binding(bool deep);
 
     // NOTE: This is intended for the JS bindings.
@@ -198,7 +198,7 @@ public:
     virtual void removed_from(Node*);
     virtual void children_changed() { }
     virtual void adopted_from(Document&) { }
-    virtual void cloned(Node&, bool) {};
+    virtual WebIDL::ExceptionOr<void> cloned(Node&, bool) { return {}; }
 
     Layout::Node const* layout_node() const { return m_layout_node; }
     Layout::Node* layout_node() { return m_layout_node; }

+ 11 - 11
Userland/Libraries/LibWeb/DOM/Range.cpp

@@ -633,7 +633,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
     // 4. If original start node is original end node and it is a CharacterData node, then:
     if (original_start_node.ptr() == original_end_node.ptr() && is<CharacterData>(*original_start_node)) {
         // 1. Let clone be a clone of original start node.
-        auto clone = original_start_node->clone_node();
+        auto clone = TRY(original_start_node->clone_node());
 
         // 2. Set the data of clone to the result of substringing data with node original start node,
         //    offset original start offset, and count original end offset minus original start offset.
@@ -723,7 +723,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
     // 15. If first partially contained child is a CharacterData node, then:
     if (first_partially_contained_child && is<CharacterData>(*first_partially_contained_child)) {
         // 1. Let clone be a clone of original start node.
-        auto clone = original_start_node->clone_node();
+        auto clone = TRY(original_start_node->clone_node());
 
         // 2. Set the data of clone to the result of substringing data with node original start node, offset original start offset,
         //    and count original start node’s length minus original start offset.
@@ -739,7 +739,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
     // 16. Otherwise, if first partially contained child is not null:
     else if (first_partially_contained_child) {
         // 1. Let clone be a clone of first partially contained child.
-        auto clone = first_partially_contained_child->clone_node();
+        auto clone = TRY(first_partially_contained_child->clone_node());
 
         // 2. Append clone to fragment.
         TRY(fragment->append_child(clone));
@@ -762,7 +762,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
     // 18. If last partially contained child is a CharacterData node, then:
     if (last_partially_contained_child && is<CharacterData>(*last_partially_contained_child)) {
         // 1. Let clone be a clone of original end node.
-        auto clone = original_end_node->clone_node();
+        auto clone = TRY(original_end_node->clone_node());
 
         // 2. Set the data of clone to the result of substringing data with node original end node, offset 0, and count original end offset.
         auto result = TRY(static_cast<CharacterData const&>(*original_end_node).substring_data(0, original_end_offset));
@@ -777,7 +777,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::extract()
     // 19. Otherwise, if last partially contained child is not null:
     else if (last_partially_contained_child) {
         // 1. Let clone be a clone of last partially contained child.
-        auto clone = last_partially_contained_child->clone_node();
+        auto clone = TRY(last_partially_contained_child->clone_node());
 
         // 2. Append clone to fragment.
         TRY(fragment->append_child(clone));
@@ -962,7 +962,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_content
     // 4. If original start node is original end node and it is a CharacterData node, then:
     if (original_start_node.ptr() == original_end_node.ptr() && is<CharacterData>(*original_start_node)) {
         // 1. Let clone be a clone of original start node.
-        auto clone = original_start_node->clone_node();
+        auto clone = TRY(original_start_node->clone_node());
 
         // 2. Set the data of clone to the result of substringing data with node original start node,
         //    offset original start offset, and count original end offset minus original start offset.
@@ -1027,7 +1027,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_content
     // 13. If first partially contained child is a CharacterData node, then:
     if (first_partially_contained_child && is<CharacterData>(*first_partially_contained_child)) {
         // 1. Let clone be a clone of original start node.
-        auto clone = original_start_node->clone_node();
+        auto clone = TRY(original_start_node->clone_node());
 
         // 2. Set the data of clone to the result of substringing data with node original start node, offset original start offset,
         //    and count original start node’s length minus original start offset.
@@ -1040,7 +1040,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_content
     // 14. Otherwise, if first partially contained child is not null:
     else if (first_partially_contained_child) {
         // 1. Let clone be a clone of first partially contained child.
-        auto clone = first_partially_contained_child->clone_node();
+        auto clone = TRY(first_partially_contained_child->clone_node());
 
         // 2. Append clone to fragment.
         TRY(fragment->append_child(clone));
@@ -1058,7 +1058,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_content
     // 15. For each contained child in contained children.
     for (auto& contained_child : contained_children) {
         // 1. Let clone be a clone of contained child with the clone children flag set.
-        auto clone = contained_child->clone_node(nullptr, true);
+        auto clone = TRY(contained_child->clone_node(nullptr, true));
 
         // 2. Append clone to fragment.
         TRY(fragment->append_child(move(clone)));
@@ -1067,7 +1067,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_content
     // 16. If last partially contained child is a CharacterData node, then:
     if (last_partially_contained_child && is<CharacterData>(*last_partially_contained_child)) {
         // 1. Let clone be a clone of original end node.
-        auto clone = original_end_node->clone_node();
+        auto clone = TRY(original_end_node->clone_node());
 
         // 2. Set the data of clone to the result of substringing data with node original end node, offset 0, and count original end offset.
         auto result = TRY(static_cast<CharacterData const&>(*original_end_node).substring_data(0, original_end_offset));
@@ -1079,7 +1079,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::clone_the_content
     // 17. Otherwise, if last partially contained child is not null:
     else if (last_partially_contained_child) {
         // 1. Let clone be a clone of last partially contained child.
-        auto clone = last_partially_contained_child->clone_node();
+        auto clone = TRY(last_partially_contained_child->clone_node());
 
         // 2. Append clone to fragment.
         TRY(fragment->append_child(clone));

+ 11 - 11
Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.cpp

@@ -46,21 +46,21 @@ void HTMLTemplateElement::adopted_from(DOM::Document&)
 }
 
 // https://html.spec.whatwg.org/multipage/scripting.html#the-template-element:concept-node-clone-ext
-void HTMLTemplateElement::cloned(Node& copy, bool clone_children)
+WebIDL::ExceptionOr<void> HTMLTemplateElement::cloned(Node& copy, bool clone_children)
 {
+    // 1. If the clone children flag is not set in the calling clone algorithm, return.
     if (!clone_children)
-        return;
+        return {};
 
+    // 2. Let copied contents be the result of cloning all the children of node's template contents,
+    //    with document set to copy's template contents's node document, and with the clone children flag set.
+    // 3. Append copied contents to copy's template contents.
     auto& template_clone = verify_cast<HTMLTemplateElement>(copy);
-
-    content()->for_each_child([&](auto& child) {
-        auto cloned_child = child.clone_node(&template_clone.content()->document(), true);
-
-        // FIXME: Should this use TreeNode::append_child instead?
-        MUST(template_clone.content()->append_child(cloned_child));
-
-        return IterationDecision::Continue;
-    });
+    for (auto child = content()->first_child(); child; child = child->next_sibling()) {
+        auto cloned_child = TRY(child->clone_node(&template_clone.content()->document(), true));
+        TRY(template_clone.content()->append_child(cloned_child));
+    }
+    return {};
 }
 
 void HTMLTemplateElement::set_template_contents(JS::NonnullGCPtr<DOM::DocumentFragment> contents)

+ 1 - 1
Userland/Libraries/LibWeb/HTML/HTMLTemplateElement.h

@@ -24,7 +24,7 @@ public:
     void set_template_contents(JS::NonnullGCPtr<DOM::DocumentFragment>);
 
     virtual void adopted_from(DOM::Document&) override;
-    virtual void cloned(Node& copy, bool clone_children) override;
+    virtual WebIDL::ExceptionOr<void> cloned(Node& copy, bool clone_children) override;
 
 private:
     HTMLTemplateElement(DOM::Document&, DOM::QualifiedName);

+ 1 - 1
Userland/Libraries/LibWeb/SVG/SVGUseElement.cpp

@@ -130,7 +130,7 @@ void SVGUseElement::clone_element_tree_as_our_shadow_tree(Element* to_clone) con
 
     if (to_clone && is_valid_reference_element(to_clone)) {
         // The ‘use’ element references another element, a copy of which is rendered in place of the ‘use’ in the document.
-        auto cloned_reference_node = to_clone->clone_node(nullptr, true);
+        auto cloned_reference_node = MUST(to_clone->clone_node(nullptr, true));
         shadow_root()->append_child(cloned_reference_node).release_value_but_fixme_should_propagate_errors();
     }
 }

+ 1 - 1
Userland/Services/WebContent/ConnectionFromClient.cpp

@@ -687,7 +687,7 @@ void ConnectionFromClient::clone_dom_node(u64 page_id, i32 node_id)
         return;
     }
 
-    auto dom_node_clone = dom_node->clone_node(nullptr, true);
+    auto dom_node_clone = MUST(dom_node->clone_node(nullptr, true));
     dom_node->parent_node()->insert_before(dom_node_clone, dom_node->next_sibling());
 
     async_did_finish_editing_dom_node(page_id, dom_node_clone->unique_id());