浏览代码

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.
Andreas Kling 10 月之前
父节点
当前提交
c8f22f65d9
共有 2 个文件被更改,包括 27 次插入7 次删除
  1. 26 7
      Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
  2. 1 0
      Userland/Libraries/LibWeb/CSS/StyleComputer.h

+ 26 - 7
Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -395,9 +395,9 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
 
 
     add_rules_to_run(rule_cache.other_rules);
     add_rules_to_run(rule_cache.other_rules);
 
 
-    Vector<MatchingRule> 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.
         // 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 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;
         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<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
             || (element.is_shadow_host() && rule_root == element.shadow_root())
             || (element.is_shadow_host() && rule_root == element.shadow_root())
             || from_user_agent_or_user_stylesheet;
             || 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<MatchingRule> matching_rules;
+    matching_rules.ensure_capacity(maximum_match_count);
+
+    for (auto const& rule_to_run : rules_to_run) {
+        if (rule_to_run.skip)
             continue;
             continue;
 
 
         // NOTE: When matching an element against a rule from outside the shadow root's style scope,
         // 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
         //       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).
         //       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;
         auto shadow_host_to_use = shadow_host;
         if (element.is_shadow_host() && rule_root != element.shadow_root())
         if (element.is_shadow_host() && rule_root != element.shadow_root())
             shadow_host_to_use = nullptr;
             shadow_host_to_use = nullptr;
 
 
         auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index];
         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 (rule_to_run.can_use_fast_matches) {
             if (!SelectorEngine::fast_matches(selector, *rule_to_run.sheet, element, shadow_host_to_use))
             if (!SelectorEngine::fast_matches(selector, *rule_to_run.sheet, element, shadow_host_to_use))
                 continue;
                 continue;

+ 1 - 0
Userland/Libraries/LibWeb/CSS/StyleComputer.h

@@ -93,6 +93,7 @@ struct MatchingRule {
     bool contains_pseudo_element { false };
     bool contains_pseudo_element { false };
     bool contains_root_pseudo_class { false };
     bool contains_root_pseudo_class { false };
     bool can_use_fast_matches { false };
     bool can_use_fast_matches { false };
+    bool skip { false };
 };
 };
 
 
 struct FontFaceKey {
 struct FontFaceKey {