blob: 664236dc866b9f73890f773e12415b815ab665d8 [file] [log] [blame]
/*
* 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