blob: 5df4c7e55774e2d98db9147fa06caa1fc796b846 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll ([email protected])
* Copyright (C) 2003-2023 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 "CSSSelectorList.h"
#include "MediaQuery.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 StyleSheetContents;
class StyleRulePositionTry;
class StyleRuleViewTransition;
namespace MQ {
class MediaQueryEvaluator;
}
namespace Style {
class Resolver;
class RuleSet;
using CascadeLayerPriority = uint16_t;
struct RuleSetAndNegation {
RefPtr<const RuleSet> ruleSet;
IsNegation isNegation { IsNegation::No };
};
using InvalidationRuleSetVector = Vector<RuleSetAndNegation, 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 setViewTransitionRule(StyleRuleViewTransition&);
RefPtr<StyleRuleViewTransition> viewTransitionRule() const;
void addToRuleSet(const AtomString& key, AtomRuleMap&, const RuleData&);
void shrinkToFit();
bool hasViewportDependentMediaQueries() const { return m_hasViewportDependentMediaQueries; }
std::optional<DynamicMediaQueryEvaluationChanges> evaluateDynamicMediaQueryRules(const MQ::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* attributeRules(const AtomString& key, bool isHTMLName) const;
const RuleDataVector* tagRules(const AtomString& key, bool isHTMLName) const;
const RuleDataVector* userAgentPartRules(const AtomString& key) const { return m_userAgentPartRules.get(key); }
const RuleDataVector* linkPseudoClassRules() const { return &m_linkPseudoClassRules; }
const RuleDataVector* namedPseudoElementRules(const AtomString& key) const { return m_namedPseudoElementRules.get(key); }
#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* rootElementRules() const { return &m_rootElementRules; }
const RuleDataVector* universalRules() const { return &m_universalRules; }
const Vector<StyleRulePage*>& pageRules() const { return m_pageRules; }
unsigned ruleCount() const { return m_ruleCount; }
bool hasAttributeRules() const { return !m_attributeLocalNameRules.isEmpty(); }
bool hasUserAgentPartRules() const { return !m_userAgentPartRules.isEmpty(); }
bool hasHostPseudoClassRulesMatchingInShadowTree() const { return m_hasHostPseudoClassRulesMatchingInShadowTree; }
bool hasHostOrScopePseudoClassRulesInUniversalBucket() const { return m_hasHostOrScopePseudoClassRulesInUniversalBucket; }
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 CQ::ContainerQuery*> containerQueriesFor(const RuleData&) const;
Vector<Ref<const StyleRuleContainer>> containerQueryRules() const;
bool hasScopeRules() const { return !m_scopeRules.isEmpty(); }
Vector<Ref<const StyleRuleScope>> scopeRulesFor(const RuleData&) const;
const RefPtr<const StyleRulePositionTry> positionTryRuleForName(const AtomString&) const;
private:
friend class RuleSetBuilder;
RuleSet();
using CascadeLayerIdentifier = unsigned;
using ContainerQueryIdentifier = unsigned;
using ScopeRuleIdentifier = unsigned;
void addRule(RuleData&&, CascadeLayerIdentifier, ContainerQueryIdentifier, ScopeRuleIdentifier);
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 MQ::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 ScopeAndParent {
Ref<const StyleRuleScope> scopeRule;
ScopeRuleIdentifier parent;
};
struct ContainerQueryAndParent {
Ref<const StyleRuleContainer> containerRule;
ContainerQueryIdentifier parent;
};
struct DynamicMediaQueryRules {
Vector<MQ::MediaQueryList> mediaQueries;
Vector<size_t> affectedRulePositions;
Vector<Ref<const StyleRule>> affectedRules;
bool requiresFullReset { false };
bool result { true };
void shrinkToFit()
{
mediaQueries.shrinkToFit();
affectedRulePositions.shrinkToFit();
affectedRules.shrinkToFit();
}
};
AtomRuleMap m_idRules;
AtomRuleMap m_classRules;
AtomRuleMap m_attributeLocalNameRules;
AtomRuleMap m_attributeLowercaseLocalNameRules;
AtomRuleMap m_tagLocalNameRules;
AtomRuleMap m_tagLowercaseLocalNameRules;
AtomRuleMap m_userAgentPartRules;
AtomRuleMap m_namedPseudoElementRules;
RuleDataVector m_linkPseudoClassRules;
#if ENABLE(VIDEO)
RuleDataVector m_cuePseudoRules;
#endif
RuleDataVector m_hostPseudoClassRules;
RuleDataVector m_slottedPseudoElementRules;
RuleDataVector m_partPseudoElementRules;
RuleDataVector m_focusPseudoClassRules;
RuleDataVector m_rootElementRules;
RuleDataVector m_universalRules;
Vector<StyleRulePage*> m_pageRules;
RefPtr<StyleRuleViewTransition> m_viewTransitionRule;
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;
// @scope
Vector<ScopeAndParent> m_scopeRules;
Vector<ScopeRuleIdentifier> m_scopeRuleIdentifierForRulePosition;
// @position-try
HashMap<AtomString, RefPtr<const StyleRulePositionTry>> m_positionTryRules;
bool m_hasHostPseudoClassRulesMatchingInShadowTree { false };
bool m_hasViewportDependentMediaQueries { false };
bool m_hasHostOrScopePseudoClassRulesInUniversalBucket { false };
};
inline const RuleSet::RuleDataVector* RuleSet::attributeRules(const AtomString& key, bool isHTMLName) const
{
auto& rules = isHTMLName ? m_attributeLowercaseLocalNameRules : m_attributeLocalNameRules;
return rules.get(key);
}
inline const RuleSet::RuleDataVector* RuleSet::tagRules(const AtomString& key, bool isHTMLName) const
{
auto& rules = isHTMLName ? m_tagLowercaseLocalNameRules : m_tagLocalNameRules;
return rules.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 CQ::ContainerQuery*> RuleSet::containerQueriesFor(const RuleData& ruleData) const
{
if (m_containerQueryIdentifierForRulePosition.size() <= ruleData.position())
return { };
Vector<const CQ::ContainerQuery*> queries;
auto identifier = m_containerQueryIdentifierForRulePosition[ruleData.position()];
while (identifier) {
auto& query = m_containerQueries[identifier - 1];
queries.append(&query.containerRule->containerQuery());
identifier = query.parent;
};
return queries;
}
} // namespace Style
} // namespace WebCore