/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/css/css_keyframes_rule.h" #include "third_party/blink/renderer/core/css/media_query_evaluator.h" #include "third_party/blink/renderer/core/css/resolver/media_query_result.h" #include "third_party/blink/renderer/core/css/rule_feature_set.h" #include "third_party/blink/renderer/core/css/style_rule.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/size_assertions.h" namespace blink { using AddRuleFlags = unsigned; enum AddRuleFlag { kRuleHasNoSpecialState = 0, kRuleHasDocumentSecurityOrigin = 1 << 0, kRuleIsVisitedDependent = 1 << 1, }; // Some CSS properties do not apply to certain pseudo-elements, and need to be // ignored when resolving styles. enum class ValidPropertyFilter : unsigned { // All properties are valid. This is the common case. kNoFilter, // Defined in a ::cue pseudo-element scope. Only properties listed // in https://w3c.github.io/webvtt/#the-cue-pseudo-element are valid. kCue, // Defined in a ::first-letter pseudo-element scope. Only properties listed in // https://drafts.csswg.org/css-pseudo-4/#first-letter-styling are valid. kFirstLetter, // Defined in a ::marker pseudo-element scope. Only properties listed in // https://drafts.csswg.org/css-pseudo-4/#marker-pseudo are valid. kMarker, // Defined in a highlight pseudo-element scope like ::selection and // ::target-text. Only properties listed in // https://drafts.csswg.org/css-pseudo-4/#highlight-styling are valid. kHighlight, }; class CSSSelector; class MediaQueryEvaluator; class StyleSheetContents; class MinimalRuleData { DISALLOW_NEW(); public: MinimalRuleData(StyleRule* rule, unsigned selector_index, AddRuleFlags flags) : rule_(rule), selector_index_(selector_index), flags_(flags) {} void Trace(Visitor*) const; Member rule_; unsigned selector_index_; AddRuleFlags flags_; }; } // namespace blink WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::MinimalRuleData) namespace blink { // This is a wrapper around a StyleRule, pointing to one of the N complex // selectors in the StyleRule. This allows us to treat each selector // independently but still tie them back to the original StyleRule. If multiple // selectors from a single rule match the same element we can see that as one // match for the rule. It computes some information about the wrapped selector // and makes it accessible cheaply. class CORE_EXPORT RuleData : public GarbageCollected { public: static RuleData* MaybeCreate(StyleRule*, unsigned selector_index, unsigned position, AddRuleFlags); RuleData(StyleRule*, unsigned selector_index, unsigned position, AddRuleFlags); unsigned GetPosition() const { return position_; } StyleRule* Rule() const { return rule_; } const CSSSelector& Selector() const { return rule_->SelectorList().SelectorAt(selector_index_); } unsigned SelectorIndex() const { return selector_index_; } bool ContainsUncommonAttributeSelector() const { return contains_uncommon_attribute_selector_; } unsigned Specificity() const { return specificity_; } unsigned LinkMatchType() const { return link_match_type_; } bool HasDocumentSecurityOrigin() const { return has_document_security_origin_; } ValidPropertyFilter GetValidPropertyFilter( bool is_matching_ua_rules = false) const { return is_matching_ua_rules ? ValidPropertyFilter::kNoFilter : static_cast(valid_property_filter_); } // Try to balance between memory usage (there can be lots of RuleData objects) // and good filtering performance. static const unsigned kMaximumIdentifierCount = 4; const unsigned* DescendantSelectorIdentifierHashes() const { return descendant_selector_identifier_hashes_; } void Trace(Visitor*) const; // This number is picked fairly arbitrary. If lowered, be aware that there // might be sites and extensions using style rules with selector lists // exceeding the number of simple selectors to fit in this bitfield. // See https://crbug.com/312913 and https://crbug.com/704562 static constexpr size_t kSelectorIndexBits = 13; // This number was picked fairly arbitrarily. We can probably lower it if we // need to. Some simple testing showed <100,000 RuleData's on large sites. static constexpr size_t kPositionBits = 18; private: Member rule_; unsigned selector_index_ : kSelectorIndexBits; unsigned position_ : kPositionBits; unsigned contains_uncommon_attribute_selector_ : 1; // 32 bits above unsigned specificity_ : 24; unsigned link_match_type_ : 2; unsigned has_document_security_origin_ : 1; unsigned valid_property_filter_ : 3; // 30 bits above // Use plain array instead of a Vector to minimize memory overhead. unsigned descendant_selector_identifier_hashes_[kMaximumIdentifierCount]; }; } // namespace blink WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::RuleData) namespace blink { struct SameSizeAsRuleData { DISALLOW_NEW(); Member a; unsigned b; unsigned c; unsigned d[4]; }; ASSERT_SIZE(RuleData, SameSizeAsRuleData); // Holds RuleData objects. It partitions them into various indexed groups, // e.g. it stores separately rules that match against id, class, tag, shadow // host, etc. It indexes these by some key where possible, e.g. rules that match // against tag name are indexed by that tag. Rules that don't fall into a // specific group are appended to the "universal" rules. The grouping is done to // optimize finding what rules apply to an element under consideration by // ElementRuleCollector::CollectMatchingRules. class CORE_EXPORT RuleSet final : public GarbageCollected { public: RuleSet() : rule_count_(0) {} RuleSet(const RuleSet&) = delete; RuleSet& operator=(const RuleSet&) = delete; void AddRulesFromSheet(StyleSheetContents*, const MediaQueryEvaluator&, AddRuleFlags = kRuleHasNoSpecialState); void AddStyleRule(StyleRule*, AddRuleFlags); void AddRule(StyleRule*, unsigned selector_index, AddRuleFlags); const RuleFeatureSet& Features() const { return features_; } const HeapVector>* IdRules( const AtomicString& key) const { DCHECK(!pending_rules_); return id_rules_.at(key); } const HeapVector>* ClassRules( const AtomicString& key) const { DCHECK(!pending_rules_); return class_rules_.at(key); } const HeapVector>* TagRules( const AtomicString& key) const { DCHECK(!pending_rules_); return tag_rules_.at(key); } const HeapVector>* ShadowPseudoElementRules( const AtomicString& key) const { DCHECK(!pending_rules_); return shadow_pseudo_element_rules_.at(key); } const HeapVector>* LinkPseudoClassRules() const { DCHECK(!pending_rules_); return &link_pseudo_class_rules_; } const HeapVector>* CuePseudoRules() const { DCHECK(!pending_rules_); return &cue_pseudo_rules_; } const HeapVector>* FocusPseudoClassRules() const { DCHECK(!pending_rules_); return &focus_pseudo_class_rules_; } const HeapVector>* SpatialNavigationInterestPseudoClassRules() const { DCHECK(!pending_rules_); return &spatial_navigation_interest_class_rules_; } const HeapVector>* UniversalRules() const { DCHECK(!pending_rules_); return &universal_rules_; } const HeapVector>* ShadowHostRules() const { DCHECK(!pending_rules_); return &shadow_host_rules_; } const HeapVector>* PartPseudoRules() const { DCHECK(!pending_rules_); return &part_pseudo_rules_; } const HeapVector>* VisitedDependentRules() const { DCHECK(!pending_rules_); return &visited_dependent_rules_; } const HeapVector>& PageRules() const { DCHECK(!pending_rules_); return page_rules_; } const HeapVector>& FontFaceRules() const { return font_face_rules_; } const HeapVector>& KeyframesRules() const { return keyframes_rules_; } StyleRuleKeyframes* KeyframeStylesForAnimation(const AtomicString& name); const HeapVector>& PropertyRules() const { return property_rules_; } const HeapVector>& ScrollTimelineRules() const { return scroll_timeline_rules_; } const HeapVector& DeepCombinatorOrShadowPseudoRules() const { return deep_combinator_or_shadow_pseudo_rules_; } const HeapVector& ContentPseudoElementRules() const { return content_pseudo_element_rules_; } const HeapVector& SlottedPseudoElementRules() const { return slotted_pseudo_element_rules_; } unsigned RuleCount() const { return rule_count_; } void CompactRulesIfNeeded() { if (!pending_rules_) return; CompactRules(); } bool HasSlottedRules() const { return !slotted_pseudo_element_rules_.IsEmpty(); } bool HasV0BoundaryCrossingRules() const { return !deep_combinator_or_shadow_pseudo_rules_.IsEmpty() || !content_pseudo_element_rules_.IsEmpty(); } bool NeedsFullRecalcForRuleSetInvalidation() const { return features_.NeedsFullRecalcForRuleSetInvalidation() || HasV0BoundaryCrossingRules(); } bool DidMediaQueryResultsChange(const MediaQueryEvaluator& evaluator) const; #ifndef NDEBUG void Show() const; #endif void Trace(Visitor*) const; private: using PendingRuleMap = HeapHashMap>>>; using CompactRuleMap = HeapHashMap>>>; void AddToRuleSet(const AtomicString& key, PendingRuleMap&, const RuleData*); void AddPageRule(StyleRulePage*); void AddViewportRule(StyleRuleViewport*); void AddFontFaceRule(StyleRuleFontFace*); void AddKeyframesRule(StyleRuleKeyframes*); void AddPropertyRule(StyleRuleProperty*); void AddScrollTimelineRule(StyleRuleScrollTimeline*); bool MatchMediaForAddRules(const MediaQueryEvaluator& evaluator, const MediaQuerySet* media_queries); void AddChildRules(const HeapVector>&, const MediaQueryEvaluator& medium, AddRuleFlags); bool FindBestRuleSetAndAdd(const CSSSelector&, RuleData*); void SortKeyframesRulesIfNeeded(); void CompactRules(); static void CompactPendingRules(PendingRuleMap&, CompactRuleMap&); class PendingRuleMaps : public GarbageCollected { public: PendingRuleMaps() = default; PendingRuleMap id_rules; PendingRuleMap class_rules; PendingRuleMap tag_rules; PendingRuleMap shadow_pseudo_element_rules; void Trace(Visitor*) const; }; PendingRuleMaps* EnsurePendingRules() { if (!pending_rules_) pending_rules_ = MakeGarbageCollected(); return pending_rules_.Get(); } CompactRuleMap id_rules_; CompactRuleMap class_rules_; CompactRuleMap tag_rules_; CompactRuleMap shadow_pseudo_element_rules_; HeapVector> link_pseudo_class_rules_; HeapVector> cue_pseudo_rules_; HeapVector> focus_pseudo_class_rules_; HeapVector> spatial_navigation_interest_class_rules_; HeapVector> universal_rules_; HeapVector> shadow_host_rules_; HeapVector> part_pseudo_rules_; HeapVector> visited_dependent_rules_; RuleFeatureSet features_; HeapVector> page_rules_; HeapVector> font_face_rules_; HeapVector> keyframes_rules_; HeapVector> property_rules_; HeapVector> scroll_timeline_rules_; HeapVector deep_combinator_or_shadow_pseudo_rules_; HeapVector content_pseudo_element_rules_; HeapVector slotted_pseudo_element_rules_; Vector media_query_set_results_; bool keyframes_rules_sorted_ = true; unsigned rule_count_; Member pending_rules_; #ifndef NDEBUG HeapVector> all_rules_; #endif }; } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_