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%.
This commit is contained in:
Andreas Kling 2024-09-09 10:44:54 +02:00 committed by Andreas Kling
parent 4ba38c55d6
commit ad37c8cd26
Notes: github-actions[bot] 2024-09-09 10:48:01 +00:00

View file

@ -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;