From 875a7141a38d47e8553b740f55280518b9e3a589 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 10 Feb 2025 19:03:09 +0100 Subject: [PATCH] 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 --- Libraries/LibWeb/CSS/StyleComputer.cpp | 6 ++++++ Libraries/LibWeb/CSS/StyleComputer.h | 1 + Libraries/LibWeb/DOM/Document.cpp | 12 +++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index b74e5e212c4..7562d51fb3b 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -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; +} + } diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index 3d26c17070c..544fb95873f 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -177,6 +177,7 @@ public: void collect_animation_into(DOM::Element&, Optional, GC::Ref 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; diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 40f5eceeea6..d9e3c22ea66 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -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