LibWeb: Move non-DOM-related methods from DOM::Node to TreeNode

Motivated by wanting to do `is_before()` on a Layout::Node, and thought
I might as well move as many as possible while I was at it.
This commit is contained in:
Sam Atkins 2025-06-07 13:01:04 +01:00 committed by Andreas Kling
commit ea33bdc975
Notes: github-actions[bot] 2025-06-07 14:51:50 +00:00
3 changed files with 154 additions and 156 deletions

View file

@ -575,17 +575,6 @@ String Node::child_text_content() const
return MUST(builder.to_string());
}
// https://dom.spec.whatwg.org/#concept-tree-root
Node& Node::root()
{
// The root of an object is itself, if its parent is null, or else it is the root of its parent.
// The root of a tree is any object participating in that tree whose parent is null.
Node* root = this;
while (root->parent())
root = root->parent();
return *root;
}
// https://dom.spec.whatwg.org/#concept-shadow-including-root
Node& Node::shadow_including_root()
{
@ -1977,13 +1966,6 @@ bool Node::is_scripting_disabled() const
return !is_scripting_enabled();
}
// https://dom.spec.whatwg.org/#dom-node-contains
bool Node::contains(GC::Ptr<Node> other) const
{
// The contains(other) method steps are to return true if other is an inclusive descendant of this; otherwise false (including when other is null).
return other && other->is_inclusive_descendant_of(*this);
}
// https://dom.spec.whatwg.org/#concept-shadow-including-descendant
bool Node::is_shadow_including_descendant_of(Node const& other) const
{
@ -2588,28 +2570,6 @@ void Node::remove_child_impl(GC::Ref<Node> node)
TreeNode::remove_child(node);
}
bool Node::is_descendant_of(Node const& other) const
{
return other.is_ancestor_of(*this);
}
bool Node::is_inclusive_descendant_of(Node const& other) const
{
return other.is_inclusive_ancestor_of(*this);
}
// https://dom.spec.whatwg.org/#concept-tree-following
bool Node::is_following(Node const& other) const
{
// An object A is following an object B if A and B are in the same tree and A comes after B in tree order.
for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
if (node == &other)
return true;
}
return false;
}
void Node::build_accessibility_tree(AccessibilityTreeNode& parent)
{
if (is_uninteresting_whitespace_node())

View file

@ -280,12 +280,6 @@ public:
String child_text_content() const;
Node& root();
Node const& root() const
{
return const_cast<Node*>(this)->root();
}
Node& shadow_including_root();
Node const& shadow_including_root() const
{
@ -376,8 +370,6 @@ public:
bool is_scripting_enabled() const;
bool is_scripting_disabled() const;
bool contains(GC::Ptr<Node>) const;
// Used for dumping the DOM Tree
void serialize_tree_as_json(JsonObjectSerializer<StringBuilder>&) const;
@ -424,98 +416,6 @@ public:
Slottable as_slottable();
size_t child_count() const
{
size_t count = 0;
for (auto* child = first_child(); child; child = child->next_sibling())
++count;
return count;
}
Node* child_at_index(int index)
{
int count = 0;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (count == index)
return child;
++count;
}
return nullptr;
}
Node const* child_at_index(int index) const
{
return const_cast<Node*>(this)->child_at_index(index);
}
bool is_descendant_of(Node const&) const;
bool is_inclusive_descendant_of(Node const&) const;
bool is_following(Node const&) const;
bool is_before(Node const& other) const
{
if (this == &other)
return false;
for (auto* node = this; node; node = node->next_in_pre_order()) {
if (node == &other)
return true;
}
return false;
}
// https://dom.spec.whatwg.org/#concept-tree-preceding (Object A is 'typename U' and Object B is 'this')
template<typename U>
bool has_preceding_node_of_type_in_tree_order() const
{
for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
if (is<U>(node))
return true;
}
return false;
}
// https://dom.spec.whatwg.org/#concept-tree-following (Object A is 'typename U' and Object B is 'this')
template<typename U>
bool has_following_node_of_type_in_tree_order() const
{
for (auto* node = next_in_pre_order(); node; node = node->next_in_pre_order()) {
if (is<U>(node))
return true;
}
return false;
}
template<typename Callback>
void for_each_ancestor(Callback callback) const
{
return const_cast<Node*>(this)->for_each_ancestor(move(callback));
}
template<typename Callback>
void for_each_ancestor(Callback callback)
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (callback(*ancestor) == IterationDecision::Break)
break;
}
}
template<typename Callback>
void for_each_inclusive_ancestor(Callback callback) const
{
return const_cast<Node*>(this)->for_each_inclusive_ancestor(move(callback));
}
template<typename Callback>
void for_each_inclusive_ancestor(Callback callback)
{
for (auto* ancestor = this; ancestor; ancestor = ancestor->parent()) {
if (callback(*ancestor) == IterationDecision::Break)
break;
}
}
template<typename U, typename Callback>
WebIDL::ExceptionOr<void> for_each_child_of_type_fallible(Callback callback)
{
@ -527,13 +427,6 @@ public:
}
return {};
}
template<typename U>
bool has_child_of_type() const
{
return first_child_of_type<U>() != nullptr;
}
template<typename U>
U const* shadow_including_first_ancestor_of_type() const
{
@ -543,15 +436,6 @@ public:
template<typename U>
U* shadow_including_first_ancestor_of_type();
bool is_parent_of(Node const& other) const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (&other == child)
return true;
}
return false;
}
ErrorOr<String> accessible_name(Document const&, ShouldComputeRole = ShouldComputeRole::Yes) const;
ErrorOr<String> accessible_description(Document const&) const;

View file

@ -72,8 +72,57 @@ public:
return index;
}
// // https://dom.spec.whatwg.org/#concept-tree-root
T& root()
{
// The root of an object is itself, if its parent is null, or else it is the root of its parent.
// The root of a tree is any object participating in that tree whose parent is null.
T* root = static_cast<T*>(this);
while (root->parent())
root = root->parent();
return *root;
}
T const& root() const { return const_cast<TreeNode*>(this)->root(); }
bool is_ancestor_of(TreeNode const&) const;
bool is_inclusive_ancestor_of(TreeNode const&) const;
bool contains(GC::Ptr<T>) const;
bool is_descendant_of(TreeNode const&) const;
bool is_inclusive_descendant_of(TreeNode const&) const;
bool is_following(TreeNode const&) const;
bool is_before(TreeNode const&) const;
// https://dom.spec.whatwg.org/#concept-tree-preceding (Object A is 'typename U' and Object B is 'this')
template<typename U>
bool has_preceding_node_of_type_in_tree_order() const
{
for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
if (is<U>(node))
return true;
}
return false;
}
// https://dom.spec.whatwg.org/#concept-tree-following (Object A is 'typename U' and Object B is 'this')
template<typename U>
bool has_following_node_of_type_in_tree_order() const
{
for (auto* node = next_in_pre_order(); node; node = node->next_in_pre_order()) {
if (is<U>(node))
return true;
}
return false;
}
bool is_parent_of(TreeNode const& other) const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (&other == child)
return true;
}
return false;
}
void append_child(GC::Ref<T> node);
void prepend_child(GC::Ref<T> node);
@ -87,6 +136,30 @@ public:
m_parent->remove_child(*static_cast<T*>(this));
}
size_t child_count() const
{
size_t count = 0;
for (auto* child = first_child(); child; child = child->next_sibling())
++count;
return count;
}
T* child_at_index(int index)
{
int count = 0;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (count == index)
return child;
++count;
}
return nullptr;
}
T const* child_at_index(int index) const
{
return const_cast<TreeNode*>(this)->child_at_index(index);
}
T* next_in_pre_order()
{
if (first_child())
@ -280,6 +353,12 @@ public:
return nullptr;
}
template<typename U>
bool has_child_of_type() const
{
return first_child_of_type<U>() != nullptr;
}
template<typename U>
U const* first_child_of_type() const
{
@ -328,6 +407,36 @@ public:
return nullptr;
}
template<typename Callback>
void for_each_ancestor(Callback callback) const
{
return const_cast<TreeNode*>(this)->for_each_ancestor(move(callback));
}
template<typename Callback>
void for_each_ancestor(Callback callback)
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (callback(static_cast<T&>(*ancestor)) == IterationDecision::Break)
break;
}
}
template<typename Callback>
void for_each_inclusive_ancestor(Callback callback) const
{
return const_cast<TreeNode*>(this)->for_each_inclusive_ancestor(move(callback));
}
template<typename Callback>
void for_each_inclusive_ancestor(Callback callback)
{
for (auto* ancestor = this; ancestor; ancestor = ancestor->parent()) {
if (callback(static_cast<T&>(*ancestor)) == IterationDecision::Break)
break;
}
}
~TreeNode() = default;
protected:
@ -464,4 +573,49 @@ inline bool TreeNode<T>::is_inclusive_ancestor_of(TreeNode<T> const& other) cons
return &other == this || is_ancestor_of(other);
}
// https://dom.spec.whatwg.org/#dom-node-contains
template<typename T>
inline bool TreeNode<T>::contains(GC::Ptr<T> other) const
{
// The contains(other) method steps are to return true if other is an inclusive descendant of this; otherwise false (including when other is null).
return other && other->is_inclusive_descendant_of(*this);
}
template<typename T>
inline bool TreeNode<T>::is_descendant_of(TreeNode<T> const& other) const
{
return other.is_ancestor_of(*this);
}
template<typename T>
inline bool TreeNode<T>::is_inclusive_descendant_of(TreeNode<T> const& other) const
{
return other.is_inclusive_ancestor_of(*this);
}
// https://dom.spec.whatwg.org/#concept-tree-following
template<typename T>
inline bool TreeNode<T>::is_following(TreeNode const& other) const
{
// An object A is following an object B if A and B are in the same tree and A comes after B in tree order.
for (auto* node = previous_in_pre_order(); node; node = node->previous_in_pre_order()) {
if (node == &other)
return true;
}
return false;
}
template<typename T>
inline bool TreeNode<T>::is_before(TreeNode const& other) const
{
if (this == &other)
return false;
for (auto* node = this; node; node = node->next_in_pre_order()) {
if (node == &other)
return true;
}
return false;
}
}