1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 09:20:46 +00:00

LibWeb: Update DOM cloning algorithm for declarative shadow DOM

(cherry picked from commit c7d9c1c0b21ad54894984dd73c775e225f82af09)
This commit is contained in:
Andreas Kling 2024-06-25 11:06:41 +02:00 committed by Nico Weber
parent 6217ebe33c
commit 1c5f0e8daf
7 changed files with 53 additions and 34 deletions

View File

@ -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 nodes node document.
if (!document)
@ -898,18 +898,37 @@ JS::NonnullGCPtr<Node> Node::clone_node(Document* document, bool clone_children)
// FIXME: 4. Set copys node document and document to copy, if copy is a document, and set copys 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 roots 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, nodes shadow roots mode, true, nodes shadow roots serializable,
// nodes shadow roots delegates focus, and nodes shadow roots 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 copys shadow roots declarative to nodes shadow roots declarative.
static_cast<Element&>(*copy).shadow_root()->set_declarative(node_shadow_root.declarative());
// 4. For each child child of nodes shadow root, in tree order:
// append the result of cloning child with document and the clone children flag set, to copys 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

View File

@ -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; }

View File

@ -632,7 +632,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.
@ -722,7 +722,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 nodes length minus original start offset.
@ -738,7 +738,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));
@ -761,7 +761,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));
@ -776,7 +776,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));
@ -961,7 +961,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.
@ -1026,7 +1026,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 nodes length minus original start offset.
@ -1039,7 +1039,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));
@ -1057,7 +1057,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)));
@ -1066,7 +1066,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));
@ -1078,7 +1078,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));

View File

@ -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)

View File

@ -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);

View File

@ -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();
}
}

View File

@ -715,7 +715,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());