Sfoglia il codice sorgente

LibWeb: Add per-attribute-name CSS rule buckets

This allows us to skip evaluating selectors like "[foo=bar]" for any
element that doesn't have a "foo" attribute.

Note that the bucket is case-insensitively keyed on the attribute name
since case sensitivity is depending on evaluation context. This ensures
we may get some false positives but no false negatives.

Reduces the number of selectors evaluated by 36% when loading our GitHub
repo at https://github.com/SerenityOS/serenity
Andreas Kling 1 anno fa
parent
commit
77d98b5697

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

@@ -339,6 +339,13 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
         add_rules_to_run(rule_cache.pseudo_element_rules);
         add_rules_to_run(rule_cache.pseudo_element_rules);
     if (element.is_document_element())
     if (element.is_document_element())
         add_rules_to_run(rule_cache.root_rules);
         add_rules_to_run(rule_cache.root_rules);
+
+    element.for_each_attribute([&](auto& name, auto&) {
+        if (auto it = rule_cache.rules_by_attribute_name.find(name); it != rule_cache.rules_by_attribute_name.end()) {
+            add_rules_to_run(it->value);
+        }
+    });
+
     add_rules_to_run(rule_cache.other_rules);
     add_rules_to_run(rule_cache.other_rules);
 
 
     Vector<MatchingRule> matching_rules;
     Vector<MatchingRule> matching_rules;
@@ -2306,6 +2313,7 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
     size_t num_tag_name_rules = 0;
     size_t num_tag_name_rules = 0;
     size_t num_pseudo_element_rules = 0;
     size_t num_pseudo_element_rules = 0;
     size_t num_root_rules = 0;
     size_t num_root_rules = 0;
+    size_t num_attribute_rules = 0;
 
 
     Vector<MatchingRule> matching_rules;
     Vector<MatchingRule> matching_rules;
     size_t style_sheet_index = 0;
     size_t style_sheet_index = 0;
@@ -2363,12 +2371,23 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
                     }
                     }
                 }
                 }
                 if (!added_to_bucket) {
                 if (!added_to_bucket) {
-                    if (matching_rule.contains_pseudo_element)
+                    if (matching_rule.contains_pseudo_element) {
                         rule_cache->pseudo_element_rules.append(move(matching_rule));
                         rule_cache->pseudo_element_rules.append(move(matching_rule));
-                    else if (matching_rule.contains_root_pseudo_class)
+                    } else if (matching_rule.contains_root_pseudo_class) {
                         rule_cache->root_rules.append(move(matching_rule));
                         rule_cache->root_rules.append(move(matching_rule));
-                    else
-                        rule_cache->other_rules.append(move(matching_rule));
+                    } else {
+                        for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) {
+                            if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) {
+                                rule_cache->rules_by_attribute_name.ensure(simple_selector.attribute().qualified_name.name.lowercase_name).append(move(matching_rule));
+                                ++num_attribute_rules;
+                                added_to_bucket = true;
+                                break;
+                            }
+                        }
+                        if (!added_to_bucket) {
+                            rule_cache->other_rules.append(move(matching_rule));
+                        }
+                    }
                 }
                 }
 
 
                 ++selector_index;
                 ++selector_index;
@@ -2415,6 +2434,7 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
         ++style_sheet_index;
         ++style_sheet_index;
     });
     });
 
 
+    size_t total_rules = num_class_rules + num_id_rules + num_tag_name_rules + num_pseudo_element_rules + num_root_rules + num_attribute_rules + rule_cache->other_rules.size();
     if constexpr (LIBWEB_CSS_DEBUG) {
     if constexpr (LIBWEB_CSS_DEBUG) {
         dbgln("Built rule cache!");
         dbgln("Built rule cache!");
         dbgln("           ID: {}", num_id_rules);
         dbgln("           ID: {}", num_id_rules);
@@ -2422,8 +2442,9 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
         dbgln("      TagName: {}", num_tag_name_rules);
         dbgln("      TagName: {}", num_tag_name_rules);
         dbgln("PseudoElement: {}", num_pseudo_element_rules);
         dbgln("PseudoElement: {}", num_pseudo_element_rules);
         dbgln("         Root: {}", num_root_rules);
         dbgln("         Root: {}", num_root_rules);
+        dbgln("    Attribute: {}", num_attribute_rules);
         dbgln("        Other: {}", rule_cache->other_rules.size());
         dbgln("        Other: {}", rule_cache->other_rules.size());
-        dbgln("        Total: {}", num_class_rules + num_id_rules + num_tag_name_rules + rule_cache->other_rules.size());
+        dbgln("        Total: {}", total_rules);
     }
     }
     return rule_cache;
     return rule_cache;
 }
 }

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

@@ -138,6 +138,7 @@ private:
         HashMap<FlyString, Vector<MatchingRule>> rules_by_id;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_id;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_class;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_class;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_tag_name;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_tag_name;
+        HashMap<FlyString, Vector<MatchingRule>, AK::ASCIICaseInsensitiveFlyStringTraits> rules_by_attribute_name;
         Vector<MatchingRule> pseudo_element_rules;
         Vector<MatchingRule> pseudo_element_rules;
         Vector<MatchingRule> root_rules;
         Vector<MatchingRule> root_rules;
         Vector<MatchingRule> other_rules;
         Vector<MatchingRule> other_rules;