LibWeb: Narrow :has() style invalidation to ancestor nodes

The current implementation of `:has()` style invalidation is divided
into two cases:
- When used in subject position (e.g., `.a:has(.b)`).
- When in a non-subject position (e.g., `.a > .b:has(.c)`).

This change focuses on improving the first case. For non-subject usage,
we still perform a full tree traversal and invalidate all elements
affected by the `:has()` pseudo-class invalidation set.

We already optimize subject `:has()` invalidations by limiting
invalidated elements to ones that were tested against `has()` selectors
during selector matching. However, selectors like `div:has(.a)`
currently cause every div element in the document to be invalidated.
By modifying the invalidation traversal to consider only ancestor nodes
(and, optionally, their siblings), we can drastically reduce the number
of invalidated elements for broad selectors like the example above.

On Discord, when scrolling through message history, this change allows
to reduce number of invalidated elements from ~1k to ~5.
This commit is contained in:
Aliaksandr Kalenik 2025-02-08 19:09:08 +01:00 committed by Andreas Kling
commit e677ab1699
Notes: github-actions[bot] 2025-02-10 00:15:09 +00:00
7 changed files with 104 additions and 22 deletions

View file

@ -423,6 +423,9 @@ public:
bool affected_by_has_pseudo_class_in_subject_position() const { return m_affected_by_has_pseudo_class_in_subject_position; }
void set_affected_by_has_pseudo_class_in_subject_position(bool value) { m_affected_by_has_pseudo_class_in_subject_position = value; }
bool affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator() const { return m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator; }
void set_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator(bool value) { m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator = value; }
bool affected_by_sibling_combinator() const { return m_affected_by_sibling_combinator; }
void set_affected_by_sibling_combinator(bool value) { m_affected_by_sibling_combinator = value; }
@ -531,6 +534,7 @@ private:
bool m_affected_by_sibling_combinator : 1 { false };
bool m_affected_by_first_or_last_child_pseudo_class : 1 { false };
bool m_affected_by_nth_child_pseudo_class : 1 { false };
bool m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator : 1 { false };
OwnPtr<CSS::CountersSet> m_counters_set;