| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * Copyright (C) 2003-2021 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. |
| * |
| */ |
| |
| #pragma once |
| |
| #include "MediaList.h" |
| #include "RuleData.h" |
| #include "RuleFeature.h" |
| #include "SelectorCompiler.h" |
| #include "StyleRule.h" |
| #include <wtf/Forward.h> |
| #include <wtf/HashMap.h> |
| #include <wtf/VectorHash.h> |
| #include <wtf/text/AtomString.h> |
| #include <wtf/text/AtomStringHash.h> |
| |
| namespace WebCore { |
| |
| class CSSSelector; |
| class MediaQueryEvaluator; |
| class StyleSheetContents; |
| |
| namespace Style { |
| |
| class Resolver; |
| class RuleSet; |
| |
| using CascadeLayerPriority = uint16_t; |
| |
| using InvalidationRuleSetVector = Vector<RefPtr<const RuleSet>, 1>; |
| |
| struct DynamicMediaQueryEvaluationChanges { |
| enum class Type { InvalidateStyle, ResetStyle }; |
| Type type; |
| InvalidationRuleSetVector invalidationRuleSets { }; |
| |
| void append(DynamicMediaQueryEvaluationChanges&& other) |
| { |
| type = std::max(type, other.type); |
| if (type == Type::ResetStyle) |
| invalidationRuleSets.clear(); |
| else |
| invalidationRuleSets.appendVector(WTFMove(other.invalidationRuleSets)); |
| }; |
| }; |
| |
| class RuleSet : public RefCounted<RuleSet> { |
| WTF_MAKE_NONCOPYABLE(RuleSet); |
| public: |
| static Ref<RuleSet> create() { return adoptRef(*new RuleSet); } |
| |
| ~RuleSet(); |
| |
| typedef Vector<RuleData, 1> RuleDataVector; |
| typedef HashMap<AtomString, std::unique_ptr<RuleDataVector>> AtomRuleMap; |
| |
| void addRule(const StyleRule&, unsigned selectorIndex, unsigned selectorListIndex); |
| void addPageRule(StyleRulePage&); |
| |
| void addToRuleSet(const AtomString& key, AtomRuleMap&, const RuleData&); |
| void shrinkToFit(); |
| |
| bool hasViewportDependentMediaQueries() const { return m_hasViewportDependentMediaQueries; } |
| |
| std::optional<DynamicMediaQueryEvaluationChanges> evaluateDynamicMediaQueryRules(const MediaQueryEvaluator&); |
| |
| const RuleFeatureSet& features() const { return m_features; } |
| |
| const RuleDataVector* idRules(const AtomString& key) const { return m_idRules.get(key); } |
| const RuleDataVector* classRules(const AtomString& key) const { return m_classRules.get(key); } |
| const RuleDataVector* tagRules(const AtomString& key, bool isHTMLName) const; |
| const RuleDataVector* shadowPseudoElementRules(const AtomString& key) const { return m_shadowPseudoElementRules.get(key); } |
| const RuleDataVector* linkPseudoClassRules() const { return &m_linkPseudoClassRules; } |
| #if ENABLE(VIDEO) |
| const RuleDataVector& cuePseudoRules() const { return m_cuePseudoRules; } |
| #endif |
| const RuleDataVector& hostPseudoClassRules() const { return m_hostPseudoClassRules; } |
| const RuleDataVector& slottedPseudoElementRules() const { return m_slottedPseudoElementRules; } |
| const RuleDataVector& partPseudoElementRules() const { return m_partPseudoElementRules; } |
| const RuleDataVector* focusPseudoClassRules() const { return &m_focusPseudoClassRules; } |
| const RuleDataVector* universalRules() const { return &m_universalRules; } |
| |
| const Vector<StyleRulePage*>& pageRules() const { return m_pageRules; } |
| |
| unsigned ruleCount() const { return m_ruleCount; } |
| |
| bool hasShadowPseudoElementRules() const { return !m_shadowPseudoElementRules.isEmpty(); } |
| bool hasHostPseudoClassRulesMatchingInShadowTree() const { return m_hasHostPseudoClassRulesMatchingInShadowTree; } |
| |
| static constexpr auto cascadeLayerPriorityForPresentationalHints = std::numeric_limits<CascadeLayerPriority>::min(); |
| static constexpr auto cascadeLayerPriorityForUnlayered = std::numeric_limits<CascadeLayerPriority>::max(); |
| |
| CascadeLayerPriority cascadeLayerPriorityFor(const RuleData&) const; |
| |
| bool hasContainerQueries() const { return !m_containerQueries.isEmpty(); } |
| Vector<const FilteredContainerQuery*> containerQueriesFor(const RuleData&) const; |
| |
| private: |
| friend class RuleSetBuilder; |
| |
| RuleSet(); |
| |
| using CascadeLayerIdentifier = unsigned; |
| using ContainerQueryIdentifier = unsigned; |
| |
| void addRule(RuleData&&, CascadeLayerIdentifier, ContainerQueryIdentifier); |
| |
| struct ResolverMutatingRule { |
| Ref<StyleRuleBase> rule; |
| CascadeLayerIdentifier layerIdentifier; |
| }; |
| |
| struct CollectedMediaQueryChanges { |
| bool requiredFullReset { false }; |
| Vector<size_t> changedQueryIndexes { }; |
| Vector<Vector<Ref<const StyleRule>>*> affectedRules { }; |
| }; |
| CollectedMediaQueryChanges evaluateDynamicMediaQueryRules(const MediaQueryEvaluator&, size_t startIndex); |
| |
| template<typename Function> void traverseRuleDatas(Function&&); |
| |
| struct CascadeLayer { |
| CascadeLayerName resolvedName; |
| CascadeLayerIdentifier parentIdentifier; |
| CascadeLayerPriority priority { 0 }; |
| }; |
| CascadeLayer& cascadeLayerForIdentifier(CascadeLayerIdentifier identifier) { return m_cascadeLayers[identifier - 1]; } |
| const CascadeLayer& cascadeLayerForIdentifier(CascadeLayerIdentifier identifier) const { return m_cascadeLayers[identifier - 1]; } |
| CascadeLayerPriority cascadeLayerPriorityForIdentifier(CascadeLayerIdentifier) const; |
| |
| struct ContainerQueryAndParent { |
| Ref<StyleRuleContainer> containerRule; |
| ContainerQueryIdentifier parent; |
| }; |
| |
| struct DynamicMediaQueryRules { |
| Vector<Ref<const MediaQuerySet>> mediaQuerySets; |
| Vector<size_t> affectedRulePositions; |
| Vector<Ref<const StyleRule>> affectedRules; |
| bool requiresFullReset { false }; |
| bool result { true }; |
| |
| void shrinkToFit() |
| { |
| mediaQuerySets.shrinkToFit(); |
| affectedRulePositions.shrinkToFit(); |
| affectedRules.shrinkToFit(); |
| } |
| }; |
| |
| AtomRuleMap m_idRules; |
| AtomRuleMap m_classRules; |
| AtomRuleMap m_tagLocalNameRules; |
| AtomRuleMap m_tagLowercaseLocalNameRules; |
| AtomRuleMap m_shadowPseudoElementRules; |
| RuleDataVector m_linkPseudoClassRules; |
| #if ENABLE(VIDEO) |
| RuleDataVector m_cuePseudoRules; |
| #endif |
| RuleDataVector m_hostPseudoClassRules; |
| RuleDataVector m_slottedPseudoElementRules; |
| RuleDataVector m_partPseudoElementRules; |
| RuleDataVector m_focusPseudoClassRules; |
| RuleDataVector m_universalRules; |
| Vector<StyleRulePage*> m_pageRules; |
| RuleFeatureSet m_features; |
| Vector<DynamicMediaQueryRules> m_dynamicMediaQueryRules; |
| HashMap<Vector<size_t>, Ref<const RuleSet>> m_mediaQueryInvalidationRuleSetCache; |
| unsigned m_ruleCount { 0 }; |
| |
| Vector<CascadeLayer> m_cascadeLayers; |
| // This is a side vector to hold layer identifiers without bloating RuleData. |
| Vector<CascadeLayerIdentifier> m_cascadeLayerIdentifierForRulePosition; |
| |
| Vector<ResolverMutatingRule> m_resolverMutatingRulesInLayers; |
| |
| Vector<ContainerQueryAndParent> m_containerQueries; |
| Vector<ContainerQueryIdentifier> m_containerQueryIdentifierForRulePosition; |
| |
| bool m_hasHostPseudoClassRulesMatchingInShadowTree { false }; |
| bool m_hasViewportDependentMediaQueries { false }; |
| }; |
| |
| inline const RuleSet::RuleDataVector* RuleSet::tagRules(const AtomString& key, bool isHTMLName) const |
| { |
| const AtomRuleMap* tagRules; |
| if (isHTMLName) |
| tagRules = &m_tagLowercaseLocalNameRules; |
| else |
| tagRules = &m_tagLocalNameRules; |
| return tagRules->get(key); |
| } |
| |
| inline CascadeLayerPriority RuleSet::cascadeLayerPriorityForIdentifier(CascadeLayerIdentifier identifier) const |
| { |
| if (!identifier) |
| return cascadeLayerPriorityForUnlayered; |
| return cascadeLayerForIdentifier(identifier).priority; |
| } |
| |
| inline CascadeLayerPriority RuleSet::cascadeLayerPriorityFor(const RuleData& ruleData) const |
| { |
| if (m_cascadeLayerIdentifierForRulePosition.size() <= ruleData.position()) |
| return cascadeLayerPriorityForUnlayered; |
| auto identifier = m_cascadeLayerIdentifierForRulePosition[ruleData.position()]; |
| return cascadeLayerPriorityForIdentifier(identifier); |
| } |
| |
| inline Vector<const FilteredContainerQuery*> RuleSet::containerQueriesFor(const RuleData& ruleData) const |
| { |
| if (m_containerQueryIdentifierForRulePosition.size() <= ruleData.position()) |
| return { }; |
| |
| Vector<const FilteredContainerQuery*> queries; |
| |
| auto identifier = m_containerQueryIdentifierForRulePosition[ruleData.position()]; |
| while (identifier) { |
| auto& query = m_containerQueries[identifier - 1]; |
| queries.append(&query.containerRule->filteredQuery()); |
| identifier = query.parent; |
| }; |
| |
| return queries; |
| } |
| |
| |
| } // namespace Style |
| } // namespace WebCore |