Pārlūkot izejas kodu

LibWeb: Collect interesting document-wide insights about CSS selectors

Starting out with these two things:
- Whether any :has() selectors are present
- The set of all names referenced by attribute selectors
Andreas Kling 6 mēneši atpakaļ
vecāks
revīzija
6983c65c54

+ 31 - 17
Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -2503,24 +2503,26 @@ static Optional<SimplifiedSelectorForBucketing> is_roundabout_selector_bucketabl
     return {};
     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& compound_selector : selector.compound_selectors()) {
         for (auto const& simple_selector : compound_selector.simple_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::RuleCache> StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin)
+NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin, SelectorInsights& insights)
 {
 {
     auto rule_cache = make<RuleCache>();
     auto rule_cache = make<RuleCache>();
 
 
@@ -2563,8 +2565,7 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
                 bool contains_root_pseudo_class = false;
                 bool contains_root_pseudo_class = false;
                 Optional<CSS::Selector::PseudoElement::Type> pseudo_element;
                 Optional<CSS::Selector::PseudoElement::Type> 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) {
                 for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) {
                     if (!matching_rule.contains_pseudo_element) {
                     if (!matching_rule.contains_pseudo_element) {
@@ -2807,17 +2808,17 @@ void StyleComputer::build_qualified_layer_names_cache()
 
 
 void StyleComputer::build_rule_cache()
 void StyleComputer::build_rule_cache()
 {
 {
+    m_selector_insights = make<SelectorInsights>();
+
     if (auto user_style_source = document().page().user_style(); user_style_source.has_value()) {
     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()));
         m_user_style_sheet = GC::make_root(parse_css_stylesheet(CSS::Parser::ParsingContext(document()), user_style_source.value()));
     }
     }
 
 
     build_qualified_layer_names_cache();
     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()
 void StyleComputer::invalidate_rule_cache()
@@ -2999,4 +3000,17 @@ size_t StyleComputer::number_of_css_font_faces_with_loading_in_progress() const
     return count;
     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);
+}
+
 }
 }

+ 11 - 5
Libraries/LibWeb/CSS/StyleComputer.h

@@ -169,7 +169,8 @@ public:
     };
     };
     void collect_animation_into(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, GC::Ref<Animations::KeyframeEffect> animation, ComputedProperties&, AnimationRefresh = AnimationRefresh::No) const;
     void collect_animation_into(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, GC::Ref<Animations::KeyframeEffect> 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;
     size_t number_of_css_font_faces_with_loading_in_progress() const;
 
 
@@ -247,6 +248,11 @@ private:
 
 
     GC::Ref<DOM::Document> m_document;
     GC::Ref<DOM::Document> m_document;
 
 
+    struct SelectorInsights {
+        bool has_has_selectors { false };
+        HashTable<FlyString> all_names_used_in_attribute_selectors;
+    };
+
     struct RuleCache {
     struct RuleCache {
         HashMap<FlyString, Vector<MatchingRule>> rules_by_id;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_id;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_class;
         HashMap<FlyString, Vector<MatchingRule>> rules_by_class;
@@ -257,15 +263,15 @@ private:
         Vector<MatchingRule> other_rules;
         Vector<MatchingRule> other_rules;
 
 
         HashMap<FlyString, NonnullRefPtr<Animations::KeyframeEffect::KeyFrameSet>> rules_by_animation_keyframes;
         HashMap<FlyString, NonnullRefPtr<Animations::KeyframeEffect::KeyFrameSet>> rules_by_animation_keyframes;
-
-        bool has_has_selectors { false };
     };
     };
 
 
-    NonnullOwnPtr<RuleCache> make_rule_cache_for_cascade_origin(CascadeOrigin);
+    NonnullOwnPtr<RuleCache> make_rule_cache_for_cascade_origin(CascadeOrigin, SelectorInsights&);
 
 
     RuleCache const& rule_cache_for_cascade_origin(CascadeOrigin) const;
     RuleCache const& rule_cache_for_cascade_origin(CascadeOrigin) const;
 
 
-    bool m_has_has_selectors { false };
+    static void collect_selector_insights(Selector const&, SelectorInsights&);
+
+    OwnPtr<SelectorInsights> m_selector_insights;
     OwnPtr<RuleCache> m_author_rule_cache;
     OwnPtr<RuleCache> m_author_rule_cache;
     OwnPtr<RuleCache> m_user_rule_cache;
     OwnPtr<RuleCache> m_user_rule_cache;
     OwnPtr<RuleCache> m_user_agent_rule_cache;
     OwnPtr<RuleCache> m_user_agent_rule_cache;