diff --git a/Libraries/LibWeb/DOM/Node.h b/Libraries/LibWeb/DOM/Node.h index e39943ca03d..8e4cae9fbfa 100644 --- a/Libraries/LibWeb/DOM/Node.h +++ b/Libraries/LibWeb/DOM/Node.h @@ -589,6 +589,36 @@ public: return TraversalDecision::Continue; } + template + void for_each_ancestor(Callback callback) const + { + return const_cast(this)->for_each_ancestor(move(callback)); + } + + template + void for_each_ancestor(Callback callback) + { + for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { + if (callback(*ancestor) == IterationDecision::Break) + break; + } + } + + template + void for_each_inclusive_ancestor(Callback callback) const + { + return const_cast(this)->for_each_inclusive_ancestor(move(callback)); + } + + template + void for_each_inclusive_ancestor(Callback callback) + { + for (auto* ancestor = this; ancestor; ancestor = ancestor->parent()) { + if (callback(*ancestor) == IterationDecision::Break) + break; + } + } + template void for_each_child(Callback callback) const { diff --git a/Libraries/LibWeb/Editing/Commands.cpp b/Libraries/LibWeb/Editing/Commands.cpp index 5720a22f0ed..a129f8d651f 100644 --- a/Libraries/LibWeb/Editing/Commands.cpp +++ b/Libraries/LibWeb/Editing/Commands.cpp @@ -153,13 +153,12 @@ bool command_delete_action(DOM::Document& document, String const&) if (offset == 0 && node->index() == 0 && node_element.local_name().is_one_of(HTML::TagNames::li, HTML::TagNames::dt, HTML::TagNames::dd)) { // 1. Let items be a list of all lis that are ancestors of node. - auto items = Vector>(); - GC::Ptr ancestor = node->parent(); - while (ancestor) { + Vector> items; + node->for_each_ancestor([&items](GC::Ref ancestor) { if (is(*ancestor)) items.append(static_cast(*ancestor)); - ancestor = ancestor->parent(); - } + return IterationDecision::Continue; + }); // 2. Normalize sublists of each item in items. for (auto item : items) @@ -179,15 +178,14 @@ bool command_delete_action(DOM::Document& document, String const&) // same editing host, set the tag name of node to the default single-line container name // and let node be the result. if (node_element.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) { - ancestor = node->parent(); bool allowed_child_of_any_ancestor = false; - while (ancestor) { - if (is_in_same_editing_host(*node, *ancestor) && is_allowed_child_of_node(GC::Ref { *node }, GC::Ref { *ancestor })) { + node->for_each_ancestor([&](GC::Ref ancestor) { + if (is_in_same_editing_host(*node, ancestor) && is_allowed_child_of_node(GC::Ref { *node }, ancestor)) { allowed_child_of_any_ancestor = true; - break; + return IterationDecision::Break; } - ancestor = ancestor->parent(); - } + return IterationDecision::Continue; + }); if (!allowed_child_of_any_ancestor) node = set_the_tag_name(node_element, document.default_single_line_container_name()); } @@ -644,15 +642,14 @@ bool command_insert_paragraph_action(DOM::Document& document, String const&) // result. if (static_cast(*container).local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) { bool allowed_child_of_any_ancestor = false; - GC::Ptr ancestor = container->parent(); - while (ancestor) { - if (is_allowed_child_of_node(GC::Ref { *container }, GC::Ref { *ancestor }) - && is_in_same_editing_host(*container, *ancestor)) { + container->for_each_ancestor([&](GC::Ref ancestor) { + if (is_allowed_child_of_node(GC::Ref { *container }, ancestor) + && is_in_same_editing_host(*container, ancestor)) { allowed_child_of_any_ancestor = true; - break; + return IterationDecision::Break; } - ancestor = ancestor->parent(); - } + return IterationDecision::Continue; + }); if (!allowed_child_of_any_ancestor) container = set_the_tag_name(static_cast(*container), document.default_single_line_container_name()); } diff --git a/Libraries/LibWeb/Editing/Internal/Algorithms.cpp b/Libraries/LibWeb/Editing/Internal/Algorithms.cpp index 5ebebb41104..bd594f24470 100644 --- a/Libraries/LibWeb/Editing/Internal/Algorithms.cpp +++ b/Libraries/LibWeb/Editing/Internal/Algorithms.cpp @@ -48,15 +48,14 @@ GC::Ref block_extend_a_range(GC::Ref range) // 2. If some inclusive ancestor of start node is an li, set start offset to the index of the last such li in tree // order, and set start node to that li's parent. - auto ancestor = start_node; - while (ancestor) { + start_node->for_each_inclusive_ancestor([&](GC::Ref ancestor) { if (is(*ancestor)) { start_offset = ancestor->index(); start_node = ancestor->parent(); - break; + return IterationDecision::Break; } - ancestor = ancestor->parent(); - } + return IterationDecision::Continue; + }); // 3. If (start node, start offset) is not a block start point, repeat the following steps: if (!is_block_start_point({ *start_node, start_offset })) { @@ -85,15 +84,14 @@ GC::Ref block_extend_a_range(GC::Ref range) // 5. If some inclusive ancestor of end node is an li, set end offset to one plus the index of the last such li in // tree order, and set end node to that li's parent. - ancestor = end_node; - while (ancestor) { + end_node->for_each_inclusive_ancestor([&](GC::Ref ancestor) { if (is(*ancestor)) { end_offset = ancestor->index() + 1; end_node = ancestor->parent(); - break; + return IterationDecision::Break; } - ancestor = ancestor->parent(); - } + return IterationDecision::Continue; + }); // 6. If (end node, end offset) is not a block end point, repeat the following steps: if (!is_block_end_point({ *end_node, end_offset })) { @@ -827,18 +825,17 @@ void delete_the_selection(Selection& selection, bool block_merging, bool strip_w // same editing host, or an inclusive ancestor ul in the same editing host whose nextSibling is also a ul in the // same editing host: while (true) { - auto inclusive_ancestor = ancestor; bool has_valid_ol_or_ul_ancestor = false; - while (inclusive_ancestor) { + ancestor->for_each_inclusive_ancestor([&](GC::Ref inclusive_ancestor) { if (inclusive_ancestor->next_sibling() && is_in_same_editing_host(*ancestor, *inclusive_ancestor) && is_in_same_editing_host(*inclusive_ancestor, *inclusive_ancestor->next_sibling()) && ((is(*inclusive_ancestor) && is(*inclusive_ancestor->next_sibling())) || (is(*inclusive_ancestor) && is(*inclusive_ancestor->next_sibling())))) { has_valid_ol_or_ul_ancestor = true; - break; + return IterationDecision::Break; } - inclusive_ancestor = inclusive_ancestor->parent(); - } + return IterationDecision::Continue; + }); if (!has_valid_ol_or_ul_ancestor) break; @@ -888,13 +885,16 @@ GC::Ptr editing_host_of_node(GC::Ref node) // or the nearest ancestor of node that is an editing host, if node is editable. if (node->is_editable()) { - auto* ancestor = node->parent(); - while (ancestor) { - if (ancestor->is_editing_host()) - return ancestor; - ancestor = ancestor->parent(); - } - VERIFY_NOT_REACHED(); + GC::Ptr result; + node->for_each_ancestor([&result](GC::Ref ancestor) { + if (ancestor->is_editing_host()) { + result = ancestor; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + VERIFY(result); + return result; } // The editing host of node is null if node is neither editable nor an editing host; @@ -1068,14 +1068,13 @@ void fix_disallowed_ancestors_of_node(GC::Ref node) // 2. If node is not an allowed child of any of its ancestors in the same editing host: bool allowed_child_of_any_ancestor = false; - GC::Ptr ancestor = node->parent(); - while (ancestor) { - if (is_in_same_editing_host(*ancestor, *node) && is_allowed_child_of_node(GC::Ref { *node }, GC::Ref { *ancestor })) { + node->for_each_ancestor([&](GC::Ref ancestor) { + if (is_in_same_editing_host(ancestor, node) && is_allowed_child_of_node(node, ancestor)) { allowed_child_of_any_ancestor = true; - break; + return IterationDecision::Break; } - ancestor = ancestor->parent(); - } + return IterationDecision::Continue; + }); if (!allowed_child_of_any_ancestor) { // 1. If node is a dd or dt, wrap the one-node list consisting of node, with sibling criteria returning true for // any dl with no attributes and false otherwise, and new parent instructions returning the result of calling @@ -1808,13 +1807,17 @@ bool is_visible_node(GC::Ref node) { // excluding any node with an inclusive ancestor Element whose "display" property has resolved // value "none". - GC::Ptr inclusive_ancestor = node; - while (inclusive_ancestor) { - auto display = resolved_display(*inclusive_ancestor); - if (display.has_value() && display->is_none()) - return false; - inclusive_ancestor = inclusive_ancestor->parent(); - } + bool has_display_none = false; + node->for_each_inclusive_ancestor([&has_display_none](GC::Ref ancestor) { + auto display = resolved_display(ancestor); + if (display.has_value() && display->is_none()) { + has_display_none = true; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + if (has_display_none) + return false; // Something is visible if it is a node that either is a block node, if (is_block_node(node))