From c8f22f65d9631c9da12390eeec05033e6efa76f2 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 9 Sep 2024 13:44:35 +0200 Subject: [PATCH] LibWeb: Filter rules to run before allocating vector of matches By filtering first, we end up allocating much less vector space most of the time. This is mostly helpful in pathological cases where there's a huge number of rules present, but most of them get rejected early. --- .../Libraries/LibWeb/CSS/StyleComputer.cpp | 33 +++++++++++++++---- Userland/Libraries/LibWeb/CSS/StyleComputer.h | 1 + 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 9d218f6a81..a5d8acb4a1 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -395,9 +395,9 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e add_rules_to_run(rule_cache.other_rules); - Vector matching_rules; - matching_rules.ensure_capacity(rules_to_run.size()); - for (auto const& rule_to_run : rules_to_run) { + size_t maximum_match_count = 0; + + for (auto& rule_to_run : rules_to_run) { // FIXME: This needs to be revised when adding support for the ::shadow selector, as it needs to cross shadow boundaries. auto rule_root = rule_to_run.shadow_root; auto from_user_agent_or_user_stylesheet = rule_to_run.cascade_origin == CascadeOrigin::UserAgent || rule_to_run.cascade_origin == CascadeOrigin::User; @@ -412,21 +412,40 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e || (element.is_shadow_host() && rule_root == element.shadow_root()) || from_user_agent_or_user_stylesheet; - if (!rule_is_relevant_for_current_scope) + if (!rule_is_relevant_for_current_scope) { + rule_to_run.skip = true; + continue; + } + + auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index]; + if (should_reject_with_ancestor_filter(*selector)) { + rule_to_run.skip = true; + continue; + } + + ++maximum_match_count; + } + + if (maximum_match_count == 0) + return {}; + + Vector matching_rules; + matching_rules.ensure_capacity(maximum_match_count); + + for (auto const& rule_to_run : rules_to_run) { + if (rule_to_run.skip) continue; // NOTE: When matching an element against a rule from outside the shadow root's style scope, // we have to pass in null for the shadow host, otherwise combinator traversal will // be confined to the element itself (since it refuses to cross the shadow boundary). + auto rule_root = rule_to_run.shadow_root; auto shadow_host_to_use = shadow_host; if (element.is_shadow_host() && rule_root != element.shadow_root()) shadow_host_to_use = nullptr; auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index]; - if (should_reject_with_ancestor_filter(*selector)) - continue; - if (rule_to_run.can_use_fast_matches) { if (!SelectorEngine::fast_matches(selector, *rule_to_run.sheet, element, shadow_host_to_use)) continue; diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index 8398ebaad7..9ad2e83035 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -93,6 +93,7 @@ struct MatchingRule { bool contains_pseudo_element { false }; bool contains_root_pseudo_class { false }; bool can_use_fast_matches { false }; + bool skip { false }; }; struct FontFaceKey {