LibWeb: Use TraversalDecision for multi level Node traversal methods

This adds the `SkipChildrenAndContinue` option, where traversal
continues but child nodes are not included.
This commit is contained in:
Tim Ledbetter 2024-05-04 14:47:04 +01:00 committed by Andrew Kaster
commit 398bf10b92
Notes: sideshowbarker 2024-07-17 08:59:18 +09:00
33 changed files with 229 additions and 215 deletions

View file

@ -751,7 +751,7 @@ JS::GCPtr<HTML::HTMLTitleElement> Document::title_element()
for_each_in_subtree_of_type<HTML::HTMLTitleElement>([&](auto& title_element_in_tree) {
title_element = title_element_in_tree;
return IterationDecision::Break;
return TraversalDecision::Break;
});
return title_element;
@ -931,10 +931,10 @@ void Document::update_base_element(Badge<HTML::HTMLBaseElement>)
for_each_in_subtree_of_type<HTML::HTMLBaseElement>([&base_element](HTML::HTMLBaseElement const& base_element_in_tree) {
if (base_element_in_tree.has_attribute(HTML::AttributeNames::href)) {
base_element = &base_element_in_tree;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
m_first_base_element_with_href_in_tree_order = base_element;
@ -1757,7 +1757,7 @@ void Document::adopt_node(Node& node)
// FIXME: 2. If inclusiveDescendant is an element, then set the node document of each attribute in inclusiveDescendants
// attribute list to document.
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 2. For each inclusiveDescendant in nodes shadow-including inclusive descendants that is custom,
@ -1765,7 +1765,7 @@ void Document::adopt_node(Node& node)
// and an argument list containing oldDocument and document.
node.for_each_shadow_including_inclusive_descendant([&](DOM::Node& inclusive_descendant) {
if (!is<DOM::Element>(inclusive_descendant))
return IterationDecision::Continue;
return TraversalDecision::Continue;
auto& element = static_cast<DOM::Element&>(inclusive_descendant);
if (element.is_custom()) {
@ -1778,14 +1778,14 @@ void Document::adopt_node(Node& node)
element.enqueue_a_custom_element_callback_reaction(HTML::CustomElementReactionNames::adoptedCallback, move(arguments));
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 3. For each inclusiveDescendant in nodes shadow-including inclusive descendants, in shadow-including tree order,
// run the adopting steps with inclusiveDescendant and oldDocument.
node.for_each_shadow_including_inclusive_descendant([&](auto& inclusive_descendant) {
inclusive_descendant.adopted_from(old_document);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// Transfer NodeIterators rooted at `node` from old_document to this document.
@ -1951,9 +1951,9 @@ Element* Document::find_a_potential_indicated_element(FlyString const& fragment)
root().for_each_in_subtree_of_type<Element>([&](Element const& element) {
if (element.name() == fragment) {
element_with_name = const_cast<Element*>(&element);
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
if (element_with_name)
return element_with_name;
@ -2959,12 +2959,12 @@ Vector<JS::Handle<HTML::Navigable>> Document::descendant_navigables()
auto& navigable_container = static_cast<HTML::NavigableContainer&>(node);
// 1. If navigableContainer's content navigable is null, then continue.
if (!navigable_container.content_navigable())
return IterationDecision::Continue;
return TraversalDecision::Continue;
// 2. Extend navigables with navigableContainer's content navigable's active document's inclusive descendant navigables.
navigables.extend(navigable_container.content_navigable()->active_document()->inclusive_descendant_navigables());
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 4. Return navigables.
@ -3041,10 +3041,10 @@ Vector<JS::Handle<HTML::Navigable>> Document::document_tree_child_navigables()
for_each_in_subtree_of_type<HTML::NavigableContainer>([&](HTML::NavigableContainer& navigable_container) {
// 1. If navigableContainer's content navigable is null, then continue.
if (!navigable_container.content_navigable())
return IterationDecision::Continue;
return TraversalDecision::Continue;
// 2. Append navigableContainer's content navigable to navigables.
navigables.append(*navigable_container.content_navigable());
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 5. Return navigables.
@ -4509,9 +4509,9 @@ Element const* Document::element_from_point(double x, double y)
auto* dom_node = result.dom_node();
if (dom_node && dom_node->is_element()) {
hit_test_result = result;
return Painting::TraversalDecision::Break;
return TraversalDecision::Break;
}
return Painting::TraversalDecision::Continue;
return TraversalDecision::Continue;
});
}
if (hit_test_result.has_value())
@ -4551,7 +4551,7 @@ Vector<JS::NonnullGCPtr<Element>> Document::elements_from_point(double x, double
auto* dom_node = result.dom_node();
if (dom_node && dom_node->is_element())
sequence.append(*static_cast<Element*>(dom_node));
return Painting::TraversalDecision::Continue;
return TraversalDecision::Continue;
});
}

View file

@ -2290,11 +2290,11 @@ Element::Directionality Element::directionality() const
// Discard not-allowed ancestors
for (auto* ancestor = text_node.parent(); ancestor && ancestor != this; ancestor = ancestor->parent()) {
if (is<HTML::HTMLScriptElement>(*ancestor) || is<HTML::HTMLStyleElement>(*ancestor) || is<HTML::HTMLTextAreaElement>(*ancestor))
return IterationDecision::Continue;
return TraversalDecision::Continue;
if (ancestor->is_element()) {
auto ancestor_element = static_cast<Element const*>(ancestor);
if (ancestor_element->dir().has_value())
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
}
@ -2304,11 +2304,11 @@ Element::Directionality Element::directionality() const
if (first_is_one_of(bidi_class, bidirectional_class_L, bidirectional_class_AL, bidirectional_class_R)) {
found_character = code_point;
found_character_bidi_class = bidi_class;
return IterationDecision::Break;
return TraversalDecision::Break;
}
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// If such a character is found and it is of bidirectional character type AL or R,

View file

@ -61,7 +61,7 @@ void HTMLCollection::update_cache_if_needed() const
m_root->for_each_in_subtree_of_type<Element>([&](auto& element) {
if (m_filter(element))
m_cached_elements.append(element);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
} else {
m_root->for_each_child_of_type<Element>([&](auto& element) {

View file

@ -42,7 +42,7 @@ JS::MarkedVector<Node*> LiveNodeList::collection() const
m_root->for_each_in_subtree([&](auto& node) {
if (m_filter(node))
nodes.append(const_cast<Node*>(&node));
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
} else {
m_root->for_each_child([&](auto& node) {
@ -61,9 +61,9 @@ Node* LiveNodeList::first_matching(Function<bool(Node const&)> const& filter) co
m_root->for_each_in_subtree([&](auto& node) {
if (m_filter(node) && filter(node)) {
matched_node = const_cast<Node*>(&node);
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
} else {
m_root->for_each_child([&](auto& node) {

View file

@ -149,7 +149,7 @@ String Node::descendant_text_content() const
StringBuilder builder;
for_each_in_subtree_of_type<Text>([&](auto& text_node) {
builder.append(text_node.data());
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return builder.to_string_without_validation();
}
@ -281,7 +281,7 @@ void Node::invalidate_style()
if (shadow_root->has_children())
shadow_root->m_child_needs_style_update = true;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host())
ancestor->m_child_needs_style_update = true;
@ -507,7 +507,7 @@ void Node::insert_before(JS::NonnullGCPtr<Node> node, JS::GCPtr<Node> child, boo
}
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}
@ -646,7 +646,7 @@ void Node::remove(bool suppress_observers)
for_each_in_inclusive_subtree_of_type<HTML::HTMLSlotElement>([&](auto const&) {
has_descendent_slot = true;
return IterationDecision::Break;
return TraversalDecision::Break;
});
if (has_descendent_slot) {
@ -692,7 +692,7 @@ void Node::remove(bool suppress_observers)
}
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 19. For each inclusive ancestor inclusiveAncestor of parent, and then for each registered of inclusiveAncestors registered observer list,

View file

@ -16,6 +16,7 @@
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOM/Slottable.h>
#include <LibWeb/DOMParsing/XMLSerializer.h>
#include <LibWeb/TraversalDecision.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::DOM {
@ -274,11 +275,11 @@ public:
// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-descendant
template<typename Callback>
IterationDecision for_each_shadow_including_inclusive_descendant(Callback);
TraversalDecision for_each_shadow_including_inclusive_descendant(Callback);
// https://dom.spec.whatwg.org/#concept-shadow-including-descendant
template<typename Callback>
IterationDecision for_each_shadow_including_descendant(Callback);
TraversalDecision for_each_shadow_including_descendant(Callback);
Slottable as_slottable();
@ -460,95 +461,95 @@ public:
}
template<typename Callback>
IterationDecision for_each_in_inclusive_subtree(Callback callback) const
TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
{
if (callback(static_cast<Node const&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename Callback>
IterationDecision for_each_in_inclusive_subtree(Callback callback)
TraversalDecision for_each_in_inclusive_subtree(Callback callback)
{
if (callback(static_cast<Node&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
IterationDecision for_each_in_inclusive_subtree_of_type(Callback callback)
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback)
{
if (is<U>(static_cast<Node&>(*this))) {
if (callback(static_cast<U&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
if (auto decision = callback(static_cast<U&>(*this)); 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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
IterationDecision 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<Node const&>(*this))) {
if (callback(static_cast<U const&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
if (auto decision = callback(static_cast<U const&>(*this)); 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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename Callback>
IterationDecision for_each_in_subtree(Callback callback) const
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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename Callback>
IterationDecision for_each_in_subtree(Callback 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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
IterationDecision for_each_in_subtree_of_type(Callback 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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
IterationDecision for_each_in_subtree_of_type(Callback callback) const
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) == IterationDecision::Break)
return IterationDecision::Break;
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename Callback>

View file

@ -24,9 +24,9 @@ public:
static_cast<NodeType const*>(this)->template for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
if (element.id() == id) {
found_element = &element;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return found_element;
}
@ -37,9 +37,9 @@ public:
static_cast<NodeType*>(this)->template for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
if (element.id() == id) {
found_element = &element;
return IterationDecision::Break;
return TraversalDecision::Continue;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return found_element;
}

View file

@ -45,10 +45,10 @@ WebIDL::ExceptionOr<JS::GCPtr<Element>> ParentNode::query_selector(StringView se
for (auto& selector : selectors) {
if (SelectorEngine::matches(selector, {}, element, {}, this)) {
result = &element;
return IterationDecision::Break;
return TraversalDecision::Break;
}
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return result;
@ -79,7 +79,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<NodeList>> ParentNode::query_selector_all(S
elements.append(&element);
}
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return StaticNodeList::create(realm(), move(elements));

View file

@ -1225,7 +1225,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> Range::create_contextual
fragment_node->for_each_in_subtree_of_type<HTML::HTMLScriptElement>([&](HTML::HTMLScriptElement& script_element) {
script_element.unmark_as_already_started({});
script_element.unmark_as_parser_inserted({});
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 5. Return the value of fragment node.

View file

@ -71,37 +71,37 @@ template<>
inline bool Node::fast_is<ShadowRoot>() const { return node_type() == to_underlying(NodeType::DOCUMENT_FRAGMENT_NODE) && is_shadow_root(); }
template<typename Callback>
inline IterationDecision Node::for_each_shadow_including_inclusive_descendant(Callback callback)
inline TraversalDecision Node::for_each_shadow_including_inclusive_descendant(Callback callback)
{
if (callback(*this) == IterationDecision::Break)
return IterationDecision::Break;
if (callback(*this) == TraversalDecision::Break)
return TraversalDecision::Break;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->is_element()) {
if (JS::GCPtr<ShadowRoot> shadow_root = static_cast<Element*>(child)->shadow_root_internal()) {
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == IterationDecision::Break)
return IterationDecision::Break;
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
}
if (child->for_each_shadow_including_inclusive_descendant(callback) == IterationDecision::Break)
return IterationDecision::Break;
if (child->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
template<typename Callback>
inline IterationDecision Node::for_each_shadow_including_descendant(Callback callback)
inline TraversalDecision Node::for_each_shadow_including_descendant(Callback callback)
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->is_element()) {
if (JS::GCPtr<ShadowRoot> shadow_root = static_cast<Element*>(child)->shadow_root()) {
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == IterationDecision::Break)
return IterationDecision::Break;
if (shadow_root->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
}
if (child->for_each_shadow_including_inclusive_descendant(callback) == IterationDecision::Break)
return IterationDecision::Break;
if (child->for_each_shadow_including_inclusive_descendant(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
}
}

View file

@ -78,10 +78,10 @@ JS::GCPtr<HTML::HTMLSlotElement> find_a_slot(Slottable const& slottable, OpenFla
shadow->for_each_in_subtree_of_type<HTML::HTMLSlotElement>([&](auto& child) {
if (!child.manually_assigned_nodes().contains_slow(slottable))
return IterationDecision::Continue;
return TraversalDecision::Continue;
slot = child;
return IterationDecision::Break;
return TraversalDecision::Break;
});
return slot;
@ -93,10 +93,10 @@ JS::GCPtr<HTML::HTMLSlotElement> find_a_slot(Slottable const& slottable, OpenFla
shadow->for_each_in_subtree_of_type<HTML::HTMLSlotElement>([&](auto& child) {
if (child.slot_name() != slottable_name)
return IterationDecision::Continue;
return TraversalDecision::Continue;
slot = child;
return IterationDecision::Break;
return TraversalDecision::Break;
});
return slot;
@ -188,7 +188,7 @@ void assign_slottables_for_a_tree(JS::NonnullGCPtr<Node> root)
// descendants, in tree order.
root->for_each_in_inclusive_subtree_of_type<HTML::HTMLSlotElement>([](auto& slot) {
assign_slottables(slot);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}