LibWeb: Invalidate less elements affected by CSS custom properties

Before this change, whenever element's attributes changed, we would add
a flag to "pending invalidation", indicating that all descendants whose
style uses CSS custom properties needed to be recomputed. This resulted
in severe overinvalidation, because we would run invalidation regardless
of whether any custom property on affected element actually changed.

This change takes another approach, and now we decide whether
descendant's style needs to be recomputed based on whether ancestor's
style recomputation results in a change of custom properties, though
this approach adds a little overhead to style computation as now we have
to compare old vs new hashmap of custom properties.

This brings substantial improvement on discord and x.com where, before
this change, advantage of using invalidation sets was lost and we had
to recompute all descendants, because almost all of them use custom
properties.
This commit is contained in:
Aliaksandr Kalenik 2025-07-27 19:41:21 +02:00 committed by Andreas Kling
commit d1fbb7b51e
Notes: github-actions[bot] 2025-07-30 09:07:23 +00:00
13 changed files with 71 additions and 67 deletions

View file

@ -201,7 +201,7 @@ public:
void run_attribute_change_steps(FlyString const& local_name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_);
CSS::RequiredInvalidationAfterStyleChange recompute_style();
CSS::RequiredInvalidationAfterStyleChange recompute_style(bool& did_change_custom_properties);
CSS::RequiredInvalidationAfterStyleChange recompute_inherited_style();
Optional<CSS::PseudoElement> use_pseudo_element() const { return m_use_pseudo_element; }
@ -256,8 +256,10 @@ public:
void set_custom_properties(Optional<CSS::PseudoElement>, HashMap<FlyString, CSS::StyleProperty> custom_properties);
[[nodiscard]] HashMap<FlyString, CSS::StyleProperty> const& custom_properties(Optional<CSS::PseudoElement>) const;
bool style_uses_css_custom_properties() const { return m_style_uses_css_custom_properties; }
void set_style_uses_css_custom_properties(bool value) { m_style_uses_css_custom_properties = value; }
bool style_uses_attr_css_function() const { return m_style_uses_attr_css_function; }
void set_style_uses_attr_css_function() { m_style_uses_attr_css_function = true; }
bool style_uses_var_css_function() const { return m_style_uses_var_css_function; }
void set_style_uses_var_css_function() { m_style_uses_var_css_function = true; }
// NOTE: The function is wrapped in a GC::HeapFunction immediately.
HTML::TaskID queue_an_element_task(HTML::Task::Source, Function<void()>);
@ -605,7 +607,8 @@ private:
bool m_in_top_layer : 1 { false };
bool m_rendered_in_top_layer : 1 { false };
bool m_style_uses_css_custom_properties : 1 { false };
bool m_style_uses_attr_css_function : 1 { false };
bool m_style_uses_var_css_function : 1 { false };
bool m_affected_by_has_pseudo_class_in_subject_position : 1 { false };
bool m_affected_by_has_pseudo_class_in_non_subject_position : 1 { false };
bool m_affected_by_direct_sibling_combinator : 1 { false };