diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index df94b01a5e9..80906f3f424 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -467,7 +467,7 @@ bool StyleComputer::invalidation_property_used_in_has_selector(InvalidationSet:: return false; } -Vector StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional pseudo_element, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name) const +Vector StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional pseudo_element, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name) const { auto const& root_node = element.root(); auto shadow_root = is(root_node) ? static_cast(&root_node) : nullptr; @@ -483,18 +483,44 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e return {}; auto& rule_cache = *maybe_rule_cache; - Vector rules_to_run; + Vector rules_to_run; + + auto add_rule_to_run = [&](MatchingRule const& rule_to_run) { + // FIXME: This needs to be revised when adding support for the ::shadow selector, as it needs to cross shadow boundaries. + auto rule_root = rule_to_run.shadow_root; + auto from_user_agent_or_user_stylesheet = rule_to_run.cascade_origin == CascadeOrigin::UserAgent || rule_to_run.cascade_origin == CascadeOrigin::User; + + // NOTE: Inside shadow trees, we only match rules that are defined in the shadow tree's style sheets. + // The key exception is the shadow tree's *shadow host*, which needs to match :host rules from inside the shadow root. + // Also note that UA or User style sheets don't have a scope, so they are always relevant. + // FIXME: We should reorganize the data so that the document-level StyleComputer doesn't cache *all* rules, + // but instead we'd have some kind of "style scope" at the document level, and also one for each shadow root. + // Then we could only evaluate rules from the current style scope. + bool rule_is_relevant_for_current_scope = rule_root == shadow_root + || (element.is_shadow_host() && rule_root == element.shadow_root()) + || from_user_agent_or_user_stylesheet; + + if (!rule_is_relevant_for_current_scope) + return; + + auto const& selector = rule_to_run.absolutized_selectors()[rule_to_run.selector_index]; + if (should_reject_with_ancestor_filter(*selector)) + return; + + rules_to_run.unchecked_append(rule_to_run); + }; + auto add_rules_to_run = [&](Vector 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.contains_pseudo_element && filter_namespace_rule(element, rule)) - rules_to_run.unchecked_append(rule); + add_rule_to_run(rule); } } else { for (auto const& rule : rules) { if (!rule.contains_pseudo_element && filter_namespace_rule(element, rule)) - rules_to_run.unchecked_append(rule); + add_rule_to_run(rule); } } }; @@ -528,47 +554,10 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e add_rules_to_run(rule_cache.other_rules); - size_t maximum_match_count = 0; - - for (auto& rule_to_run : rules_to_run) { - // FIXME: This needs to be revised when adding support for the ::shadow selector, as it needs to cross shadow boundaries. - auto rule_root = rule_to_run.shadow_root; - auto from_user_agent_or_user_stylesheet = rule_to_run.cascade_origin == CascadeOrigin::UserAgent || rule_to_run.cascade_origin == CascadeOrigin::User; - - // NOTE: Inside shadow trees, we only match rules that are defined in the shadow tree's style sheets. - // The key exception is the shadow tree's *shadow host*, which needs to match :host rules from inside the shadow root. - // Also note that UA or User style sheets don't have a scope, so they are always relevant. - // FIXME: We should reorganize the data so that the document-level StyleComputer doesn't cache *all* rules, - // but instead we'd have some kind of "style scope" at the document level, and also one for each shadow root. - // Then we could only evaluate rules from the current style scope. - bool rule_is_relevant_for_current_scope = rule_root == shadow_root - || (element.is_shadow_host() && rule_root == element.shadow_root()) - || from_user_agent_or_user_stylesheet; - - if (!rule_is_relevant_for_current_scope) { - rule_to_run.skip = true; - continue; - } - - auto const& selector = rule_to_run.absolutized_selectors()[rule_to_run.selector_index]; - if (should_reject_with_ancestor_filter(*selector)) { - rule_to_run.skip = true; - continue; - } - - ++maximum_match_count; - } - - if (maximum_match_count == 0) - return {}; - - Vector matching_rules; - matching_rules.ensure_capacity(maximum_match_count); + Vector matching_rules; + matching_rules.ensure_capacity(rules_to_run.size()); for (auto const& rule_to_run : rules_to_run) { - if (rule_to_run.skip) - continue; - // NOTE: When matching an element against a rule from outside the shadow root's style scope, // we have to pass in null for the shadow host, otherwise combinator traversal will // be confined to the element itself (since it refuses to cross the shadow boundary). @@ -591,22 +580,22 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e if (!SelectorEngine::matches(selector, element, shadow_host_to_use, context, pseudo_element)) continue; } - matching_rules.append(rule_to_run); + matching_rules.append(&rule_to_run); } return matching_rules; } -static void sort_matching_rules(Vector& matching_rules) +static void sort_matching_rules(Vector& matching_rules) { - quick_sort(matching_rules, [&](MatchingRule& a, MatchingRule& b) { - auto const& a_selector = a.absolutized_selectors()[a.selector_index]; - auto const& b_selector = b.absolutized_selectors()[b.selector_index]; + quick_sort(matching_rules, [&](MatchingRule const* a, MatchingRule const* b) { + auto const& a_selector = a->absolutized_selectors()[a->selector_index]; + auto const& b_selector = b->absolutized_selectors()[b->selector_index]; auto a_specificity = a_selector->specificity(); auto b_specificity = b_selector->specificity(); if (a_specificity == b_specificity) { - if (a.style_sheet_index == b.style_sheet_index) - return a.rule_index < b.rule_index; - return a.style_sheet_index < b.style_sheet_index; + if (a->style_sheet_index == b->style_sheet_index) + return a->rule_index < b->rule_index; + return a->style_sheet_index < b->style_sheet_index; } return a_specificity < b_specificity; }); @@ -992,18 +981,18 @@ void StyleComputer::cascade_declarations( CascadedProperties& cascaded_properties, DOM::Element& element, Optional pseudo_element, - Vector const& matching_rules, + Vector const& matching_rules, CascadeOrigin cascade_origin, Important important, Optional layer_name) const { for (auto const& match : matching_rules) { - for (auto const& property : match.declaration().properties()) { + for (auto const& property : match->declaration().properties()) { if (important != property.important) continue; if (property.property_id == CSS::PropertyID::All) { - set_all_properties(cascaded_properties, element, pseudo_element, property.value, m_document, &match.declaration(), cascade_origin, important, layer_name); + set_all_properties(cascaded_properties, element, pseudo_element, property.value, m_document, &match->declaration(), cascade_origin, important, layer_name); continue; } @@ -1011,7 +1000,7 @@ void StyleComputer::cascade_declarations( if (property.value->is_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(cascaded_properties, property.property_id, property_value, &match.declaration(), cascade_origin, important, layer_name); + set_property_expanding_shorthands(cascaded_properties, property.property_id, property_value, &match->declaration(), cascade_origin, important, layer_name); } } @@ -1036,11 +1025,11 @@ void StyleComputer::cascade_declarations( } } -static void cascade_custom_properties(DOM::Element& element, Optional pseudo_element, Vector const& matching_rules, HashMap& custom_properties) +static void cascade_custom_properties(DOM::Element& element, Optional pseudo_element, Vector const& matching_rules, HashMap& custom_properties) { size_t needed_capacity = 0; for (auto const& matching_rule : matching_rules) - needed_capacity += matching_rule.declaration().custom_properties().size(); + needed_capacity += matching_rule->declaration().custom_properties().size(); if (!pseudo_element.has_value()) { if (auto const inline_style = element.inline_style()) @@ -1050,7 +1039,7 @@ static void cascade_custom_properties(DOM::Element& element, Optionaldeclaration().custom_properties()) { auto style_value = it.value.value; if (style_value->is_revert_layer()) continue; diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index 37909461fd3..1ff8de0ae4f 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -87,7 +87,6 @@ struct MatchingRule { bool contains_pseudo_element { false }; bool can_use_fast_matches { false }; bool must_be_hovered { false }; - bool skip { false }; // Helpers to deal with the fact that `rule` might be a CSSStyleRule or a CSSNestedDeclarations PropertyOwningCSSStyleDeclaration const& declaration() const; @@ -148,7 +147,7 @@ public: [[nodiscard]] GC::Ptr compute_pseudo_element_style_if_needed(DOM::Element&, Optional) const; Vector const& get_hover_rules() const; - Vector collect_matching_rules(DOM::Element const&, CascadeOrigin, Optional, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name = {}) const; + [[nodiscard]] Vector collect_matching_rules(DOM::Element const&, CascadeOrigin, Optional, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name = {}) const; InvalidationSet invalidation_set_for_properties(Vector const&) const; bool invalidation_property_used_in_has_selector(InvalidationSet::Property const&) const; @@ -233,12 +232,12 @@ private: struct LayerMatchingRules { FlyString qualified_layer_name; - Vector rules; + Vector rules; }; struct MatchingRuleSet { - Vector user_agent_rules; - Vector user_rules; + Vector user_agent_rules; + Vector user_rules; Vector author_rules; }; @@ -246,7 +245,7 @@ private: CascadedProperties&, DOM::Element&, Optional, - Vector const&, + Vector const&, CascadeOrigin, Important, Optional layer_name) const;