mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibWeb: Add Node::for_each(_inclusive)_ancestor()
Allows for easy iteration over the chain of ancestors for a node.
This commit is contained in:
parent
4323669939
commit
d08febcf67
Notes:
github-actions[bot]
2025-01-10 22:39:07 +00:00
Author: https://github.com/gmta
Commit: d08febcf67
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3216
3 changed files with 83 additions and 53 deletions
|
@ -589,6 +589,36 @@ public:
|
||||||
return TraversalDecision::Continue;
|
return TraversalDecision::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 Callback>
|
template<typename Callback>
|
||||||
void for_each_child(Callback callback) const
|
void for_each_child(Callback callback) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -153,13 +153,12 @@ bool command_delete_action(DOM::Document& document, String const&)
|
||||||
if (offset == 0 && node->index() == 0
|
if (offset == 0 && node->index() == 0
|
||||||
&& node_element.local_name().is_one_of(HTML::TagNames::li, HTML::TagNames::dt, HTML::TagNames::dd)) {
|
&& 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.
|
// 1. Let items be a list of all lis that are ancestors of node.
|
||||||
auto items = Vector<GC::Ref<DOM::Element>>();
|
Vector<GC::Ref<DOM::Element>> items;
|
||||||
GC::Ptr<DOM::Node> ancestor = node->parent();
|
node->for_each_ancestor([&items](GC::Ref<DOM::Node> ancestor) {
|
||||||
while (ancestor) {
|
|
||||||
if (is<HTML::HTMLLIElement>(*ancestor))
|
if (is<HTML::HTMLLIElement>(*ancestor))
|
||||||
items.append(static_cast<DOM::Element&>(*ancestor));
|
items.append(static_cast<DOM::Element&>(*ancestor));
|
||||||
ancestor = ancestor->parent();
|
return IterationDecision::Continue;
|
||||||
}
|
});
|
||||||
|
|
||||||
// 2. Normalize sublists of each item in items.
|
// 2. Normalize sublists of each item in items.
|
||||||
for (auto item : 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
|
// same editing host, set the tag name of node to the default single-line container name
|
||||||
// and let node be the result.
|
// and let node be the result.
|
||||||
if (node_element.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) {
|
if (node_element.local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) {
|
||||||
ancestor = node->parent();
|
|
||||||
bool allowed_child_of_any_ancestor = false;
|
bool allowed_child_of_any_ancestor = false;
|
||||||
while (ancestor) {
|
node->for_each_ancestor([&](GC::Ref<DOM::Node> ancestor) {
|
||||||
if (is_in_same_editing_host(*node, *ancestor) && is_allowed_child_of_node(GC::Ref { *node }, 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;
|
allowed_child_of_any_ancestor = true;
|
||||||
break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
ancestor = ancestor->parent();
|
return IterationDecision::Continue;
|
||||||
}
|
});
|
||||||
if (!allowed_child_of_any_ancestor)
|
if (!allowed_child_of_any_ancestor)
|
||||||
node = set_the_tag_name(node_element, document.default_single_line_container_name());
|
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.
|
// result.
|
||||||
if (static_cast<DOM::Element&>(*container).local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) {
|
if (static_cast<DOM::Element&>(*container).local_name().is_one_of(HTML::TagNames::dd, HTML::TagNames::dt)) {
|
||||||
bool allowed_child_of_any_ancestor = false;
|
bool allowed_child_of_any_ancestor = false;
|
||||||
GC::Ptr<DOM::Node> ancestor = container->parent();
|
container->for_each_ancestor([&](GC::Ref<DOM::Node> ancestor) {
|
||||||
while (ancestor) {
|
if (is_allowed_child_of_node(GC::Ref { *container }, ancestor)
|
||||||
if (is_allowed_child_of_node(GC::Ref { *container }, GC::Ref { *ancestor })
|
&& is_in_same_editing_host(*container, ancestor)) {
|
||||||
&& is_in_same_editing_host(*container, *ancestor)) {
|
|
||||||
allowed_child_of_any_ancestor = true;
|
allowed_child_of_any_ancestor = true;
|
||||||
break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
ancestor = ancestor->parent();
|
return IterationDecision::Continue;
|
||||||
}
|
});
|
||||||
if (!allowed_child_of_any_ancestor)
|
if (!allowed_child_of_any_ancestor)
|
||||||
container = set_the_tag_name(static_cast<DOM::Element&>(*container), document.default_single_line_container_name());
|
container = set_the_tag_name(static_cast<DOM::Element&>(*container), document.default_single_line_container_name());
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,15 +48,14 @@ GC::Ref<DOM::Range> block_extend_a_range(GC::Ref<DOM::Range> 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
|
// 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.
|
// order, and set start node to that li's parent.
|
||||||
auto ancestor = start_node;
|
start_node->for_each_inclusive_ancestor([&](GC::Ref<DOM::Node> ancestor) {
|
||||||
while (ancestor) {
|
|
||||||
if (is<HTML::HTMLLIElement>(*ancestor)) {
|
if (is<HTML::HTMLLIElement>(*ancestor)) {
|
||||||
start_offset = ancestor->index();
|
start_offset = ancestor->index();
|
||||||
start_node = ancestor->parent();
|
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:
|
// 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 })) {
|
if (!is_block_start_point({ *start_node, start_offset })) {
|
||||||
|
@ -85,15 +84,14 @@ GC::Ref<DOM::Range> block_extend_a_range(GC::Ref<DOM::Range> 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
|
// 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.
|
// tree order, and set end node to that li's parent.
|
||||||
ancestor = end_node;
|
end_node->for_each_inclusive_ancestor([&](GC::Ref<DOM::Node> ancestor) {
|
||||||
while (ancestor) {
|
|
||||||
if (is<HTML::HTMLLIElement>(*ancestor)) {
|
if (is<HTML::HTMLLIElement>(*ancestor)) {
|
||||||
end_offset = ancestor->index() + 1;
|
end_offset = ancestor->index() + 1;
|
||||||
end_node = ancestor->parent();
|
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:
|
// 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 })) {
|
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, or an inclusive ancestor ul in the same editing host whose nextSibling is also a ul in the
|
||||||
// same editing host:
|
// same editing host:
|
||||||
while (true) {
|
while (true) {
|
||||||
auto inclusive_ancestor = ancestor;
|
|
||||||
bool has_valid_ol_or_ul_ancestor = false;
|
bool has_valid_ol_or_ul_ancestor = false;
|
||||||
while (inclusive_ancestor) {
|
ancestor->for_each_inclusive_ancestor([&](GC::Ref<DOM::Node> inclusive_ancestor) {
|
||||||
if (inclusive_ancestor->next_sibling() && is_in_same_editing_host(*ancestor, *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_in_same_editing_host(*inclusive_ancestor, *inclusive_ancestor->next_sibling())
|
||||||
&& ((is<HTML::HTMLOListElement>(*inclusive_ancestor) && is<HTML::HTMLOListElement>(*inclusive_ancestor->next_sibling()))
|
&& ((is<HTML::HTMLOListElement>(*inclusive_ancestor) && is<HTML::HTMLOListElement>(*inclusive_ancestor->next_sibling()))
|
||||||
|| (is<HTML::HTMLUListElement>(*inclusive_ancestor) && is<HTML::HTMLUListElement>(*inclusive_ancestor->next_sibling())))) {
|
|| (is<HTML::HTMLUListElement>(*inclusive_ancestor) && is<HTML::HTMLUListElement>(*inclusive_ancestor->next_sibling())))) {
|
||||||
has_valid_ol_or_ul_ancestor = true;
|
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)
|
if (!has_valid_ol_or_ul_ancestor)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -888,13 +885,16 @@ GC::Ptr<DOM::Node> editing_host_of_node(GC::Ref<DOM::Node> node)
|
||||||
|
|
||||||
// or the nearest ancestor of node that is an editing host, if node is editable.
|
// or the nearest ancestor of node that is an editing host, if node is editable.
|
||||||
if (node->is_editable()) {
|
if (node->is_editable()) {
|
||||||
auto* ancestor = node->parent();
|
GC::Ptr<DOM::Node> result;
|
||||||
while (ancestor) {
|
node->for_each_ancestor([&result](GC::Ref<DOM::Node> ancestor) {
|
||||||
if (ancestor->is_editing_host())
|
if (ancestor->is_editing_host()) {
|
||||||
return ancestor;
|
result = ancestor;
|
||||||
ancestor = ancestor->parent();
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
VERIFY_NOT_REACHED();
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
VERIFY(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The editing host of node is null if node is neither editable nor an editing host;
|
// 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<DOM::Node> node)
|
||||||
|
|
||||||
// 2. If node is not an allowed child of any of its ancestors in the same editing host:
|
// 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;
|
bool allowed_child_of_any_ancestor = false;
|
||||||
GC::Ptr<DOM::Node> ancestor = node->parent();
|
node->for_each_ancestor([&](GC::Ref<DOM::Node> ancestor) {
|
||||||
while (ancestor) {
|
if (is_in_same_editing_host(ancestor, node) && is_allowed_child_of_node(node, ancestor)) {
|
||||||
if (is_in_same_editing_host(*ancestor, *node) && is_allowed_child_of_node(GC::Ref { *node }, GC::Ref { *ancestor })) {
|
|
||||||
allowed_child_of_any_ancestor = true;
|
allowed_child_of_any_ancestor = true;
|
||||||
break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
ancestor = ancestor->parent();
|
return IterationDecision::Continue;
|
||||||
}
|
});
|
||||||
if (!allowed_child_of_any_ancestor) {
|
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
|
// 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
|
// 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<DOM::Node> node)
|
||||||
{
|
{
|
||||||
// excluding any node with an inclusive ancestor Element whose "display" property has resolved
|
// excluding any node with an inclusive ancestor Element whose "display" property has resolved
|
||||||
// value "none".
|
// value "none".
|
||||||
GC::Ptr<DOM::Node> inclusive_ancestor = node;
|
bool has_display_none = false;
|
||||||
while (inclusive_ancestor) {
|
node->for_each_inclusive_ancestor([&has_display_none](GC::Ref<DOM::Node> ancestor) {
|
||||||
auto display = resolved_display(*inclusive_ancestor);
|
auto display = resolved_display(ancestor);
|
||||||
if (display.has_value() && display->is_none())
|
if (display.has_value() && display->is_none()) {
|
||||||
return false;
|
has_display_none = true;
|
||||||
inclusive_ancestor = inclusive_ancestor->parent();
|
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,
|
// Something is visible if it is a node that either is a block node,
|
||||||
if (is_block_node(node))
|
if (is_block_node(node))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue