1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 22:57:44 +00:00

LibWeb: Ignore layout/paint invalidations in subtree under display:none

The entire subtree of an element with display:none is irrelevant for
purposes of layout and/or paint invalidation.

We now simply ignore invalidation triggers inside such subtrees.

This avoids a *lot* of redundant busywork when running CSS animations
inside not-even-rendered content. As an example, this avoids repainting
YouTube embeds repeatedly due to animating-but-hidden progress
indicators.

Note that the subtree *root* (i.e the `display:none` element itself)
still gets to trigger invalidation, since we may need to rebuild the
layout tree when the `display` property changes.
This commit is contained in:
Andreas Kling 2024-01-04 13:46:55 +01:00
parent f157cd50a1
commit 302752d092

View file

@ -1077,26 +1077,39 @@ void Document::update_layout()
m_layout_update_timer->stop();
}
[[nodiscard]] static Element::RequiredInvalidationAfterStyleChange update_style_recursively(DOM::Node& node)
[[nodiscard]] static Element::RequiredInvalidationAfterStyleChange update_style_recursively(Node& node)
{
bool const needs_full_style_update = node.document().needs_full_style_update();
Element::RequiredInvalidationAfterStyleChange invalidation;
// NOTE: If the current node has `display:none`, we can disregard all invalidation
// caused by its children, as they will not be rendered anyway.
// We will still recompute style for the children, though.
bool is_display_none = false;
if (is<Element>(node)) {
invalidation |= static_cast<Element&>(node).recompute_style();
is_display_none = static_cast<Element&>(node).computed_css_values()->display().is_none();
}
node.set_needs_style_update(false);
if (needs_full_style_update || node.child_needs_style_update()) {
if (node.is_element()) {
if (auto* shadow_root = static_cast<DOM::Element&>(node).shadow_root_internal()) {
if (needs_full_style_update || shadow_root->needs_style_update() || shadow_root->child_needs_style_update())
invalidation |= update_style_recursively(*shadow_root);
if (needs_full_style_update || shadow_root->needs_style_update() || shadow_root->child_needs_style_update()) {
auto subtree_invalidation = update_style_recursively(*shadow_root);
if (!is_display_none)
invalidation |= subtree_invalidation;
}
}
}
node.for_each_child([&](auto& child) {
if (needs_full_style_update || child.needs_style_update() || child.child_needs_style_update())
invalidation |= update_style_recursively(child);
if (needs_full_style_update || child.needs_style_update() || child.child_needs_style_update()) {
auto subtree_invalidation = update_style_recursively(child);
if (!is_display_none)
invalidation |= subtree_invalidation;
}
return IterationDecision::Continue;
});
}