LibWeb: Include siblings+descendants when invalidating style

When an element is invalidated, it's possible for any subsequent sibling
or any of their descendants to also need invalidation. (Due to the CSS
sibling combinators, `+` and `~`)

For DOM node insertion/removal, we must also invalidate preceding
siblings, since they could be affected by :first-child, :last-child or
:nth-child() selectors.

This increases the amount of invalidation we do, but it's more correct.

In the future, we will implement optimizations that drastically reduce
the number of elements invalidated.
This commit is contained in:
Andreas Kling 2024-09-19 13:18:34 +02:00 committed by Andreas Kling
commit df048e10f5
Notes: github-actions[bot] 2024-09-22 18:08:54 +00:00

View file

@ -405,18 +405,42 @@ void Node::invalidate_style(StyleInvalidationReason reason)
return; return;
} }
for_each_in_inclusive_subtree([&](Node& node) { // When invalidating style for a node, we actually invalidate:
node.m_needs_style_update = true; // - the node itself
if (node.has_children()) // - all of its descendants
node.m_child_needs_style_update = true; // - all of its preceding siblings and their descendants (only on DOM insert/remove)
if (auto shadow_root = node.is_element() ? static_cast<DOM::Element&>(node).shadow_root() : nullptr) { // - all of its subsequent siblings and their descendants
node.m_child_needs_style_update = true; // FIXME: This is a lot of invalidation and we should implement more sophisticated invalidation to do less work!
shadow_root->m_needs_style_update = true;
if (shadow_root->has_children()) auto invalidate_entire_subtree = [&](Node& subtree_root) {
shadow_root->m_child_needs_style_update = true; subtree_root.for_each_in_inclusive_subtree([&](Node& node) {
node.m_needs_style_update = true;
if (node.has_children())
node.m_child_needs_style_update = true;
if (auto shadow_root = node.is_element() ? static_cast<DOM::Element&>(node).shadow_root() : nullptr) {
node.m_child_needs_style_update = true;
shadow_root->m_needs_style_update = true;
if (shadow_root->has_children())
shadow_root->m_child_needs_style_update = true;
}
return TraversalDecision::Continue;
});
};
invalidate_entire_subtree(*this);
if (reason == StyleInvalidationReason::NodeInsertBefore || reason == StyleInvalidationReason::NodeRemove) {
for (auto* sibling = previous_sibling(); sibling; sibling = sibling->previous_sibling()) {
if (sibling->is_element())
invalidate_entire_subtree(*sibling);
} }
return TraversalDecision::Continue; }
});
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
if (sibling->is_element())
invalidate_entire_subtree(*sibling);
}
for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host()) for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host())
ancestor->m_child_needs_style_update = true; ancestor->m_child_needs_style_update = true;
document().schedule_style_update(); document().schedule_style_update();