mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibWeb: Optimize :hover style invalidation
Instead of recalculating styles for all nodes in the common ancestor of the new and old hovered nodes' subtrees, this change introduces the following approach: - While calculating ComputedProperties, a flag is saved if any rule applied to an element is affected by the hover state during the execution of SelectorEngine::matches(). - When the hovered element changes, styles are marked for recalculation only if the flag saved in ComputedProperties indicates that the element could be affected by the hover state.
This commit is contained in:
parent
db58986e5f
commit
e465e922bd
Notes:
github-actions[bot]
2025-01-04 19:33:47 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: e465e922bd
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3121
9 changed files with 124 additions and 77 deletions
|
@ -422,7 +422,7 @@ bool StyleComputer::should_reject_with_ancestor_filter(Selector const& selector)
|
|||
return false;
|
||||
}
|
||||
|
||||
Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, FlyString const& qualified_layer_name) const
|
||||
Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name) const
|
||||
{
|
||||
auto const& root_node = element.root();
|
||||
auto shadow_root = is<DOM::ShadowRoot>(root_node) ? static_cast<DOM::ShadowRoot const*>(&root_node) : nullptr;
|
||||
|
@ -435,22 +435,16 @@ 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);
|
||||
}
|
||||
|
@ -537,11 +531,16 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
|
|||
|
||||
auto const& selector = rule_to_run.absolutized_selectors()[rule_to_run.selector_index];
|
||||
|
||||
SelectorEngine::MatchContext context { .style_sheet_for_rule = *rule_to_run.sheet };
|
||||
ScopeGuard guard = [&] {
|
||||
if (context.did_match_any_hover_rules)
|
||||
did_match_any_hover_rules = true;
|
||||
};
|
||||
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, element, shadow_host_to_use, context))
|
||||
continue;
|
||||
} else {
|
||||
if (!SelectorEngine::matches(selector, *rule_to_run.sheet, element, shadow_host_to_use, pseudo_element))
|
||||
if (!SelectorEngine::matches(selector, element, shadow_host_to_use, context, pseudo_element))
|
||||
continue;
|
||||
}
|
||||
matching_rules.append(rule_to_run);
|
||||
|
@ -1488,24 +1487,24 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
|||
|
||||
// https://www.w3.org/TR/css-cascade/#cascading
|
||||
// https://drafts.csswg.org/css-cascade-5/#layering
|
||||
GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, bool& did_match_any_pseudo_element_rules, ComputeStyleMode mode) const
|
||||
GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, bool& did_match_any_pseudo_element_rules, bool& did_match_any_hover_rules, ComputeStyleMode mode) const
|
||||
{
|
||||
auto cascaded_properties = m_document->heap().allocate<CascadedProperties>();
|
||||
|
||||
// First, we collect all the CSS rules whose selectors match `element`:
|
||||
MatchingRuleSet matching_rule_set;
|
||||
matching_rule_set.user_agent_rules = collect_matching_rules(element, CascadeOrigin::UserAgent, pseudo_element);
|
||||
matching_rule_set.user_agent_rules = collect_matching_rules(element, CascadeOrigin::UserAgent, pseudo_element, did_match_any_hover_rules);
|
||||
sort_matching_rules(matching_rule_set.user_agent_rules);
|
||||
matching_rule_set.user_rules = collect_matching_rules(element, CascadeOrigin::User, pseudo_element);
|
||||
matching_rule_set.user_rules = collect_matching_rules(element, CascadeOrigin::User, pseudo_element, did_match_any_hover_rules);
|
||||
sort_matching_rules(matching_rule_set.user_rules);
|
||||
// @layer-ed author rules
|
||||
for (auto const& layer_name : m_qualified_layer_names_in_order) {
|
||||
auto layer_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, layer_name);
|
||||
auto layer_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, did_match_any_hover_rules, layer_name);
|
||||
sort_matching_rules(layer_rules);
|
||||
matching_rule_set.author_rules.append({ layer_name, layer_rules });
|
||||
}
|
||||
// Un-@layer-ed author rules
|
||||
auto unlayered_author_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element);
|
||||
auto unlayered_author_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, did_match_any_hover_rules);
|
||||
sort_matching_rules(unlayered_author_rules);
|
||||
matching_rule_set.author_rules.append({ {}, unlayered_author_rules });
|
||||
|
||||
|
@ -2299,7 +2298,8 @@ GC::Ptr<ComputedProperties> StyleComputer::compute_style_impl(DOM::Element& elem
|
|||
|
||||
// 1. Perform the cascade. This produces the "specified style"
|
||||
bool did_match_any_pseudo_element_rules = false;
|
||||
auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, mode);
|
||||
bool did_match_any_hover_rules = false;
|
||||
auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, did_match_any_hover_rules, mode);
|
||||
|
||||
element.set_cascaded_properties(pseudo_element, cascaded_properties);
|
||||
|
||||
|
@ -2332,7 +2332,10 @@ GC::Ptr<ComputedProperties> StyleComputer::compute_style_impl(DOM::Element& elem
|
|||
}
|
||||
}
|
||||
|
||||
return compute_properties(element, pseudo_element, cascaded_properties);
|
||||
auto computed_properties = compute_properties(element, pseudo_element, cascaded_properties);
|
||||
if (did_match_any_hover_rules)
|
||||
computed_properties->set_did_match_any_hover_rules();
|
||||
return computed_properties;
|
||||
}
|
||||
|
||||
GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::Element& element, Optional<Selector::PseudoElement::Type> pseudo_element, CascadedProperties& cascaded_properties) const
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue