1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 11:37:44 +00:00

LibWeb: Implement Node.isEqualNode(Node? otherNode)

This commit is contained in:
Andreas Kling 2021-09-13 12:54:24 +02:00
parent a0528598b5
commit 449cbd5ecc
4 changed files with 87 additions and 0 deletions

View file

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