LibWeb: Filter :hover selectors early for elements that aren't hovered

Some websites (like vercel.com...) have a *lot* of :hover selectors that
we can simply skip for any element that isn't currently hovered.
This commit is contained in:
Andreas Kling 2024-09-09 15:37:17 +02:00 committed by Andreas Kling
parent 5bb0f43b90
commit ef4f5ac8fb
Notes: github-actions[bot] 2024-09-09 18:13:06 +00:00
4 changed files with 33 additions and 1 deletions

View file

@ -140,7 +140,7 @@ static inline bool matches_link_pseudo_class(DOM::Element const& element)
return element.has_attribute(HTML::AttributeNames::href);
}
static inline bool matches_hover_pseudo_class(DOM::Element const& element)
bool matches_hover_pseudo_class(DOM::Element const& element)
{
auto* hovered_node = element.document().hovered_node();
if (!hovered_node)

View file

@ -21,4 +21,6 @@ bool matches(CSS::Selector const&, Optional<CSS::CSSStyleSheet const&> style_she
[[nodiscard]] bool fast_matches(CSS::Selector const&, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const&, JS::GCPtr<DOM::Element const> shadow_host);
[[nodiscard]] bool can_use_fast_matches(CSS::Selector const&);
[[nodiscard]] bool matches_hover_pseudo_class(DOM::Element const&);
}

View file

@ -356,16 +356,22 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
auto const& rule_cache = rule_cache_for_cascade_origin(cascade_origin);
bool is_hovered = SelectorEngine::matches_hover_pseudo_class(element);
Vector<MatchingRule, 512> rules_to_run;
auto add_rules_to_run = [&](Vector<MatchingRule> const& rules) {
rules_to_run.grow_capacity(rules_to_run.size() + rules.size());
if (pseudo_element.has_value()) {
for (auto const& rule : rules) {
if (rule.must_be_hovered && !is_hovered)
continue;
if (rule.contains_pseudo_element && filter_namespace_rule(element, rule) && filter_layer(qualified_layer_name, rule))
rules_to_run.unchecked_append(rule);
}
} else {
for (auto const& rule : rules) {
if (rule.must_be_hovered && !is_hovered)
continue;
if (!rule.contains_pseudo_element && filter_namespace_rule(element, rule) && filter_layer(qualified_layer_name, rule))
rules_to_run.unchecked_append(rule);
}
@ -2692,6 +2698,7 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
size_t num_pseudo_element_rules = 0;
size_t num_root_rules = 0;
size_t num_attribute_rules = 0;
size_t num_hover_rules = 0;
Vector<MatchingRule> matching_rules;
size_t style_sheet_index = 0;
@ -2711,6 +2718,7 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
cascade_origin,
false,
SelectorEngine::can_use_fast_matches(selector),
false,
};
bool contains_root_pseudo_class = false;
@ -2729,6 +2737,27 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
++num_root_rules;
}
}
if (!matching_rule.must_be_hovered) {
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass && simple_selector.pseudo_class().type == CSS::PseudoClass::Hover) {
matching_rule.must_be_hovered = true;
++num_hover_rules;
}
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass
&& (simple_selector.pseudo_class().type == CSS::PseudoClass::Is
|| simple_selector.pseudo_class().type == CSS::PseudoClass::Where)) {
auto const& argument_selectors = simple_selector.pseudo_class().argument_selector_list;
if (argument_selectors.size() == 1) {
auto const& simple_argument_selector = argument_selectors.first()->compound_selectors().last().simple_selectors.last();
if (simple_argument_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass
&& simple_argument_selector.pseudo_class().type == CSS::PseudoClass::Hover) {
matching_rule.must_be_hovered = true;
++num_hover_rules;
}
}
}
}
}
// NOTE: We traverse the simple selectors in reverse order to make sure that class/ID buckets are preferred over tag buckets

View file

@ -92,6 +92,7 @@ struct MatchingRule {
CascadeOrigin cascade_origin;
bool contains_pseudo_element { false };
bool can_use_fast_matches { false };
bool must_be_hovered { false };
bool skip { false };
};