LibWeb: Implement Node.replaceChild

The `if (child->parent())` check seems to be redundant, but I'm keeping
it just to match the spec.
This commit is contained in:
Luke 2021-05-07 00:52:23 +01:00 committed by Andreas Kling
parent 228c1f4968
commit 46f2c278b0
Notes: sideshowbarker 2024-07-18 18:37:36 +09:00
3 changed files with 62 additions and 0 deletions

View File

@ -1,6 +1,7 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -345,6 +346,64 @@ void Node::remove(bool suppress_observers)
parent->children_changed();
}
// https://dom.spec.whatwg.org/#concept-node-replace
ExceptionOr<NonnullRefPtr<Node>> Node::replace_child(NonnullRefPtr<Node> node, NonnullRefPtr<Node> child)
{
// NOTE: This differs slightly from ensure_pre_insertion_validity.
if (!is<Document>(this) && !is<DocumentFragment>(this) && !is<Element>(this))
return DOM::HierarchyRequestError::create("Can only insert into a document, document fragment or element");
if (node->is_host_including_inclusive_ancestor_of(*this))
return DOM::HierarchyRequestError::create("New node is an ancestor of this node");
if (child->parent() != this)
return DOM::NotFoundError::create("This node is not the parent of the given child");
// FIXME: All the following "Invalid node type for insertion" messages could be more descriptive.
if (!is<DocumentFragment>(*node) && !is<DocumentType>(*node) && !is<Element>(*node) && !is<Text>(*node) && !is<Comment>(*node) && !is<ProcessingInstruction>(*node))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
if ((is<Text>(*node) && is<Document>(this)) || (is<DocumentType>(*node) && !is<Document>(this)))
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
if (is<Document>(this)) {
if (is<DocumentFragment>(*node)) {
auto node_element_child_count = downcast<DocumentFragment>(*node).child_element_count();
if ((node_element_child_count > 1 || node->has_child_of_type<Text>())
|| (node_element_child_count == 1 && (first_child_of_type<Element>() != child /* FIXME: or a doctype is following child. */))) {
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
}
} else if (is<Element>(*node)) {
if (first_child_of_type<Element>() != child /* FIXME: or a doctype is following child. */)
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
} else if (is<DocumentType>(*node)) {
if (first_child_of_type<DocumentType>() != node /* FIXME: or an element is preceding child */)
return DOM::HierarchyRequestError::create("Invalid node type for insertion");
}
}
auto reference_child = child->next_sibling();
if (reference_child == node)
reference_child = node->next_sibling();
// FIXME: Let previousSibling be childs previous sibling. (Currently unused so not included)
// FIXME: Let removedNodes be the empty set. (Currently unused so not included)
if (child->parent()) {
// FIXME: Set removedNodes to « child ».
child->remove(true);
}
// FIXME: Let nodes be nodes children if node is a DocumentFragment node; otherwise « node ». (Currently unused so not included)
insert_before(node, reference_child, true);
// FIXME: Queue a tree mutation record for parent with nodes, removedNodes, previousSibling, and referenceChild.
return child;
}
// https://dom.spec.whatwg.org/#concept-node-clone
NonnullRefPtr<Node> Node::clone_node(Document* document, bool clone_children) const
{

View File

@ -82,6 +82,8 @@ public:
void remove_all_children(bool suppress_observers = false);
u16 compare_document_position(RefPtr<Node> other);
ExceptionOr<NonnullRefPtr<Node>> replace_child(NonnullRefPtr<Node> node, NonnullRefPtr<Node> child);
NonnullRefPtr<Node> clone_node(Document* document = nullptr, bool clone_children = false) const;
ExceptionOr<NonnullRefPtr<Node>> clone_node_binding(bool deep) const;

View File

@ -18,6 +18,7 @@ interface Node : EventTarget {
Node appendChild(Node node);
[ImplementedAs=pre_insert] Node insertBefore(Node node, Node? child);
Node replaceChild(Node node, Node child);
[ImplementedAs=pre_remove] Node removeChild(Node child);
[ImplementedAs=clone_node_binding] Node cloneNode(optional boolean deep = false);