From dc8343cc23fc4fc091ce4400f57a195c20868646 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 22 Dec 2024 11:59:58 +0100 Subject: [PATCH] LibWeb: Add mechanism to invalidate only inherited styles We can now mark an element as needing an "inherited style update" rather than a full "style update". This effectively means that the next style update will visit the element and pull all of its inherited properties from the relevant ancestor element. This is now used for descendants of elements with animated style. --- .../LibWeb/Animations/KeyframeEffect.cpp | 17 +++---------- Libraries/LibWeb/CSS/StyleComputer.h | 3 ++- Libraries/LibWeb/DOM/Document.cpp | 5 +++- Libraries/LibWeb/DOM/Element.cpp | 25 +++++++++++++++++++ Libraries/LibWeb/DOM/Element.h | 1 + Libraries/LibWeb/DOM/Node.cpp | 16 ++++++++++++ Libraries/LibWeb/DOM/Node.h | 4 +++ 7 files changed, 55 insertions(+), 16 deletions(-) diff --git a/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Libraries/LibWeb/Animations/KeyframeEffect.cpp index 1f1684fb492..8d0986c261a 100644 --- a/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -935,25 +935,14 @@ void KeyframeEffect::update_computed_properties() auto& document = target->document(); document.style_computer().collect_animation_into(*target, pseudo_element_type(), *this, *style, CSS::StyleComputer::AnimationRefresh::Yes); + auto invalidation = compute_required_invalidation(animated_properties_before_update, style->animated_property_values()); + // Traversal of the subtree is necessary to update the animated properties inherited from the target element. target->for_each_in_subtree_of_type([&](auto& element) { - auto element_style = element.computed_properties(); - if (!element_style || !element.layout_node()) - 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(i))) { - auto new_value = CSS::StyleComputer::get_inherit_value(static_cast(i), &element); - element_style->set_property(static_cast(i), *new_value, CSS::ComputedProperties::Inherited::Yes); - } - } - - element.layout_node()->apply_style(*element_style); + invalidation |= element.recompute_inherited_style(); return TraversalDecision::Continue; }); - auto invalidation = compute_required_invalidation(animated_properties_before_update, style->animated_property_values()); - if (!pseudo_element_type().has_value()) { if (target->layout_node()) target->layout_node()->apply_style(*style); diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index c3566b3384d..aa874338d7b 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -175,6 +175,8 @@ public: [[nodiscard]] GC::Ref compute_properties(DOM::Element&, Optional, CascadedProperties&) const; + void absolutize_values(ComputedProperties&) const; + private: enum class ComputeStyleMode { Normal, @@ -194,7 +196,6 @@ private: void compute_math_depth(ComputedProperties&, DOM::Element const*, Optional) const; void compute_defaulted_values(ComputedProperties&, DOM::Element const*, Optional) const; void start_needed_transitions(ComputedProperties const& old_style, ComputedProperties& new_style, DOM::Element&, Optional) const; - void absolutize_values(ComputedProperties&) const; void resolve_effective_overflow_values(ComputedProperties&) const; void transform_box_type_if_needed(ComputedProperties&, DOM::Element const&, Optional) const; diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index ca3b69549b1..78c8ac5be3c 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1297,10 +1297,13 @@ void Document::update_layout() if (is(node)) { if (needs_full_style_update || node.needs_style_update()) { invalidation |= static_cast(node).recompute_style(); + } else if (node.needs_inherited_style_update()) { + invalidation |= static_cast(node).recompute_inherited_style(); } is_display_none = static_cast(node).computed_properties()->display().is_none(); } node.set_needs_style_update(false); + node.set_needs_inherited_style_update(false); if (needs_full_style_update || node.child_needs_style_update()) { if (node.is_element()) { @@ -1314,7 +1317,7 @@ void Document::update_layout() } node.for_each_child([&](auto& child) { - if (needs_full_style_update || child.needs_style_update() || child.child_needs_style_update()) { + if (needs_full_style_update || child.needs_style_update() || child.needs_inherited_style_update() || child.child_needs_style_update()) { auto subtree_invalidation = update_style_recursively(child, style_computer); if (!is_display_none) invalidation |= subtree_invalidation; diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 89f751f36ce..132f3b7f9dc 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -554,6 +554,31 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style() return invalidation; } +CSS::RequiredInvalidationAfterStyleChange Element::recompute_inherited_style() +{ + // Traversal of the subtree is necessary to update the animated properties inherited from the target element. + auto computed_properties = this->computed_properties(); + if (!computed_properties || !layout_node()) + return {}; + + CSS::RequiredInvalidationAfterStyleChange invalidation; + + for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) { + auto property_id = static_cast(i); + if (!computed_properties->is_property_inherited(property_id)) + continue; + RefPtr old_value = computed_properties->maybe_null_property(property_id); + RefPtr new_value = CSS::StyleComputer::get_inherit_value(property_id, this); + computed_properties->set_property(property_id, *new_value, CSS::ComputedProperties::Inherited::Yes); + invalidation |= CSS::compute_property_invalidation(property_id, old_value, new_value); + } + + document().style_computer().absolutize_values(*computed_properties); + + layout_node()->apply_style(*computed_properties); + return invalidation; +} + GC::Ref Element::resolved_css_values(Optional type) { auto element_computed_style = CSS::ResolvedCSSStyleDeclaration::create(*this, type); diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 96dd9472b1d..216bfa6f24a 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -178,6 +178,7 @@ public: void run_attribute_change_steps(FlyString const& local_name, Optional const& old_value, Optional const& value, Optional const& namespace_); CSS::RequiredInvalidationAfterStyleChange recompute_style(); + CSS::RequiredInvalidationAfterStyleChange recompute_inherited_style(); Optional use_pseudo_element() const { return m_use_pseudo_element; } void set_use_pseudo_element(Optional use_pseudo_element) { m_use_pseudo_element = move(use_pseudo_element); } diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index 4b458e032fc..ade0af01b85 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -1249,6 +1249,22 @@ EventTarget* Node::get_parent(Event const&) return parent(); } +void Node::set_needs_inherited_style_update(bool value) +{ + if (m_needs_inherited_style_update == value) + return; + m_needs_inherited_style_update = value; + + if (m_needs_inherited_style_update) { + for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host()) { + if (ancestor->m_child_needs_style_update) + break; + ancestor->m_child_needs_style_update = true; + } + document().schedule_style_update(); + } +} + void Node::set_needs_style_update(bool value) { if (m_needs_style_update == value) diff --git a/Libraries/LibWeb/DOM/Node.h b/Libraries/LibWeb/DOM/Node.h index 92fa32e31f2..dfe51be8a49 100644 --- a/Libraries/LibWeb/DOM/Node.h +++ b/Libraries/LibWeb/DOM/Node.h @@ -275,6 +275,9 @@ public: bool needs_style_update() const { return m_needs_style_update; } void set_needs_style_update(bool); + bool needs_inherited_style_update() const { return m_needs_inherited_style_update; } + void set_needs_inherited_style_update(bool); + bool child_needs_style_update() const { return m_child_needs_style_update; } void set_child_needs_style_update(bool b) { m_child_needs_style_update = b; } @@ -746,6 +749,7 @@ protected: GC::Ptr m_paintable; NodeType m_type { NodeType::INVALID }; bool m_needs_style_update { false }; + bool m_needs_inherited_style_update { false }; bool m_child_needs_style_update { false }; UniqueNodeID m_unique_id;