blob: f7057028f60bef56c8f67ee745fa78abc79601ed [file]
/*
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "MatchResult.h"
#include "WebAnimationTypes.h"
#include <wtf/BitSet.h>
#include <wtf/EnumSet.h>
#include <wtf/OrderedHashMap.h>
#include <wtf/TZoneMalloc.h>
#include <wtf/ValueOrReference.h>
namespace WebCore {
class StyleResolver;
namespace Style {
class PropertyCascade {
WTF_MAKE_TZONE_ALLOCATED(PropertyCascade);
public:
using PropertyBitSet = WTF::BitSet<lastLowPriorityProperty + 1>;
enum class AnimationSource : uint8_t {
CSSAnimation,
CSSTransition,
};
enum class PropertyType : uint8_t {
NonInherited = 1 << 0,
Inherited = 1 << 1,
ExplicitlyInherited = 1 << 2,
AfterAnimation = 1 << 3,
StartingStyle = 1 << 4,
NonCacheable = 1 << 5,
};
enum class Origin : uint8_t {
UserAgent,
User,
Author,
PositionFallback
};
static constexpr OptionSet<PropertyType> normalPropertyTypes() { return { PropertyType::NonInherited, PropertyType::Inherited }; }
static constexpr OptionSet<PropertyType> startingStylePropertyTypes() { return normalPropertyTypes() | PropertyType::StartingStyle; }
struct IncludedProperties {
OptionSet<PropertyType> types;
// Ids are mutually exclusive with types. They are low-priority only and have any cascade aliases resolved.
Vector<CSSPropertyID, 4> ids { };
bool isEmpty() const { return !types && ids.isEmpty(); }
};
static IncludedProperties normalProperties() { return { normalPropertyTypes() }; }
PropertyCascade(const MatchResult&, IncludedProperties&&, const HashMap<AnimatableCSSProperty, EnumSet<AnimationSource>>* animatedProperties = nullptr, const StyleProperties* positionTryFallbackProperties = nullptr);
PropertyCascade(const PropertyCascade&, Origin, std::optional<ScopeOrdinal> rollbackScope = { }, std::optional<CascadeLayerPriority> maximumCascadeLayerPriorityForRollback = { });
enum RevertRuleTag { RevertRule };
PropertyCascade(const PropertyCascade&, RevertRuleTag);
~PropertyCascade();
struct Property {
CSSPropertyID id;
Origin origin;
ScopeOrdinal styleScopeOrdinal;
CascadeLayerPriority cascadeLayerPriority;
FromStyleAttribute fromStyleAttribute;
std::array<CSSValue*, 3> cssValue; // Values for link match states MatchDefault, MatchLink and MatchVisited
std::array<Origin, 3> origins;
};
bool isEmpty() const { return m_propertyIsPresent.isEmpty() && !m_seenLogicalGroupPropertyCount; }
bool hasNormalProperty(CSSPropertyID) const;
const Property& normalProperty(CSSPropertyID) const LIFETIME_BOUND;
bool hasLogicalGroupProperty(CSSPropertyID) const;
const Property& logicalGroupProperty(CSSPropertyID) const LIFETIME_BOUND;
const Property* lastPropertyResolvingLogicalPropertyPair(CSSPropertyID, WritingMode) const LIFETIME_BOUND;
bool hasCustomProperty(const AtomString&) const;
const Property& customProperty(const AtomString&) const LIFETIME_BOUND;
const Property& functionResultProperty() const LIFETIME_BOUND;
std::span<const CSSPropertyID> logicalGroupPropertyIDs() const LIFETIME_BOUND;
const OrderedHashMap<AtomString, Property>& customProperties() const LIFETIME_BOUND { return m_customProperties; }
ValueOrReference<HashSet<AnimatableCSSProperty>> overriddenAnimatedProperties() const;
const MatchResult& matchResult() const LIFETIME_BOUND { return m_matchResult; }
PropertyBitSet& propertyIsPresent() LIFETIME_BOUND { return m_propertyIsPresent; }
const PropertyBitSet& propertyIsPresent() const LIFETIME_BOUND { return m_propertyIsPresent; }
bool applyLowPriorityOnly() const { return !m_includedProperties.ids.isEmpty(); }
private:
void buildCascade();
bool addNormalMatches(Origin);
void addImportantMatches(Origin);
bool addMatch(const MatchedProperties&, Origin, IsImportant);
bool shouldApplyAfterAnimation(const StyleProperties::PropertyReference&);
void addPositionTryFallbackProperties();
void set(CSSPropertyID, CSSValue&, const MatchedProperties&, Origin);
void setLogicalGroupProperty(CSSPropertyID, CSSValue&, const MatchedProperties&, Origin);
static void NODELETE setPropertyInternal(Property&, CSSPropertyID, CSSValue&, const MatchedProperties&, Origin);
void setDelayingForRuleRollback(CSSPropertyID, CSSValue&, const MatchedProperties&, Origin);
bool NODELETE hasProperty(CSSPropertyID, const CSSValue&);
bool NODELETE mayOverrideExistingProperty(CSSPropertyID, const CSSValue&);
unsigned logicalGroupPropertyIndex(CSSPropertyID) const;
void setLogicalGroupPropertyIndex(CSSPropertyID, unsigned);
void sortLogicalGroupPropertyIDs();
const MatchResult& m_matchResult;
const IncludedProperties m_includedProperties;
const Origin m_maximumOrigin;
const std::optional<ScopeOrdinal> m_rollbackScope;
const std::optional<CascadeLayerPriority> m_maximumCascadeLayerPriorityForRollback;
const unsigned m_ruleRollbackDepth { 0 };
struct AnimationLayer {
explicit AnimationLayer(const HashMap<AnimatableCSSProperty, EnumSet<AnimationSource>>&);
const HashMap<AnimatableCSSProperty, EnumSet<AnimationSource>>& properties;
HashSet<AnimatableCSSProperty> overriddenProperties;
bool hasCustomProperties { false };
bool hasFontSize { false };
bool hasLineHeight { false };
};
std::optional<AnimationLayer> m_animationLayer;
std::optional<MatchedProperties> m_positionTryFallbackProperties;
struct DelayedRollbackProperty {
CSSValue& value;
const MatchedProperties& properties;
Origin origin;
};
HashMap<std::pair<unsigned, AtomString>, Deque<DelayedRollbackProperty>> m_delayedRollbackProperties;
// The CSSPropertyID enum is sorted like this:
// 1. CSSPropertyInvalid and CSSPropertyCustom.
// 2. Normal longhand properties (high priority ones followed by low priority ones).
// 3. Longhand properties in a logical property group.
// 4. Shorthand properties.
//
// 'm_properties' is used for both normal and logical longhands, so it has size 'lastLogicalGroupProperty + 1'.
// It could actually be 2 units smaller, but then we would have to subtract 'firstCSSProperty', which may not be worth it.
// 'm_propertyIsPresent' is not used for logical group properties, so we only need to cover up to the last low priority one.
std::array<Property, lastLogicalGroupProperty + 1> m_properties;
PropertyBitSet m_propertyIsPresent;
static constexpr unsigned logicalGroupPropertyCount = lastLogicalGroupProperty - firstLogicalGroupProperty + 1;
std::array<unsigned, logicalGroupPropertyCount> m_logicalGroupPropertyIndices { };
unsigned m_lastIndexForLogicalGroup { 0 };
std::array<CSSPropertyID, logicalGroupPropertyCount> m_logicalGroupPropertyIDs { };
unsigned m_seenLogicalGroupPropertyCount { 0 };
CSSPropertyID m_lowestSeenLogicalGroupProperty { lastLogicalGroupProperty };
CSSPropertyID m_highestSeenLogicalGroupProperty { firstLogicalGroupProperty };
OrderedHashMap<AtomString, Property> m_customProperties;
};
inline bool PropertyCascade::hasNormalProperty(CSSPropertyID id) const
{
ASSERT(id < firstLogicalGroupProperty);
return m_propertyIsPresent.get(id);
}
inline const PropertyCascade::Property& PropertyCascade::normalProperty(CSSPropertyID id) const
{
ASSERT(hasNormalProperty(id));
return m_properties[id];
}
inline unsigned PropertyCascade::logicalGroupPropertyIndex(CSSPropertyID id) const
{
ASSERT(id >= firstLogicalGroupProperty);
ASSERT(id <= lastLogicalGroupProperty);
return m_logicalGroupPropertyIndices[id - firstLogicalGroupProperty];
}
inline void PropertyCascade::setLogicalGroupPropertyIndex(CSSPropertyID id, unsigned index)
{
ASSERT(id >= firstLogicalGroupProperty);
ASSERT(id <= lastLogicalGroupProperty);
m_logicalGroupPropertyIndices[id - firstLogicalGroupProperty] = index;
}
inline bool PropertyCascade::hasLogicalGroupProperty(CSSPropertyID id) const
{
return logicalGroupPropertyIndex(id);
}
inline const PropertyCascade::Property& PropertyCascade::logicalGroupProperty(CSSPropertyID id) const
{
ASSERT(hasLogicalGroupProperty(id));
return m_properties[id];
}
inline std::span<const CSSPropertyID> PropertyCascade::logicalGroupPropertyIDs() const
{
return std::span { m_logicalGroupPropertyIDs }.first(m_seenLogicalGroupPropertyCount);
}
inline bool PropertyCascade::hasCustomProperty(const AtomString& name) const
{
return m_customProperties.contains(name);
}
inline const PropertyCascade::Property& PropertyCascade::customProperty(const AtomString& name) const
{
ASSERT(hasCustomProperty(name));
return m_customProperties.find(name)->value;
}
} // namespace Style
} // namespace WebCore