LibWeb/DOM: Inherit Node from TreeNode

This allows to delete lots of tree helper functions duplicated between
Node and TreeNode.
This commit is contained in:
Aliaksandr Kalenik 2025-02-03 15:25:24 +01:00 committed by Andreas Kling
commit dfcee2bbdf
Notes: github-actions[bot] 2025-02-03 17:38:11 +00:00
2 changed files with 8 additions and 355 deletions

View file

@ -107,12 +107,8 @@ void Node::finalize()
void Node::visit_edges(Cell::Visitor& visitor) void Node::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
TreeNode::visit_edges(visitor);
visitor.visit(m_document); visitor.visit(m_document);
visitor.visit(m_parent);
visitor.visit(m_first_child);
visitor.visit(m_last_child);
visitor.visit(m_next_sibling);
visitor.visit(m_previous_sibling);
visitor.visit(m_child_nodes); visitor.visit(m_child_nodes);
visitor.visit(m_layout_node); visitor.visit(m_layout_node);
@ -2292,75 +2288,24 @@ void Node::queue_tree_mutation_record(Vector<GC::Root<Node>> added_nodes, Vector
void Node::append_child_impl(GC::Ref<Node> node) void Node::append_child_impl(GC::Ref<Node> node)
{ {
VERIFY(!node->m_parent); VERIFY(!node->parent());
if (!is_child_allowed(*node)) if (!is_child_allowed(*node))
return; return;
if (m_last_child) TreeNode::append_child(node);
m_last_child->m_next_sibling = node.ptr();
node->m_previous_sibling = m_last_child;
node->m_parent = this;
m_last_child = node.ptr();
if (!m_first_child)
m_first_child = m_last_child;
} }
void Node::insert_before_impl(GC::Ref<Node> node, GC::Ptr<Node> child) void Node::insert_before_impl(GC::Ref<Node> node, GC::Ptr<Node> child)
{ {
if (!child) if (!child)
return append_child_impl(move(node)); return append_child_impl(move(node));
TreeNode::insert_before(node, child);
VERIFY(!node->m_parent);
VERIFY(child->parent() == this);
node->m_previous_sibling = child->m_previous_sibling;
node->m_next_sibling = child;
if (child->m_previous_sibling)
child->m_previous_sibling->m_next_sibling = node;
if (m_first_child == child)
m_first_child = node;
child->m_previous_sibling = node;
node->m_parent = this;
} }
void Node::remove_child_impl(GC::Ref<Node> node) void Node::remove_child_impl(GC::Ref<Node> node)
{ {
VERIFY(node->m_parent.ptr() == this); TreeNode::remove_child(node);
if (m_first_child == node)
m_first_child = node->m_next_sibling;
if (m_last_child == node)
m_last_child = node->m_previous_sibling;
if (node->m_next_sibling)
node->m_next_sibling->m_previous_sibling = node->m_previous_sibling;
if (node->m_previous_sibling)
node->m_previous_sibling->m_next_sibling = node->m_next_sibling;
node->m_next_sibling = nullptr;
node->m_previous_sibling = nullptr;
node->m_parent = nullptr;
}
bool Node::is_ancestor_of(Node const& other) const
{
for (auto* ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) {
if (ancestor == this)
return true;
}
return false;
}
bool Node::is_inclusive_ancestor_of(Node const& other) const
{
return &other == this || is_ancestor_of(other);
} }
bool Node::is_descendant_of(Node const& other) const bool Node::is_descendant_of(Node const& other) const

View file

@ -6,12 +6,10 @@
#pragma once #pragma once
#include <AK/Badge.h>
#include <AK/DistinctNumeric.h> #include <AK/DistinctNumeric.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/GenericShorthands.h> #include <AK/GenericShorthands.h>
#include <AK/JsonObjectSerializer.h> #include <AK/JsonObjectSerializer.h>
#include <AK/RefPtr.h>
#include <AK/TypeCasts.h> #include <AK/TypeCasts.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibWeb/CSS/InvalidationSet.h> #include <LibWeb/CSS/InvalidationSet.h>
@ -20,6 +18,7 @@
#include <LibWeb/DOM/Slottable.h> #include <LibWeb/DOM/Slottable.h>
#include <LibWeb/DOMParsing/XMLSerializer.h> #include <LibWeb/DOMParsing/XMLSerializer.h>
#include <LibWeb/TraversalDecision.h> #include <LibWeb/TraversalDecision.h>
#include <LibWeb/TreeNode.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::DOM { namespace Web::DOM {
@ -104,7 +103,8 @@ enum class StyleInvalidationReason {
#undef __ENUMERATE_STYLE_INVALIDATION_REASON #undef __ENUMERATE_STYLE_INVALIDATION_REASON
}; };
class Node : public EventTarget { class Node : public EventTarget
, public TreeNode<Node> {
WEB_PLATFORM_OBJECT(Node, EventTarget); WEB_PLATFORM_OBJECT(Node, EventTarget);
public: public:
@ -381,19 +381,6 @@ public:
Slottable as_slottable(); Slottable as_slottable();
Node* parent() { return m_parent.ptr(); }
Node const* parent() const { return m_parent.ptr(); }
bool has_children() const { return m_first_child; }
Node* next_sibling() { return m_next_sibling.ptr(); }
Node* previous_sibling() { return m_previous_sibling.ptr(); }
Node* first_child() { return m_first_child.ptr(); }
Node* last_child() { return m_last_child.ptr(); }
Node const* next_sibling() const { return m_next_sibling.ptr(); }
Node const* previous_sibling() const { return m_previous_sibling.ptr(); }
Node const* first_child() const { return m_first_child.ptr(); }
Node const* last_child() const { return m_last_child.ptr(); }
size_t child_count() const size_t child_count() const
{ {
size_t count = 0; size_t count = 0;
@ -418,80 +405,11 @@ public:
return const_cast<Node*>(this)->child_at_index(index); return const_cast<Node*>(this)->child_at_index(index);
} }
// https://dom.spec.whatwg.org/#concept-tree-index
size_t index() const
{
// The index of an object is its number of preceding siblings, or 0 if it has none.
size_t index = 0;
for (auto* node = previous_sibling(); node; node = node->previous_sibling())
++index;
return index;
}
bool is_ancestor_of(Node const&) const;
bool is_inclusive_ancestor_of(Node const&) const;
bool is_descendant_of(Node const&) const; bool is_descendant_of(Node const&) const;
bool is_inclusive_descendant_of(Node const&) const; bool is_inclusive_descendant_of(Node const&) const;
bool is_following(Node const&) const; bool is_following(Node const&) const;
Node* next_in_pre_order()
{
if (first_child())
return first_child();
Node* node;
if (!(node = next_sibling())) {
node = parent();
while (node && !node->next_sibling())
node = node->parent();
if (node)
node = node->next_sibling();
}
return node;
}
Node* next_in_pre_order(Node const* stay_within)
{
if (first_child())
return first_child();
Node* node = static_cast<Node*>(this);
Node* next = nullptr;
while (!(next = node->next_sibling())) {
node = node->parent();
if (!node || node == stay_within)
return nullptr;
}
return next;
}
Node const* next_in_pre_order() const
{
return const_cast<Node*>(this)->next_in_pre_order();
}
Node const* next_in_pre_order(Node const* stay_within) const
{
return const_cast<Node*>(this)->next_in_pre_order(stay_within);
}
Node* previous_in_pre_order()
{
if (auto* node = previous_sibling()) {
while (node->last_child())
node = node->last_child();
return node;
}
return parent();
}
Node const* previous_in_pre_order() const
{
return const_cast<Node*>(this)->previous_in_pre_order();
}
bool is_before(Node const& other) const bool is_before(Node const& other) const
{ {
if (this == &other) if (this == &other)
@ -525,98 +443,6 @@ public:
return false; return false;
} }
template<typename Callback>
TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
{
if (auto decision = callback(static_cast<Node const&>(*this)); decision != TraversalDecision::Continue)
return decision;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename Callback>
TraversalDecision for_each_in_inclusive_subtree(Callback callback)
{
if (auto decision = callback(static_cast<Node&>(*this)); decision != TraversalDecision::Continue)
return decision;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback)
{
if (auto* maybe_node_of_type = as_if<U>(static_cast<Node&>(*this))) {
if (auto decision = callback(*maybe_node_of_type); decision != TraversalDecision::Continue)
return decision;
}
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
{
if (auto* maybe_node_of_type = as_if<U>(static_cast<Node const&>(*this))) {
if (auto decision = callback(*maybe_node_of_type); decision != TraversalDecision::Continue)
return decision;
}
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename Callback>
TraversalDecision for_each_in_subtree(Callback callback) const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename Callback>
TraversalDecision for_each_in_subtree(Callback callback)
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
TraversalDecision for_each_in_subtree_of_type(Callback callback)
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
TraversalDecision for_each_in_subtree_of_type(Callback callback) const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename Callback> template<typename Callback>
void for_each_ancestor(Callback callback) const void for_each_ancestor(Callback callback) const
{ {
@ -647,38 +473,6 @@ public:
} }
} }
template<typename Callback>
void for_each_child(Callback callback) const
{
return const_cast<Node*>(this)->for_each_child(move(callback));
}
template<typename Callback>
void for_each_child(Callback callback)
{
for (auto* node = first_child(); node; node = node->next_sibling()) {
if (callback(*node) == IterationDecision::Break)
return;
}
}
template<typename U, typename Callback>
void for_each_child_of_type(Callback callback)
{
for (auto* node = first_child(); node; node = node->next_sibling()) {
if (auto* maybe_child_of_type = as_if<U>(node)) {
if (callback(*maybe_child_of_type) == IterationDecision::Break)
return;
}
}
}
template<typename U, typename Callback>
void for_each_child_of_type(Callback callback) const
{
return const_cast<Node*>(this)->template for_each_child_of_type<U>(move(callback));
}
template<typename U, typename Callback> template<typename U, typename Callback>
WebIDL::ExceptionOr<void> for_each_child_of_type_fallible(Callback callback) WebIDL::ExceptionOr<void> for_each_child_of_type_fallible(Callback callback)
{ {
@ -691,92 +485,12 @@ public:
return {}; return {};
} }
template<typename U>
U const* next_sibling_of_type() const
{
return const_cast<Node*>(this)->template next_sibling_of_type<U>();
}
template<typename U>
inline U* next_sibling_of_type()
{
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
if (auto* maybe_sibling_of_type = as_if<U>(*sibling))
return maybe_sibling_of_type;
}
return nullptr;
}
template<typename U>
U const* previous_sibling_of_type() const
{
return const_cast<Node*>(this)->template previous_sibling_of_type<U>();
}
template<typename U>
U* previous_sibling_of_type()
{
for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
if (auto* maybe_sibling_of_type = as_if<U>(*sibling))
return maybe_sibling_of_type;
}
return nullptr;
}
template<typename U>
U const* first_child_of_type() const
{
return const_cast<Node*>(this)->template first_child_of_type<U>();
}
template<typename U>
U const* last_child_of_type() const
{
return const_cast<Node*>(this)->template last_child_of_type<U>();
}
template<typename U>
U* first_child_of_type()
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (auto* maybe_child_of_type = as_if<U>(*child))
return maybe_child_of_type;
}
return nullptr;
}
template<typename U>
U* last_child_of_type()
{
for (auto* child = last_child(); child; child = child->previous_sibling()) {
if (auto* maybe_child_of_type = as_if<U>(*child))
return maybe_child_of_type;
}
return nullptr;
}
template<typename U> template<typename U>
bool has_child_of_type() const bool has_child_of_type() const
{ {
return first_child_of_type<U>() != nullptr; return first_child_of_type<U>() != nullptr;
} }
template<typename U>
U const* first_ancestor_of_type() const
{
return const_cast<Node*>(this)->template first_ancestor_of_type<U>();
}
template<typename U>
U* first_ancestor_of_type()
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (auto* maybe_ancestor_of_type = as_if<U>(*ancestor))
return maybe_ancestor_of_type;
}
return nullptr;
}
template<typename U> template<typename U>
U const* shadow_including_first_ancestor_of_type() const U const* shadow_including_first_ancestor_of_type() const
{ {
@ -843,12 +557,6 @@ private:
static Optional<StringView> first_valid_id(StringView, Document const&); static Optional<StringView> first_valid_id(StringView, Document const&);
GC::Ptr<Node> m_parent;
GC::Ptr<Node> m_first_child;
GC::Ptr<Node> m_last_child;
GC::Ptr<Node> m_next_sibling;
GC::Ptr<Node> m_previous_sibling;
GC::Ptr<NodeList> m_child_nodes; GC::Ptr<NodeList> m_child_nodes;
}; };