LibWeb: Avoid unnecessary work when element display property changes

`play_or_cancel_animations_after_display_property_change` is called
whenever an element is inserted or removed, or it's display property
changes, but it is only required to run if we actually have animations
to play or cancel.

Reduces time spent in the aforementioned function from ~2% to ~0.03%
when loading https://en.wikipedia.org/wiki/2023_in_American_television
This commit is contained in:
Callum Law 2025-11-11 16:08:42 +13:00 committed by Tim Ledbetter
parent cfc22a4075
commit 55afa9d37e
Notes: github-actions[bot] 2025-11-11 08:36:51 +00:00
4 changed files with 21 additions and 0 deletions

View file

@ -279,6 +279,19 @@ void Animatable::visit_edges(JS::Cell::Visitor& visitor)
} }
} }
void Animatable::set_has_css_defined_animations()
{
ensure_impl().has_css_defined_animations = true;
}
bool Animatable::has_css_defined_animations() const
{
if (!m_impl)
return false;
return m_impl->has_css_defined_animations;
}
HashMap<FlyString, GC::Ref<Animation>>* Animatable::css_defined_animations(Optional<CSS::PseudoElement> pseudo_element) HashMap<FlyString, GC::Ref<Animation>>* Animatable::css_defined_animations(Optional<CSS::PseudoElement> pseudo_element)
{ {
auto& impl = ensure_impl(); auto& impl = ensure_impl();

View file

@ -52,6 +52,8 @@ public:
void associate_with_animation(GC::Ref<Animation>); void associate_with_animation(GC::Ref<Animation>);
void disassociate_with_animation(GC::Ref<Animation>); void disassociate_with_animation(GC::Ref<Animation>);
void set_has_css_defined_animations();
bool has_css_defined_animations() const;
HashMap<FlyString, GC::Ref<Animation>>* css_defined_animations(Optional<CSS::PseudoElement>); HashMap<FlyString, GC::Ref<Animation>>* css_defined_animations(Optional<CSS::PseudoElement>);
void add_css_animation(FlyString name, Optional<CSS::PseudoElement>, GC::Ref<Animation>); void add_css_animation(FlyString name, Optional<CSS::PseudoElement>, GC::Ref<Animation>);
void remove_css_animation(FlyString name, Optional<CSS::PseudoElement>); void remove_css_animation(FlyString name, Optional<CSS::PseudoElement>);
@ -79,6 +81,7 @@ private:
struct Impl { struct Impl {
Vector<GC::Ref<Animation>> associated_animations; Vector<GC::Ref<Animation>> associated_animations;
bool is_sorted_by_composite_order { true }; bool is_sorted_by_composite_order { true };
bool has_css_defined_animations { false };
mutable Array<OwnPtr<HashMap<FlyString, GC::Ref<Animation>>>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> css_defined_animations; mutable Array<OwnPtr<HashMap<FlyString, GC::Ref<Animation>>>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> css_defined_animations;
mutable Array<OwnPtr<Transition>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> transitions; mutable Array<OwnPtr<Transition>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> transitions;

View file

@ -1266,6 +1266,7 @@ void StyleComputer::process_animation_definitions(ComputedProperties const& comp
} }
effect->set_target(&element); effect->set_target(&element);
element.set_has_css_defined_animations();
element_animations->set(animation_properties.name, animation); element_animations->set(animation_properties.name, animation);
} }

View file

@ -4177,6 +4177,10 @@ FlyString const& Element::html_uppercased_qualified_name() const
void Element::play_or_cancel_animations_after_display_property_change() void Element::play_or_cancel_animations_after_display_property_change()
{ {
// OPTIMIZATION: We don't care about elements with no CSS defined animations
if (!has_css_defined_animations())
return;
// OPTIMIZATION: We don't care about animations in disconnected subtrees. // OPTIMIZATION: We don't care about animations in disconnected subtrees.
if (!is_connected()) if (!is_connected())
return; return;