LibWeb: Skip pending :has() invalidations if there are no :has()

`invalidate_style()` already tries to avoid scheduling invalidation for
`:has()` by checking result of `may_have_has_selectors()`, but it might
still result in unnecessary work because `may_have_has_selectors()`
does not force building of rules cache. This change adds
`have_has_selectors()` that forces building of rules cache and is
invoked in `update_style()` to double-check whether we actually need to
process scheduled `:has()` invalidations.

This allows to skip ~100000 ancestor traversals on this WPT test:
https://wpt.live/html/select/options-length-too-large.html
This commit is contained in:
Aliaksandr Kalenik 2025-02-10 19:03:09 +01:00 committed by Andreas Kling
parent 90ba4b16c2
commit 875a7141a3
Notes: github-actions[bot] 2025-02-11 09:23:18 +00:00
3 changed files with 18 additions and 1 deletions

View file

@ -3172,4 +3172,10 @@ bool StyleComputer::may_have_has_selectors() const
return m_selector_insights->has_has_selectors;
}
bool StyleComputer::have_has_selectors() const
{
build_rule_cache_if_needed();
return m_selector_insights->has_has_selectors;
}
}

View file

@ -177,6 +177,7 @@ public:
void collect_animation_into(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, GC::Ref<Animations::KeyframeEffect> animation, ComputedProperties&, AnimationRefresh = AnimationRefresh::No) const;
[[nodiscard]] bool may_have_has_selectors() const;
[[nodiscard]] bool have_has_selectors() const;
size_t number_of_css_font_faces_with_loading_in_progress() const;

View file

@ -1665,12 +1665,22 @@ void Document::invalidate_style_of_elements_affected_by_has()
return;
}
ScopeGuard clear_pending_nodes_guard = [&] {
m_pending_nodes_for_style_invalidation_due_to_presence_of_has.clear();
};
// It's ok to call have_has_selectors() instead of may_have_has_selectors() here and force
// rule cache build, because it's going to be build soon anyway, since we could get here
// only from update_style().
if (!style_computer().have_has_selectors()) {
return;
}
for (auto const& node : m_pending_nodes_for_style_invalidation_due_to_presence_of_has) {
if (node.is_null())
continue;
node->invalidate_ancestors_affected_by_has_in_subject_position();
}
m_pending_nodes_for_style_invalidation_due_to_presence_of_has.clear();
// Take care of elements that affected by :has() in non-subject position, i.e., ".a:has(.b) > .c".
// Elements affected by :has() in subject position, i.e., ".a:has(.b)", are handled by