From a9b8840a829b2e7f98984c639b54e8427cf28570 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 16 Mar 2024 07:44:48 +0100 Subject: [PATCH] LibWeb: Add fast path for animated style properties update Patch up existing style properties instead of using the regular style invalidation path, which requires rule matching for each element in the invalidated subtree. - !important properties: this change introduces a flag used to skip the update of animated properties overridden by !important. - inherited animated properties: for now, these are invalidated by traversing animated element's subtree to propagate the update. - StyleProperties has a separate array for animated properties that allows the removal animated properties after animation has ended, without requiring full style invalidation. --- .../Libraries/LibWeb/Animations/Animation.cpp | 5 +- .../LibWeb/Animations/AnimationEffect.h | 2 + .../LibWeb/Animations/KeyframeEffect.cpp | 54 +++++++++++++++++++ .../LibWeb/Animations/KeyframeEffect.h | 2 + .../Libraries/LibWeb/CSS/StyleComputer.cpp | 43 +++++++++------ Userland/Libraries/LibWeb/CSS/StyleComputer.h | 13 +++-- .../Libraries/LibWeb/CSS/StyleProperties.cpp | 44 ++++++++++++++- .../Libraries/LibWeb/CSS/StyleProperties.h | 24 ++++++++- Userland/Libraries/LibWeb/DOM/Document.cpp | 16 +++++- Userland/Libraries/LibWeb/DOM/Element.cpp | 19 ++++--- Userland/Libraries/LibWeb/DOM/Element.h | 4 ++ 11 files changed, 194 insertions(+), 32 deletions(-) diff --git a/Userland/Libraries/LibWeb/Animations/Animation.cpp b/Userland/Libraries/LibWeb/Animations/Animation.cpp index cb6a503fcc1..a64b851ee40 100644 --- a/Userland/Libraries/LibWeb/Animations/Animation.cpp +++ b/Userland/Libraries/LibWeb/Animations/Animation.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -1315,8 +1316,8 @@ JS::NonnullGCPtr Animation::current_finished_promise() const void Animation::invalidate_effect() { if (m_effect) { - if (auto target = m_effect->target()) - target->invalidate_style(); + if (auto target = m_effect->target(); target && target->paintable()) + target->paintable()->set_needs_display(); } } diff --git a/Userland/Libraries/LibWeb/Animations/AnimationEffect.h b/Userland/Libraries/LibWeb/Animations/AnimationEffect.h index ec6e2454fe8..1e0a061862a 100644 --- a/Userland/Libraries/LibWeb/Animations/AnimationEffect.h +++ b/Userland/Libraries/LibWeb/Animations/AnimationEffect.h @@ -146,6 +146,8 @@ public: virtual DOM::Element* target() const { return {}; } virtual bool is_keyframe_effect() const { return false; } + virtual void update_style_properties() = 0; + protected: AnimationEffect(JS::Realm&); virtual ~AnimationEffect() = default; diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp index 9fdc1a65223..71a12cb2dbb 100644 --- a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include namespace Web::Animations { @@ -869,4 +871,56 @@ void KeyframeEffect::visit_edges(Cell::Visitor& visitor) visitor.visit(keyframe); } +void KeyframeEffect::update_style_properties() +{ + if (!target()) + return; + + if (pseudo_element_type().has_value()) { + // StyleProperties are not saved for pseudo-elements so there is nothing to patch + target()->invalidate_style(); + return; + } + + auto* style = target()->computed_css_values(); + if (!style) + return; + + auto style_before_animation_update = style->clone(); + + auto& document = target()->document(); + document.style_computer().collect_animation_into(*this, *style, CSS::StyleComputer::AnimationRefresh::Yes); + + // 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_css_values(); + if (!element_style || !element.layout_node()) + return IterationDecision::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(document.realm(), static_cast(i), &element); + element_style->set_property(static_cast(i), *new_value, nullptr, CSS::StyleProperties::Inherited::Yes); + } + } + + element.layout_node()->apply_style(*element_style); + return IterationDecision::Continue; + }); + + auto invalidation = DOM::Element::compute_required_invalidation(style_before_animation_update, *style); + + if (target()->layout_node()) + target()->layout_node()->apply_style(*style); + + if (invalidation.relayout) + document.set_needs_layout(); + if (invalidation.rebuild_layout_tree) + document.invalidate_layout(); + if (invalidation.repaint) + document.set_needs_to_resolve_paint_only_properties(); + if (invalidation.rebuild_stacking_context_tree) + document.invalidate_stacking_context_tree(); +} + } diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h index 5b1707962e8..5ce0e59e647 100644 --- a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h @@ -103,6 +103,8 @@ public: virtual bool is_keyframe_effect() const override { return true; } + virtual void update_style_properties() override; + private: KeyframeEffect(JS::Realm&); virtual ~KeyframeEffect() override = default; diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index a59c636ff02..e8b41cd170a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -90,7 +90,6 @@ struct Traits : public DefaultTraits); -static NonnullRefPtr get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID, DOM::Element const*, Optional); StyleComputer::StyleComputer(DOM::Document& document) : m_document(document) @@ -668,26 +667,27 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i set_longhand_property(property_id, value); } -void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert) +void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important important) { for_each_property_expanding_shorthands(property_id, value, [&](PropertyID shorthand_id, StyleValue const& shorthand_value) { if (shorthand_value.is_revert()) { auto& property_in_previous_cascade_origin = properties_for_revert[to_underlying(shorthand_id)]; if (property_in_previous_cascade_origin.has_value()) - style.set_property(shorthand_id, property_in_previous_cascade_origin->style, property_in_previous_cascade_origin->declaration); + style.set_property(shorthand_id, property_in_previous_cascade_origin->style, property_in_previous_cascade_origin->declaration, StyleProperties::Inherited::No, important); } else { - style.set_property(shorthand_id, shorthand_value, declaration); + style.set_property(shorthand_id, shorthand_value, declaration, StyleProperties::Inherited::No, important); } }); } -void StyleComputer::set_all_properties(DOM::Element& element, Optional pseudo_element, StyleProperties& style, StyleValue const& value, DOM::Document& document, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert) const +void StyleComputer::set_all_properties(DOM::Element& element, Optional pseudo_element, StyleProperties& style, StyleValue const& value, DOM::Document& document, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important important) const { for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) { auto property_id = (CSS::PropertyID)i; if (value.is_revert()) { style.m_property_values[to_underlying(property_id)] = properties_for_revert[to_underlying(property_id)]; + style.m_property_values[to_underlying(property_id)]->important = important; continue; } @@ -696,6 +696,7 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optionalimportant = important; continue; } @@ -705,7 +706,9 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optionalis_unresolved()) set_property_expanding_shorthands(style, property_id, property_value, declaration, properties_for_revert); - set_property_expanding_shorthands(style, property_id, value, declaration, properties_for_revert); + style.m_property_values[to_underlying(property_id)]->important = important; + + set_property_expanding_shorthands(style, property_id, value, declaration, properties_for_revert, important); } } @@ -719,7 +722,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e continue; if (property.property_id == CSS::PropertyID::All) { - set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), properties_for_revert); + set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); continue; } @@ -727,7 +730,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e if (property.value->is_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), properties_for_revert); + set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); } } @@ -738,7 +741,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e continue; if (property.property_id == CSS::PropertyID::All) { - set_all_properties(element, pseudo_element, style, property.value, m_document, inline_style, properties_for_revert); + set_all_properties(element, pseudo_element, style, property.value, m_document, inline_style, properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); continue; } @@ -746,7 +749,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e if (property.value->is_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(style, property.property_id, property_value, inline_style, properties_for_revert); + set_property_expanding_shorthands(style, property.property_id, property_value, inline_style, properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No); } } } @@ -1276,7 +1279,7 @@ static ValueComparingRefPtr interpolate_property(DOM::Element& } } -void StyleComputer::collect_animation_into(JS::NonnullGCPtr effect, StyleProperties& style_properties) const +void StyleComputer::collect_animation_into(JS::NonnullGCPtr effect, StyleProperties& style_properties, AnimationRefresh refresh) const { auto animation = effect->associated_animation(); if (!animation) @@ -1329,6 +1332,8 @@ void StyleComputer::collect_animation_into(JS::NonnullGCPtr RefPtr { + if (refresh == AnimationRefresh::Yes) + return {}; return style_properties.maybe_null_property(it.key); }, [&](RefPtr value) { return value; }); @@ -1339,7 +1344,7 @@ void StyleComputer::collect_animation_into(JS::NonnullGCPtrto_string()); } continue; @@ -1356,13 +1361,17 @@ void StyleComputer::collect_animation_into(JS::NonnullGCPtrtarget(), it.key, *start, *end, progress_in_keyframe)) { dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} = {}", string_from_property_id(it.key), progress_in_keyframe, start->to_string(), end->to_string(), next_value->to_string()); - style_properties.set_property(it.key, *next_value); + style_properties.set_animated_property(it.key, *next_value); } else { // If interpolate_property() fails, the element should not be rendered dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} is invalid", string_from_property_id(it.key), progress_in_keyframe, start->to_string(), end->to_string()); - style_properties.set_property(PropertyID::Visibility, IdentifierStyleValue::create(ValueID::Hidden)); + style_properties.set_animated_property(PropertyID::Visibility, IdentifierStyleValue::create(ValueID::Hidden)); } } } @@ -1570,7 +1579,7 @@ DOM::Element const* element_to_inherit_style_from(DOM::Element const* element, O return parent_element; } -NonnullRefPtr get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID property_id, DOM::Element const* element, Optional pseudo_element) +NonnullRefPtr StyleComputer::get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID property_id, DOM::Element const* element, Optional pseudo_element) { auto* parent_element = element_to_inherit_style_from(element, pseudo_element); @@ -1586,7 +1595,7 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM auto& value_slot = style.m_property_values[to_underlying(property_id)]; if (!value_slot.has_value()) { if (is_inherited_property(property_id)) - style.m_property_values[to_underlying(property_id)] = { { get_inherit_value(document().realm(), property_id, element, pseudo_element), nullptr } }; + style.m_property_values[to_underlying(property_id)] = { { get_inherit_value(document().realm(), property_id, element, pseudo_element), nullptr, StyleProperties::Important::No, StyleProperties::Inherited::Yes } }; else style.m_property_values[to_underlying(property_id)] = { { property_initial_value(document().realm(), property_id), nullptr } }; return; @@ -1599,6 +1608,7 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM if (value_slot->style->is_inherit()) { value_slot->style = get_inherit_value(document().realm(), property_id, element, pseudo_element); + value_slot->inherited = StyleProperties::Inherited::Yes; return; } @@ -1608,6 +1618,7 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM if (is_inherited_property(property_id)) { // then if it is an inherited property, this is treated as inherit, value_slot->style = get_inherit_value(document().realm(), property_id, element, pseudo_element); + value_slot->inherited = StyleProperties::Inherited::Yes; } else { // and if it is not, this is treated as initial. value_slot->style = property_initial_value(document().realm(), property_id); diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index cf5d60ffdd3..61e14f4f110 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -55,7 +55,8 @@ struct FontFaceKey { class StyleComputer { public: static void for_each_property_expanding_shorthands(PropertyID, StyleValue const&, Function const& set_longhand_property); - static void set_property_expanding_shorthands(StyleProperties&, PropertyID, StyleValue const&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert); + static void set_property_expanding_shorthands(StyleProperties&, PropertyID, StyleValue const&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important = StyleProperties::Important::No); + static NonnullRefPtr get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID, DOM::Element const*, Optional = {}); explicit StyleComputer(DOM::Document&); ~StyleComputer(); @@ -82,6 +83,12 @@ public: void set_viewport_rect(Badge, CSSPixelRect const& viewport_rect) { m_viewport_rect = viewport_rect; } + enum class AnimationRefresh { + No, + Yes, + }; + void collect_animation_into(JS::NonnullGCPtr animation, StyleProperties& style_properties, AnimationRefresh = AnimationRefresh::No) const; + private: enum class ComputeStyleMode { Normal, @@ -105,7 +112,7 @@ private: void compute_defaulted_property_value(StyleProperties&, DOM::Element const*, CSS::PropertyID, Optional) const; - void set_all_properties(DOM::Element&, Optional, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert) const; + void set_all_properties(DOM::Element&, Optional, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important = StyleProperties::Important::No) const; template void for_each_stylesheet(CascadeOrigin, Callback) const; @@ -142,8 +149,6 @@ private: RuleCache const& rule_cache_for_cascade_origin(CascadeOrigin) const; - void collect_animation_into(JS::NonnullGCPtr animation, StyleProperties& style_properties) const; - OwnPtr m_author_rule_cache; OwnPtr m_user_rule_cache; OwnPtr m_user_agent_rule_cache; diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 794365752e0..3448fcf2ab5 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -34,13 +34,49 @@ namespace Web::CSS { -void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr value, CSS::CSSStyleDeclaration const* source_declaration) +NonnullRefPtr StyleProperties::clone() const { - m_property_values[to_underlying(id)] = StyleAndSourceDeclaration { move(value), source_declaration }; + auto clone = adopt_ref(*new StyleProperties); + clone->m_property_values = m_property_values; + clone->m_animated_property_values = m_animated_property_values; + clone->m_font_list = m_font_list; + clone->m_line_height = m_line_height; + clone->m_math_depth = m_math_depth; + return clone; +} + +bool StyleProperties::is_property_important(CSS::PropertyID property_id) const +{ + return m_property_values[to_underlying(property_id)].has_value() && m_property_values[to_underlying(property_id)]->important == Important::Yes; +} + +bool StyleProperties::is_property_inherited(CSS::PropertyID property_id) const +{ + return m_property_values[to_underlying(property_id)].has_value() && m_property_values[to_underlying(property_id)]->inherited == Inherited::Yes; +} + +void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr value, CSS::CSSStyleDeclaration const* source_declaration, Inherited inherited, Important important) +{ + m_property_values[to_underlying(id)] = StyleAndSourceDeclaration { move(value), source_declaration, important, inherited }; +} + +void StyleProperties::set_animated_property(CSS::PropertyID id, NonnullRefPtr value) +{ + m_animated_property_values[to_underlying(id)] = move(value); +} + +void StyleProperties::reset_animated_properties() +{ + for (auto& animated_property : m_animated_property_values) + animated_property.clear(); } NonnullRefPtr StyleProperties::property(CSS::PropertyID property_id) const { + auto animated_value = m_animated_property_values[to_underlying(property_id)]; + if (animated_value.has_value()) + return *animated_value; + auto value = m_property_values[to_underlying(property_id)]; // By the time we call this method, all properties have values assigned. VERIFY(value.has_value()); @@ -49,6 +85,10 @@ NonnullRefPtr StyleProperties::property(CSS::PropertyID proper RefPtr StyleProperties::maybe_null_property(CSS::PropertyID property_id) const { + auto animated_value = m_animated_property_values[to_underlying(property_id)]; + if (animated_value.has_value()) + return *animated_value; + auto value = m_property_values[to_underlying(property_id)]; if (value.has_value()) return value->style; diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index 7518f6fa894..8f04378d355 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -23,6 +23,8 @@ public: static NonnullRefPtr create() { return adopt_ref(*new StyleProperties); } + NonnullRefPtr clone() const; + template inline void for_each_property(Callback callback) const { @@ -32,16 +34,34 @@ public: } } + enum class Important { + No, + Yes + }; + + enum class Inherited { + No, + Yes + }; + struct StyleAndSourceDeclaration { NonnullRefPtr style; CSS::CSSStyleDeclaration const* declaration = nullptr; + Important important { Important::No }; + Inherited inherited { Inherited::No }; }; using PropertyValues = Array, to_underlying(CSS::last_property_id) + 1>; auto& properties() { return m_property_values; } auto const& properties() const { return m_property_values; } - void set_property(CSS::PropertyID, NonnullRefPtr value, CSS::CSSStyleDeclaration const* source_declaration = nullptr); + void reset_animated_properties(); + + bool is_property_important(CSS::PropertyID property_id) const; + bool is_property_inherited(CSS::PropertyID property_id) const; + + void set_property(CSS::PropertyID, NonnullRefPtr value, CSS::CSSStyleDeclaration const* source_declaration = nullptr, Inherited = Inherited::No, Important = Important::No); + void set_animated_property(CSS::PropertyID, NonnullRefPtr value); NonnullRefPtr property(CSS::PropertyID) const; RefPtr maybe_null_property(CSS::PropertyID) const; CSS::CSSStyleDeclaration const* property_source_declaration(CSS::PropertyID) const; @@ -166,6 +186,8 @@ private: friend class StyleComputer; PropertyValues m_property_values; + Array>, to_underlying(CSS::last_property_id) + 1> m_animated_property_values; + Optional overflow(CSS::PropertyID) const; Vector shadow(CSS::PropertyID, Layout::Node const&) const; diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 5a0da0a2b90..5d0c0be217f 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -1163,6 +1163,19 @@ void Document::update_style() { if (!browsing_context()) return; + + for (auto& timeline : m_associated_animation_timelines) { + for (auto& animation : timeline->associated_animations()) { + if (auto effect = animation->effect(); effect && effect->target()) + effect->target()->reset_animated_css_properties(); + } + + for (auto& animation : timeline->associated_animations()) { + if (auto effect = animation->effect()) + effect->update_style_properties(); + } + } + if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update()) return; @@ -1917,7 +1930,8 @@ void Document::dispatch_events_for_animation_if_necessary(JS::NonnullGCPtr(*animation); - css_animation.owning_element()->set_needs_style_update(true); + if (auto target = effect->target(); target && target->paintable()) + target->paintable()->set_needs_display(); auto previous_phase = effect->previous_phase(); auto current_phase = effect->phase(); diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 4b04d89bcfa..525081f6e33 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -510,7 +510,7 @@ void Element::attribute_changed(FlyString const& name, Optional const& v } } -static Element::RequiredInvalidationAfterStyleChange compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style) +Element::RequiredInvalidationAfterStyleChange Element::compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style) { Element::RequiredInvalidationAfterStyleChange invalidation; @@ -519,12 +519,12 @@ static Element::RequiredInvalidationAfterStyleChange compute_required_invalidati for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) { auto property_id = static_cast(i); - auto const& old_value = old_style.properties()[i]; - auto const& new_value = new_style.properties()[i]; - if (!old_value.has_value() && !new_value.has_value()) + auto old_value = old_style.maybe_null_property(property_id); + auto new_value = new_style.maybe_null_property(property_id); + if (!old_value && !new_value) continue; - bool const property_value_changed = (!old_value.has_value() || !new_value.has_value()) || *old_value->style != *new_value->style; + bool const property_value_changed = (!old_value || !new_value) || *old_value != *new_value; if (!property_value_changed) continue; @@ -544,7 +544,7 @@ static Element::RequiredInvalidationAfterStyleChange compute_required_invalidati // OPTIMIZATION: Special handling for CSS `visibility`: if (property_id == CSS::PropertyID::Visibility) { // We don't need to relayout if the visibility changes from visible to hidden or vice versa. Only collapse requires relayout. - if ((old_value.has_value() && old_value->style->to_identifier() == CSS::ValueID::Collapse) != (new_value.has_value() && new_value->style->to_identifier() == CSS::ValueID::Collapse)) + if ((old_value && old_value->to_identifier() == CSS::ValueID::Collapse) != (new_value && new_value->to_identifier() == CSS::ValueID::Collapse)) invalidation.relayout = true; // Of course, we still have to repaint on any visibility change. invalidation.repaint = true; @@ -624,6 +624,13 @@ NonnullRefPtr Element::resolved_css_values() return properties; } +void Element::reset_animated_css_properties() +{ + if (!m_computed_css_values) + return; + m_computed_css_values->reset_animated_properties(); +} + DOMTokenList* Element::class_list() { if (!m_class_list) diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 904de8fe331..e70eecd04e5 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -166,6 +166,8 @@ public: static RequiredInvalidationAfterStyleChange full() { return { true, true, true, true }; } }; + static Element::RequiredInvalidationAfterStyleChange compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style); + RequiredInvalidationAfterStyleChange recompute_style(); Optional use_pseudo_element() const { return m_use_pseudo_element; } @@ -179,6 +181,8 @@ public: void set_computed_css_values(RefPtr); NonnullRefPtr resolved_css_values(); + void reset_animated_css_properties(); + CSS::CSSStyleDeclaration const* inline_style() const; CSS::CSSStyleDeclaration* style_for_bindings();