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
parent c57d395a48
commit 398bf10b92
33 changed files with 229 additions and 215 deletions

View file

@ -923,7 +923,7 @@ void KeyframeEffect::update_style_properties()
target->for_each_in_subtree_of_type<DOM::Element>([&](auto& element) {
auto* element_style = element.computed_css_values();
if (!element_style || !element.layout_node())
return IterationDecision::Continue;
return TraversalDecision::Continue;
for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) {
if (element_style->is_property_inherited(static_cast<CSS::PropertyID>(i))) {
@ -933,7 +933,7 @@ void KeyframeEffect::update_style_properties()
}
element.layout_node()->apply_style(*element_style);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
auto invalidation = compute_required_invalidation(animated_properties_before_update, style->animated_property_values());

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;
});
}

View file

@ -18,6 +18,7 @@ class PageClient;
class PaintContext;
class Resource;
class ResourceLoader;
enum class TraversalDecision;
class XMLDocumentBuilder;
}

View file

@ -53,47 +53,47 @@ public:
bool is_familiar_with(BrowsingContext const&) const;
template<typename Callback>
IterationDecision for_each_in_inclusive_subtree(Callback callback) const
TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
{
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->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(*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->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) 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;
}
bool is_top_level() const;

View file

@ -292,14 +292,14 @@ JS::ThrowCompletionOr<void> CustomElementRegistry::define(String const& name, We
document.for_each_shadow_including_descendant([&](DOM::Node& inclusive_descendant) {
if (!is<DOM::Element>(inclusive_descendant))
return IterationDecision::Continue;
return TraversalDecision::Continue;
auto& inclusive_descendant_element = static_cast<DOM::Element&>(inclusive_descendant);
if (inclusive_descendant_element.namespace_uri() == Namespace::HTML && inclusive_descendant_element.local_name() == local_name && (!extends.has_value() || inclusive_descendant_element.is_value() == name))
upgrade_candidates.append(JS::make_handle(inclusive_descendant_element));
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 19. For each element element in upgrade candidates, enqueue a custom element upgrade reaction given element and definition.
@ -388,12 +388,12 @@ void CustomElementRegistry::upgrade(JS::NonnullGCPtr<DOM::Node> root) const
root->for_each_shadow_including_inclusive_descendant([&](DOM::Node& inclusive_descendant) {
if (!is<DOM::Element>(inclusive_descendant))
return IterationDecision::Continue;
return TraversalDecision::Continue;
auto& inclusive_descendant_element = static_cast<DOM::Element&>(inclusive_descendant);
candidates.append(JS::make_handle(inclusive_descendant_element));
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 2. For each candidate of candidates, try to upgrade candidate.

View file

@ -137,10 +137,10 @@ void FormAssociatedElement::reset_form_owner()
html_element.root().for_each_in_inclusive_subtree_of_type<HTMLFormElement>([this, &form_value](HTMLFormElement& form_element) {
if (form_element.id() == form_value) {
set_form(&form_element);
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}

View file

@ -91,7 +91,7 @@ JS::MarkedVector<JS::NonnullGCPtr<DOM::Element>> HTMLAllCollection::collect_matc
m_root->for_each_in_subtree_of_type<DOM::Element>([&](auto& element) {
if (m_filter(element))
elements.append(element);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
} else {
m_root->for_each_child_of_type<DOM::Element>([&](auto& element) {

View file

@ -157,15 +157,15 @@ void HTMLDetailsElement::update_shadow_tree_slots()
for_each_in_subtree([&](auto& child) {
if (&child == summary)
return IterationDecision::Continue;
return TraversalDecision::Continue;
if (!child.is_slottable())
return IterationDecision::Continue;
return TraversalDecision::Continue;
child.as_slottable().visit([&](auto& node) {
descendants_assignment.append(JS::make_handle(node));
});
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
m_summary_slot->assign(move(summary_assignment));

View file

@ -561,7 +561,7 @@ Vector<JS::NonnullGCPtr<DOM::Element>> HTMLFormElement::get_submittable_elements
submittable_elements.append(form_associated_element->form_associated_element_to_html_element());
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return submittable_elements;
@ -1086,14 +1086,14 @@ FormAssociatedElement* HTMLFormElement::default_button()
root().for_each_in_subtree([&](auto& node) {
auto* form_associated_element = dynamic_cast<FormAssociatedElement*>(&node);
if (!form_associated_element)
return IterationDecision::Continue;
return TraversalDecision::Continue;
if (form_associated_element->form() == this && form_associated_element->is_submit_button()) {
default_button = form_associated_element;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return default_button;

View file

@ -1398,7 +1398,7 @@ void HTMLInputElement::set_checked_within_group()
document().for_each_in_inclusive_subtree_of_type<HTML::HTMLInputElement>([&](auto& element) {
if (element.checked() && &element != this && is_in_same_radio_button_group(*this, element))
element.set_checked(false, ChangeSource::User);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}
@ -1425,9 +1425,9 @@ void HTMLInputElement::legacy_pre_activation_behavior()
document().for_each_in_inclusive_subtree_of_type<HTML::HTMLInputElement>([&](auto& element) {
if (element.checked() && is_in_same_radio_button_group(*this, element)) {
m_legacy_pre_activation_behavior_checked_element_in_group = &element;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
set_checked_within_group();

View file

@ -46,9 +46,9 @@ JS::GCPtr<HTMLElement> HTMLLabelElement::control() const
for_each_in_inclusive_subtree_of_type<HTMLElement>([&](auto& element) {
if (element.id() == *for_() && element.is_labelable()) {
control = &const_cast<HTMLElement&>(element);
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return control;
}
@ -58,9 +58,9 @@ JS::GCPtr<HTMLElement> HTMLLabelElement::control() const
for_each_in_subtree_of_type<HTMLElement>([&](auto& element) {
if (element.is_labelable()) {
control = &const_cast<HTMLElement&>(element);
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return control;

View file

@ -1698,14 +1698,14 @@ Vector<FlyString> Window::supported_property_names() const
// that have a non-empty name content attribute and are in a document tree with window's associated Document as their root; and
// - the value of the id content attribute for all HTML elements that have a non-empty id content attribute
// and are in a document tree with window's associated Document as their root.
associated_document().for_each_in_subtree_of_type<DOM::Element>([&property_names](auto& element) -> IterationDecision {
associated_document().for_each_in_subtree_of_type<DOM::Element>([&property_names](auto& element) -> TraversalDecision {
if (is<HTMLEmbedElement>(element) || is<HTMLFormElement>(element) || is<HTMLImageElement>(element) || is<HTMLObjectElement>(element)) {
if (element.name().has_value())
property_names.set(element.name().value(), AK::HashSetExistingEntryBehavior::Keep);
}
if (auto const& name = element.id(); name.has_value())
property_names.set(name.value().to_string(), AK::HashSetExistingEntryBehavior::Keep);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return property_names.values();
@ -1729,12 +1729,12 @@ WebIDL::ExceptionOr<JS::Value> Window::named_item_value(FlyString const& name) c
JS::GCPtr<NavigableContainer> container = nullptr;
mutable_this.associated_document().for_each_in_subtree_of_type<HTML::NavigableContainer>([&](HTML::NavigableContainer& navigable_container) {
if (!navigable_container.content_navigable())
return IterationDecision::Continue;
return TraversalDecision::Continue;
if (objects.navigables.contains_slow(JS::NonnullGCPtr { *navigable_container.content_navigable() })) {
container = navigable_container;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
// 2. Return container's content navigable's active WindowProxy.
VERIFY(container);
@ -1775,13 +1775,13 @@ Window::NamedObjects Window::named_objects(StringView name)
// embed, form, img, or object elements that have a name content attribute whose value is name
// and are in a document tree with window's associated Document as their root; and
// HTML elements that have an id content attribute whose value is name and are in a document tree with window's associated Document as their root.
associated_document().for_each_in_subtree_of_type<DOM::Element>([&objects, &name](auto& element) -> IterationDecision {
associated_document().for_each_in_subtree_of_type<DOM::Element>([&objects, &name](auto& element) -> TraversalDecision {
if ((is<HTMLEmbedElement>(element) || is<HTMLFormElement>(element) || is<HTMLImageElement>(element) || is<HTMLObjectElement>(element))
&& (element.name() == name))
objects.elements.append(element);
else if (element.id() == name)
objects.elements.append(element);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
return objects;

View file

@ -410,9 +410,9 @@ CSSPixels FormattingContext::compute_table_box_width_inside_table_wrapper(Box co
box.for_each_in_subtree_of_type<Box>([&](Box const& child_box) {
if (child_box.display().is_table_inside()) {
table_box = child_box;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
VERIFY(table_box.has_value());
@ -464,9 +464,9 @@ CSSPixels FormattingContext::compute_table_box_height_inside_table_wrapper(Box c
box.for_each_in_subtree_of_type<Box>([&](Box const& child_box) {
if (child_box.display().is_table_inside()) {
table_box = child_box;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
VERIFY(table_box.has_value());
@ -1808,9 +1808,9 @@ bool FormattingContext::can_skip_is_anonymous_text_run(Box& box)
box.for_each_in_subtree([&](auto const& node) {
if (!is<TextNode>(node) || !static_cast<TextNode const&>(node).dom_node().data().bytes_as_string_view().is_whitespace()) {
contains_only_white_space = false;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
if (contains_only_white_space)
return true;

View file

@ -104,9 +104,9 @@ Label const* Label::label_for_control_node(LabelableNode const& control)
control.document().layout_node()->for_each_in_inclusive_subtree_of_type<Label>([&](auto& node) {
if (node.dom_node().for_() == id) {
label = &node;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
if (label)

View file

@ -210,11 +210,11 @@ void LayoutState::commit(Box& root)
// when text paintables shift around in the tree.
root.for_each_in_inclusive_subtree([&](Layout::Node& node) {
node.set_paintable(nullptr);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
root.document().for_each_shadow_including_inclusive_descendant([&](DOM::Node& node) {
node.set_paintable(nullptr);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
HashTable<Layout::TextNode*> text_nodes;

View file

@ -67,7 +67,7 @@ TableGrid TableGrid::calculate_row_column_grid(Box const& box, Vector<Cell>& cel
auto dom_node = col_group.dom_node();
dom_node->template for_each_in_subtree_of_type<HTML::HTMLTableColElement>([&](auto&) {
x_width += 1;
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
};

View file

@ -254,9 +254,9 @@ static bool is_ignorable_whitespace(Layout::Node const& node)
node.for_each_in_inclusive_subtree_of_type<TextNode>([&contains_only_white_space](auto& text_node) {
if (!text_node.text_for_rendering().bytes_as_string_view().is_whitespace()) {
contains_only_white_space = false;
return IterationDecision::Break;
return TraversalDecision::Break;
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
if (contains_only_white_space)
return true;
@ -316,7 +316,7 @@ void TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
node.set_paintable(nullptr);
if (is<DOM::Element>(node))
static_cast<DOM::Element&>(node).clear_pseudo_element_nodes({});
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}
};
@ -520,7 +520,7 @@ void TreeBuilder::for_each_in_tree_with_internal_display(NodeWithStyle& root, Ca
auto const display = box.display();
if (display.is_internal() && display.internal() == internal)
callback(box);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}
@ -531,7 +531,7 @@ void TreeBuilder::for_each_in_tree_with_inside_display(NodeWithStyle& root, Call
auto const display = box.display();
if (display.is_outside_and_inside() && display.inside() == inside)
callback(box);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}
@ -742,7 +742,7 @@ Vector<JS::Handle<Box>> TreeBuilder::generate_missing_parents(NodeWithStyle& roo
table_roots_to_wrap.append(parent);
}
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
for (auto& table_box : table_roots_to_wrap) {

View file

@ -13,12 +13,6 @@
namespace Web::Painting {
enum class TraversalDecision {
Continue,
SkipChildrenAndContinue,
Break,
};
enum class PaintPhase {
Background,
Border,

View file

@ -794,9 +794,9 @@ Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestTy
(void)PaintableBox::hit_test(position, type, [&](HitTestResult candidate) {
VERIFY(!result.has_value());
if (!candidate.paintable->visible_for_hit_testing())
return Painting::TraversalDecision::Continue;
return TraversalDecision::Continue;
result = move(candidate);
return Painting::TraversalDecision::Break;
return TraversalDecision::Break;
});
return result;
}

View file

@ -81,7 +81,7 @@ void SVGElement::update_use_elements_that_reference_this()
document().for_each_in_subtree_of_type<SVGUseElement>([this](SVGUseElement& use_element) {
use_element.svg_element_changed(*this);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}
@ -100,7 +100,7 @@ void SVGElement::remove_from_use_element_that_reference_this()
document().for_each_in_subtree_of_type<SVGUseElement>([this](SVGUseElement& use_element) {
use_element.svg_element_removed(*this);
return IterationDecision::Continue;
return TraversalDecision::Continue;
});
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2024, Tim Ledbetter <timledbetter@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace Web {
enum class TraversalDecision {
Continue,
SkipChildrenAndContinue,
Break,
};
}

View file

@ -11,6 +11,7 @@
#include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibWeb/Forward.h>
#include <LibWeb/TraversalDecision.h>
namespace Web {
@ -124,95 +125,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<T const&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
if (auto decision = callback(static_cast<T 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;
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<T&>(*this)) == IterationDecision::Break)
return IterationDecision::Break;
if (auto decision = callback(static_cast<T&>(*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<T const&>(*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<T 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

@ -443,7 +443,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, i32 node_id, Optional<W
if (ctx.active_document() != nullptr) {
ctx.active_document()->set_inspected_node(nullptr, {});
}
return IterationDecision::Continue;
return Web::TraversalDecision::Continue;
});
Web::DOM::Node* node = Web::DOM::Node::from_unique_id(node_id);