/* * 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 namespace Web::CSS { class StyleScope; struct MatchingRule { GC::Ptr rule; // Either CSSStyleRule or CSSNestedDeclarations GC::Ptr sheet; Optional default_namespace; Selector const& selector; size_t style_sheet_index { 0 }; size_t rule_index { 0 }; u32 specificity { 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&); }; 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(CSS::PseudoElement::KnownPseudoElementCount)> rules_by_pseudo_element; Vector root_rules; Vector slotted_rules; Vector part_rules; Vector other_rules; HashMap> rules_by_animation_keyframes; void add_rule(MatchingRule const&, Optional, bool contains_root_pseudo_class); void for_each_matching_rules(DOM::AbstractElement, Function const&)> callback) const; void visit_edges(GC::Cell::Visitor&); }; struct RuleCaches { RuleCache main; HashMap> by_layer; void visit_edges(GC::Cell::Visitor&); }; struct SelectorInsights { bool has_has_selectors { false }; bool has_has_selectors_with_relative_selector_that_has_sibling_combinator { false }; }; struct StyleCache : public RefCounted { static NonnullRefPtr create(); static NonnullRefPtr create_for_style_scope(StyleScope&); Vector qualified_layer_names_in_order; SelectorInsights selector_insights; Array, to_underlying(PseudoClass::__Count)> pseudo_class_rule_cache; StyleInvalidationData style_invalidation_data; RuleCaches author_rule_cache; RuleCaches user_rule_cache; RuleCaches user_agent_rule_cache; 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 m_rule_cache->author_rule_cache; } RuleCaches const& user_rule_cache() const { return m_rule_cache->user_rule_cache; } RuleCaches const& user_agent_rule_cache() const { return m_rule_cache->user_agent_rule_cache; } [[nodiscard]] bool has_valid_rule_cache() const { return m_rule_cache; } void invalidate_rule_cache(); [[nodiscard]] RuleCache const& get_pseudo_class_rule_cache(PseudoClass) const; void for_each_stylesheet(CascadeOrigin, Function const&) const; void build_user_style_sheet_if_needed(); void make_rule_cache_for_cascade_origin(CascadeOrigin, StyleCache&); void build_rule_cache(); void build_rule_cache_if_needed() const; void populate_rule_cache(StyleCache&); static void collect_selector_insights(Selector const&, SelectorInsights&); void build_qualified_layer_names_cache(StyleCache&); [[nodiscard]] bool may_have_has_selectors() 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; 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&); RefPtr m_rule_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; }; }