Переглянути джерело

LibWeb: Treat :is(.foo) & :where(.foo) as class selectors when bucketing

These are just roundabout ways of writing .foo, so we can still put them
in the rules-by-class bucket and skip running them when the element
doesn't have that class.

Note that :is(.foo .bar) is also bucketed as a class rule, since the
context element must have the `bar` class for the selector to match.

This is a massive speedup on https://vercel.com/ as it cuts the number
of selectors we actually evaluate from 7.0% to 1.9%.
Andreas Kling 11 місяців тому
батько
коміт
ad37c8cd26
1 змінених файлів з 32 додано та 0 видалено
  1. 32 0
      Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

+ 32 - 0
Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -2627,6 +2627,31 @@ void StyleComputer::build_rule_cache_if_needed() const
     const_cast<StyleComputer&>(*this).build_rule_cache();
 }
 
+static Optional<FlyString> is_roundabout_selector_bucketable_as_class(CSS::Selector::SimpleSelector const& simple_selector)
+{
+    if (simple_selector.type != CSS::Selector::SimpleSelector::Type::PseudoClass)
+        return {};
+
+    if (simple_selector.pseudo_class().type != CSS::PseudoClass::Is
+        && simple_selector.pseudo_class().type != CSS::PseudoClass::Where)
+        return {};
+
+    if (simple_selector.pseudo_class().argument_selector_list.size() != 1)
+        return {};
+
+    auto const& argument_selector = *simple_selector.pseudo_class().argument_selector_list.first();
+
+    auto const& compound_selector = argument_selector.compound_selectors().last();
+    if (compound_selector.simple_selectors.size() != 1)
+        return {};
+
+    auto const& inner_simple_selector = compound_selector.simple_selectors.first();
+    if (inner_simple_selector.type != CSS::Selector::SimpleSelector::Type::Class)
+        return {};
+
+    return inner_simple_selector.name();
+}
+
 NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin)
 {
     auto rule_cache = make<RuleCache>();
@@ -2689,6 +2714,13 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
                         added_to_bucket = true;
                         break;
                     }
+                    // NOTE: Selectors like `:is/where(.foo)` and `:is/where(.foo .bar)` are bucketed as class selectors for `foo` and `bar` respectively.
+                    if (auto class_ = is_roundabout_selector_bucketable_as_class(simple_selector); class_.has_value()) {
+                        rule_cache->rules_by_class.ensure(class_.value()).append(move(matching_rule));
+                        ++num_class_rules;
+                        added_to_bucket = true;
+                        break;
+                    }
                     if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) {
                         rule_cache->rules_by_tag_name.ensure(simple_selector.qualified_name().name.lowercase_name).append(move(matching_rule));
                         ++num_tag_name_rules;