diff options
author | Andreas Kling <kling@serenityos.org> | 2021-09-13 12:54:24 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-09-13 12:54:24 +0200 |
commit | 449cbd5ecc3eb8a40ecc830fcab648e7111676d5 (patch) | |
tree | 20f9a8f6d1e5a32be48a9782a78bc10430c8084a | |
parent | a0528598b56383d1c8a7db7d1cc4d6365dd87259 (diff) | |
download | serenity-449cbd5ecc3eb8a40ecc830fcab648e7111676d5.zip |
LibWeb: Implement Node.isEqualNode(Node? otherNode)
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Element.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Node.cpp | 84 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Node.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Node.idl | 1 |
4 files changed, 87 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index c6bc944399..8f7edebc00 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -52,6 +52,7 @@ public: String get_attribute(const FlyString& name) const { return attribute(name); } ExceptionOr<void> set_attribute(const FlyString& name, const String& value); void remove_attribute(const FlyString& name); + size_t attribute_list_size() const { return m_attributes.size(); } template<typename Callback> void for_each_attribute(Callback callback) const diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp index 2730a47223..b5367692eb 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.cpp +++ b/Userland/Libraries/LibWeb/DOM/Node.cpp @@ -774,4 +774,88 @@ bool Node::is_same_node(Node const* other_node) const return this == other_node; } +// https://dom.spec.whatwg.org/#dom-node-isequalnode +bool Node::is_equal_node(Node const* other_node) const +{ + // The isEqualNode(otherNode) method steps are to return true if otherNode is non-null and this equals otherNode; otherwise false. + if (!other_node) + return false; + + // Fast path for testing a node against itself. + if (this == other_node) + return true; + + // A node A equals a node B if all of the following conditions are true: + + // A and B implement the same interfaces. + if (node_name() != other_node->node_name()) + return false; + + // The following are equal, switching on the interface A implements: + switch (node_type()) { + case (u16)NodeType::DOCUMENT_TYPE_NODE: { + // Its name, public ID, and system ID. + auto& this_doctype = verify_cast<DocumentType>(*this); + auto& other_doctype = verify_cast<DocumentType>(*other_node); + if (this_doctype.name() != other_doctype.name() + || this_doctype.public_id() != other_doctype.public_id() + || this_doctype.system_id() != other_doctype.system_id()) + return false; + break; + } + case (u16)NodeType::ELEMENT_NODE: { + // Its namespace, namespace prefix, local name, and its attribute listโs size. + auto& this_element = verify_cast<Element>(*this); + auto& other_element = verify_cast<Element>(*other_node); + if (this_element.namespace_() != other_element.namespace_() + || this_element.prefix() != other_element.prefix() + || this_element.local_name() != other_element.local_name() + || this_element.attribute_list_size() != other_element.attribute_list_size()) + return false; + // If A is an element, each attribute in its attribute list has an attribute that equals an attribute in Bโs attribute list. + bool has_same_attributes = true; + this_element.for_each_attribute([&](auto& name, auto& value) { + if (other_element.get_attribute(name) != value) + has_same_attributes = false; + }); + if (!has_same_attributes) + return false; + break; + } + case (u16)NodeType::COMMENT_NODE: + case (u16)NodeType::TEXT_NODE: { + // Its data. + auto& this_cdata = verify_cast<CharacterData>(*this); + auto& other_cdata = verify_cast<CharacterData>(*other_node); + if (this_cdata.data() != other_cdata.data()) + return false; + break; + } + case (u16)NodeType::PROCESSING_INSTRUCTION_NODE: + case (u16)NodeType::ATTRIBUTE_NODE: + TODO(); + default: + break; + } + + // A and B have the same number of children. + size_t this_child_count = child_count(); + size_t other_child_count = other_node->child_count(); + if (this_child_count != other_child_count) + return false; + + // Each child of A equals the child of B at the identical index. + // FIXME: This can be made nicer. child_at_index() is O(n). + for (size_t i = 0; i < this_child_count; ++i) { + auto* this_child = child_at_index(i); + auto* other_child = other_node->child_at_index(i); + VERIFY(this_child); + VERIFY(other_child); + if (!this_child->is_equal_node(other_child)) + return false; + } + + return true; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h index eb2f6747f1..22266eeafa 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.h +++ b/Userland/Libraries/LibWeb/DOM/Node.h @@ -187,6 +187,7 @@ public: void string_replace_all(String const&); bool is_same_node(Node const*) const; + bool is_equal_node(Node const*) const; protected: Node(Document&, NodeType); diff --git a/Userland/Libraries/LibWeb/DOM/Node.idl b/Userland/Libraries/LibWeb/DOM/Node.idl index 419793432f..a1a15d6baa 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.idl +++ b/Userland/Libraries/LibWeb/DOM/Node.idl @@ -26,6 +26,7 @@ interface Node : EventTarget { [ImplementedAs=pre_remove] Node removeChild(Node child); [ImplementedAs=clone_node_binding] Node cloneNode(optional boolean deep = false); boolean contains(Node? other); + boolean isEqualNode(Node? otherNode); boolean isSameNode(Node? otherNode); const unsigned short ELEMENT_NODE = 1; |