LibWeb/DOM: Don't use recursion for subtree traversal in Node.h

This change is mainly motivated by the fact that iterating in a loop
makes profiles easier to read and understand where time was spent in
traversal callback. Additionally, using a loop reduces function call
overhead and ensures constant stack usage.
This commit is contained in:
Aliaksandr Kalenik 2025-02-03 15:59:13 +01:00 committed by Andreas Kling
commit f5ba22d3e8
Notes: github-actions[bot] 2025-02-03 17:38:04 +00:00

View file

@ -15,6 +15,37 @@
namespace Web { namespace Web {
template<typename T, typename Callback>
TraversalDecision traverse_preorder(T root, Callback callback)
{
T current = root;
while (current != nullptr) {
TraversalDecision decision = callback(*current);
if (decision == TraversalDecision::Break)
return TraversalDecision::Break;
if (decision != TraversalDecision::SkipChildrenAndContinue && current->first_child() != nullptr) {
current = current->first_child();
continue;
}
if (current == root)
break;
if (current->next_sibling() != nullptr) {
current = current->next_sibling();
continue;
}
while (current != root && current->next_sibling() == nullptr)
current = current->parent();
if (current == root)
break;
current = current->next_sibling();
}
return TraversalDecision::Continue;
}
template<typename T> template<typename T>
class TreeNode { class TreeNode {
public: public:
@ -116,53 +147,33 @@ public:
template<typename Callback> template<typename Callback>
TraversalDecision for_each_in_inclusive_subtree(Callback callback) const TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
{ {
if (auto decision = callback(static_cast<T const&>(*this)); decision != TraversalDecision::Continue) return traverse_preorder(static_cast<T const*>(this), callback);
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> template<typename Callback>
TraversalDecision for_each_in_inclusive_subtree(Callback callback) TraversalDecision for_each_in_inclusive_subtree(Callback callback)
{ {
if (auto decision = callback(static_cast<T&>(*this)); decision != TraversalDecision::Continue) return traverse_preorder(static_cast<T*>(this), callback);
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> template<typename U, typename Callback>
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback)
{ {
if (is<U>(static_cast<T const&>(*this))) { return for_each_in_inclusive_subtree([callback = move(callback)](T& node) {
if (auto decision = callback(static_cast<U&>(*this)); decision != TraversalDecision::Continue) if (auto* maybe_node_of_type = as_if<U>(node))
return decision; return callback(*maybe_node_of_type);
} return TraversalDecision::Continue;
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> template<typename U, typename Callback>
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
{ {
if (is<U>(static_cast<T const&>(*this))) { return for_each_in_inclusive_subtree([callback = move(callback)](T const& node) {
if (auto decision = callback(static_cast<U const&>(*this)); decision != TraversalDecision::Continue) if (auto* maybe_node_of_type = as_if<U>(node))
return decision; return callback(*maybe_node_of_type);
} return TraversalDecision::Continue;
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>