/* * Copyright (c) 2025, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Web::CSS { class StyleScope; struct MatchingRule { GC::Ptr rule; // Either CSSStyleRule or CSSNestedDeclarations GC::Ptr sheet; GC::Ptr container_rule; GC::Ptr scope_rule; // Either CSSScopeRule or CSSImportRule Optional default_namespace; Selector const& selector; size_t selector_index { 0 }; size_t style_sheet_index { 0 }; size_t rule_index { 0 }; u32 specificity { 0 }; u32 multi_bucket_rule_index { 0 }; CascadeOrigin cascade_origin; bool contains_pseudo_element { false }; bool slotted { false }; bool contains_part_pseudo_element { false }; // Helpers to deal with the fact that `rule` might be a CSSStyleRule or a CSSNestedDeclarations CSSStyleProperties const& declaration() const; SelectorList const& absolutized_selectors() const; FlyString const& qualified_layer_name() const; void visit_edges(GC::Cell::Visitor&) const; }; enum class SubjectPseudoClassBuckets { No, Yes, }; enum class AncestorHashBuckets { No, Yes, }; struct RuleCache { HashMap> rules_by_id; HashMap> rules_by_class; HashMap> rules_by_tag_name; HashMap, AK::ASCIICaseInsensitiveFlyStringTraits> rules_by_attribute_name; Array, to_underlying(PseudoClass::__Count)> rules_by_subject_pseudo_class; HashMap> rules_by_ancestor_hash; Vector root_rules; Vector slotted_rules; Vector part_rules; Vector other_rules; struct PseudoElementRules { HashMap> rules_by_id; HashMap> rules_by_class; HashMap> rules_by_tag_name; HashMap, AK::ASCIICaseInsensitiveFlyStringTraits> rules_by_attribute_name; Array, to_underlying(PseudoClass::__Count)> rules_by_subject_pseudo_class; HashMap> rules_by_ancestor_hash; Vector root_rules; Vector other_rules; }; Array rules_by_pseudo_element; HashMap> rules_by_animation_keyframes; u32 next_multi_bucket_rule_index { 0 }; void add_rule(MatchingRule const&, Optional, bool contains_root_pseudo_class, SubjectPseudoClassBuckets, AncestorHashBuckets); void for_each_matching_rules(DOM::AbstractElement, Function const& may_contain_ancestor_hash, Function const&)> callback) const; void for_each_matching_pseudo_element_rules(DOM::AbstractElement, Function const& may_contain_ancestor_hash, Function const&)> callback) const; void visit_edges(GC::Cell::Visitor&); }; struct RuleCaches { RuleCache main; HashMap> by_layer; void visit_edges(GC::Cell::Visitor&); }; struct StyleRuleCache { StyleRuleCache(); Vector qualified_layer_names_in_order; SelectorInsights selector_insights; Array, to_underlying(PseudoClass::__Count)> pseudo_class_rule_cache; RuleCaches author_rule_cache; RuleCaches user_rule_cache; RuleCaches user_agent_rule_cache; bool has_size_container_queries { false }; void visit_edges(GC::Cell::Visitor&); }; struct StyleCache : public RefCounted { static NonnullRefPtr create(); OwnPtr rule_cache; OwnPtr style_invalidation_data; void visit_edges(GC::Cell::Visitor&); }; struct PendingHasInvalidationMutationFeatures { bool is_conservative { false }; bool may_affect_sibling_relationships { false }; bool may_affect_pseudo_classes { false }; HashTable tag_names; HashTable ids; HashTable class_names; HashTable attribute_names; HashTable pseudo_classes; }; class StyleScope { public: explicit StyleScope(GC::Ref); DOM::Node& node() const { return m_node; } DOM::Document& document() const; RuleCaches const& author_rule_cache() const { return rule_cache().author_rule_cache; } RuleCaches const& user_rule_cache() const { return rule_cache().user_rule_cache; } RuleCaches const& user_agent_rule_cache() const { return rule_cache().user_agent_rule_cache; } [[nodiscard]] StyleRuleCache const& rule_cache() const; [[nodiscard]] StyleInvalidationData const& style_invalidation_data() const; [[nodiscard]] bool has_valid_rule_cache() const { return m_style_cache && m_style_cache->rule_cache; } [[nodiscard]] bool has_valid_style_invalidation_data() const { return m_style_cache && m_style_cache->style_invalidation_data; } void invalidate_style_cache(); void invalidate_user_style_sheet(); [[nodiscard]] RuleCache const& get_pseudo_class_rule_cache(PseudoClass) const; void for_each_stylesheet(CascadeOrigin, Function const&) const; static WEB_API void for_each_user_agent_stylesheet(bool include_quirks_mode_stylesheet, Function const&); static Optional user_agent_style_sheet_identifier(CSS::CSSStyleSheet const&); void build_user_style_sheet_if_needed(); void make_rule_cache_for_cascade_origin(CascadeOrigin, StyleRuleCache&); void build_style_invalidation_data_for_cascade_origin(CascadeOrigin, StyleInvalidationData&); void build_rule_cache(); void build_rule_cache_if_needed() const; void build_style_invalidation_data(); void build_style_invalidation_data_if_needed() const; void populate_rule_cache(StyleRuleCache&); void populate_style_invalidation_data(StyleInvalidationData&); static void collect_selector_insights(Selector const&, SelectorInsights&); void build_qualified_layer_names_cache(StyleRuleCache&); [[nodiscard]] bool may_have_has_selectors() const; [[nodiscard]] bool may_have_user_has_selectors() const; [[nodiscard]] bool may_have_user_pseudo_class_selectors(PseudoClass) const; [[nodiscard]] bool have_has_selectors() const; [[nodiscard]] bool may_have_has_selectors_with_relative_selector_that_has_sibling_combinator() const; [[nodiscard]] bool have_has_selectors_with_relative_selector_that_has_sibling_combinator() const; [[nodiscard]] bool have_size_container_queries() const; void for_each_active_css_style_sheet(Function const& callback) const; void invalidate_counter_style_cache(); void build_counter_style_cache(); RefPtr get_registered_counter_style(FlyString const& name) const; void schedule_ancestors_style_invalidation_due_to_presence_of_has(GC::Ref); void record_conservative_pending_has_invalidation(GC::Ref, bool may_affect_sibling_relationships); void record_pending_has_invalidation_mutation_features(GC::Ref, GC::Ref, bool includes_descendants); void record_pending_has_invalidation_mutation_features(GC::Ref, Vector const&); template Optional dereference_global_tree_scoped_reference(Function(StyleScope const&)> const& callback) const; void visit_edges(GC::Cell::Visitor&); StyleCache& ensure_style_cache(); StyleCache& ensure_style_cache() const; RefPtr m_style_cache; GC::Ptr m_user_style_sheet; OrderedHashMap, PendingHasInvalidationMutationFeatures> m_pending_has_invalidations; bool m_needs_counter_style_cache_update : 1 { true }; bool m_is_doing_counter_style_cache_update : 1 { false }; HashMap> m_registered_counter_styles; GC::Ref m_node; }; }