diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index e2e94f00a24..638c89da668 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -2503,24 +2503,26 @@ static Optional is_roundabout_selector_bucketabl return {}; } -static bool contains_has_pseudo_class(Selector const& selector) +void StyleComputer::collect_selector_insights(Selector const& selector, SelectorInsights& insights) { for (auto const& compound_selector : selector.compound_selectors()) { for (auto const& simple_selector : compound_selector.simple_selectors) { - if (simple_selector.type != CSS::Selector::SimpleSelector::Type::PseudoClass) - continue; - if (simple_selector.pseudo_class().type == CSS::PseudoClass::Has) - return true; - for (auto const& argument_selector : simple_selector.pseudo_class().argument_selector_list) { - if (contains_has_pseudo_class(argument_selector)) - return true; + if (simple_selector.type == Selector::SimpleSelector::Type::Attribute) { + insights.all_names_used_in_attribute_selectors.set(simple_selector.attribute().qualified_name.name.lowercase_name); + } + if (simple_selector.type == Selector::SimpleSelector::Type::PseudoClass) { + if (simple_selector.pseudo_class().type == PseudoClass::Has) { + insights.has_has_selectors = true; + } + for (auto const& argument_selector : simple_selector.pseudo_class().argument_selector_list) { + collect_selector_insights(*argument_selector, insights); + } } } } - return false; } -NonnullOwnPtr StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin) +NonnullOwnPtr StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin, SelectorInsights& insights) { auto rule_cache = make(); @@ -2563,8 +2565,7 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca bool contains_root_pseudo_class = false; Optional pseudo_element; - if (!rule_cache->has_has_selectors) - rule_cache->has_has_selectors = contains_has_pseudo_class(selector); + collect_selector_insights(selector, insights); for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) { if (!matching_rule.contains_pseudo_element) { @@ -2807,17 +2808,17 @@ void StyleComputer::build_qualified_layer_names_cache() void StyleComputer::build_rule_cache() { + m_selector_insights = make(); + if (auto user_style_source = document().page().user_style(); user_style_source.has_value()) { m_user_style_sheet = GC::make_root(parse_css_stylesheet(CSS::Parser::ParsingContext(document()), user_style_source.value())); } build_qualified_layer_names_cache(); - m_author_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::Author); - m_user_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::User); - m_user_agent_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::UserAgent); - - m_has_has_selectors = m_author_rule_cache->has_has_selectors || m_user_rule_cache->has_has_selectors || m_user_agent_rule_cache->has_has_selectors; + m_author_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::Author, *m_selector_insights); + m_user_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::User, *m_selector_insights); + m_user_agent_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::UserAgent, *m_selector_insights); } void StyleComputer::invalidate_rule_cache() @@ -2999,4 +3000,17 @@ size_t StyleComputer::number_of_css_font_faces_with_loading_in_progress() const return count; } +bool StyleComputer::has_has_selectors() const +{ + build_rule_cache_if_needed(); + return m_selector_insights->has_has_selectors; +} + +bool StyleComputer::has_attribute_selector(FlyString const& attribute_name) const +{ + build_rule_cache_if_needed(); + + return m_selector_insights->all_names_used_in_attribute_selectors.contains(attribute_name); +} + } diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index aa874338d7b..718dd12ef50 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -169,7 +169,8 @@ public: }; void collect_animation_into(DOM::Element&, Optional, GC::Ref animation, ComputedProperties&, AnimationRefresh = AnimationRefresh::No) const; - [[nodiscard]] bool has_has_selectors() const { return m_has_has_selectors; } + [[nodiscard]] bool has_has_selectors() const; + [[nodiscard]] bool has_attribute_selector(FlyString const& attribute_name) const; size_t number_of_css_font_faces_with_loading_in_progress() const; @@ -247,6 +248,11 @@ private: GC::Ref m_document; + struct SelectorInsights { + bool has_has_selectors { false }; + HashTable all_names_used_in_attribute_selectors; + }; + struct RuleCache { HashMap> rules_by_id; HashMap> rules_by_class; @@ -257,15 +263,15 @@ private: Vector other_rules; HashMap> rules_by_animation_keyframes; - - bool has_has_selectors { false }; }; - NonnullOwnPtr make_rule_cache_for_cascade_origin(CascadeOrigin); + NonnullOwnPtr make_rule_cache_for_cascade_origin(CascadeOrigin, SelectorInsights&); RuleCache const& rule_cache_for_cascade_origin(CascadeOrigin) const; - bool m_has_has_selectors { false }; + static void collect_selector_insights(Selector const&, SelectorInsights&); + + OwnPtr m_selector_insights; OwnPtr m_author_rule_cache; OwnPtr m_user_rule_cache; OwnPtr m_user_agent_rule_cache;