summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-09-13 12:54:24 +0200
committerAndreas Kling <kling@serenityos.org>2021-09-13 12:54:24 +0200
commit449cbd5ecc3eb8a40ecc830fcab648e7111676d5 (patch)
tree20f9a8f6d1e5a32be48a9782a78bc10430c8084a
parenta0528598b56383d1c8a7db7d1cc4d6365dd87259 (diff)
downloadserenity-449cbd5ecc3eb8a40ecc830fcab648e7111676d5.zip
LibWeb: Implement Node.isEqualNode(Node? otherNode)
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.h1
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.cpp84
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.h1
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.idl1
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;