blob: 3b483362c496fcdeb93be802b7274d8ca6997191 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll ([email protected])
* Copyright (C) 2004-2005 Allan Sandfeld Jensen ([email protected])
* Copyright (C) 2006, 2007 Nicholas Shanks ([email protected])
* Copyright (C) 2005-2021 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <[email protected]>
* Copyright (C) 2007, 2008 Eric Seidel <[email protected]>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
* Copyright (C) 2012, 2013 Google Inc. All rights reserved.
* Copyright (C) 2014 Igalia S.L.
*
* 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.
*/
#include "config.h"
#include "StyleBuilder.h"
#include "CSSCustomPropertyValue.h"
#include "CSSFontSelector.h"
#include "CSSFunctionValue.h"
#include "CSSPaintImageValue.h"
#include "CSSPendingSubstitutionValue.h"
#include "CSSPropertyParser.h"
#include "CSSRegisteredCustomProperty.h"
#include "CSSValuePair.h"
#include "CSSValuePool.h"
#include "CSSWideKeyword.h"
#include "ComputedStyleDependencies.h"
#include "Document.h"
#include "HTMLElement.h"
#include "PaintWorkletGlobalScope.h"
#include "RenderStyle+GettersInlines.h"
#include "RenderStyle+SettersInlines.h"
#include "Settings.h"
#include "StyleAdjuster.h"
#include "StyleBuilderGenerated.h"
#include "StyleComputedStyle+InitialInlines.h"
#include "StyleCustomProperty.h"
#include "StyleCustomPropertyData.h"
#include "StyleCustomPropertyRegistry.h"
#include "StyleFontSizeFunctions.h"
#include "StylePropertyShorthand.h"
#include <wtf/SetForScope.h>
#include <wtf/TZoneMallocInlines.h>
namespace WebCore {
namespace Style {
WTF_MAKE_TZONE_ALLOCATED_IMPL(Builder);
static const CSSPropertyID firstLowPriorityProperty = static_cast<CSSPropertyID>(lastHighPriorityProperty + 1);
inline bool isValidVisitedLinkProperty(CSSPropertyID id)
{
switch (id) {
case CSSPropertyBackgroundColor:
case CSSPropertyBorderLeftColor:
case CSSPropertyBorderRightColor:
case CSSPropertyBorderTopColor:
case CSSPropertyBorderBottomColor:
case CSSPropertyCaretColor:
case CSSPropertyColor:
case CSSPropertyOutlineColor:
case CSSPropertyColumnRuleColor:
case CSSPropertyTextDecorationColor:
case CSSPropertyTextEmphasisColor:
case CSSPropertyWebkitTextFillColor:
case CSSPropertyWebkitTextStrokeColor:
case CSSPropertyFill:
case CSSPropertyStroke:
case CSSPropertyStrokeColor:
return true;
default:
break;
}
return false;
}
static auto positionTryFallbackProperties(const BuilderContext& context)
{
return context.positionTryFallback ? context.positionTryFallback->properties.get() : nullptr;
}
Builder::Builder(RenderStyle& style, BuilderContext&& context, const MatchResult& matchResult, PropertyCascade::IncludedProperties&& includedProperties, const HashSet<AnimatableCSSProperty>* animatedPropertes)
: m_cascade(matchResult, WTF::move(includedProperties), animatedPropertes, positionTryFallbackProperties(context))
, m_state(BuilderState::create(style, WTF::move(context)))
{
}
Builder::~Builder() = default;
void Builder::applyAllProperties()
{
if (m_cascade.isEmpty())
return;
applyTopPriorityProperties();
applyHighPriorityProperties();
applyNonHighPriorityProperties();
adjustAfterApplying();
}
// Top priority properties affect resolution of high priority properties.
void Builder::applyTopPriorityProperties()
{
if (m_cascade.applyLowPriorityOnly())
return;
applyProperties(firstTopPriorityProperty, lastTopPriorityProperty);
m_state->adjustStyleForInterCharacterRuby();
}
// High priority properties may affect resolution of other properties (they are mostly font related).
void Builder::applyHighPriorityProperties()
{
if (m_cascade.applyLowPriorityOnly())
return;
applyProperties(firstHighPriorityProperty, lastHighPriorityProperty);
m_state->updateFont();
// This needs to apply before other properties for the `lh` unit, but after updating the font.
applyProperties(CSSPropertyLineHeight, CSSPropertyLineHeight);
}
void Builder::applyNonHighPriorityProperties()
{
ASSERT(!m_state->fontDirty());
applyProperties(firstLowPriorityProperty, lastLowPriorityProperty);
applyLogicalGroupProperties();
// Any referenced custom properties are already resolved. This will resolve the remaining ones.
applyCustomProperties();
ASSERT(!m_state->fontDirty());
}
void Builder::adjustAfterApplying()
{
Adjuster::adjustFromBuilder(m_state->renderStyle());
}
void Builder::applyLogicalGroupProperties()
{
// Properties in a logical property group are applied in author specified order which is maintained separately for them.
for (auto id : m_cascade.logicalGroupPropertyIDs())
applyCascadeProperty(m_cascade.logicalGroupProperty(id));
}
void Builder::applyProperties(int firstProperty, int lastProperty)
{
if (m_cascade.customProperties().isEmpty()) [[likely]]
return applyPropertiesImpl<CustomPropertyCycleTracking::Disabled>(firstProperty, lastProperty);
return applyPropertiesImpl<CustomPropertyCycleTracking::Enabled>(firstProperty, lastProperty);
}
template<Builder::CustomPropertyCycleTracking trackCycles>
inline void Builder::applyPropertiesImpl(int firstProperty, int lastProperty)
{
auto applyProperty = [&](size_t index) ALWAYS_INLINE_LAMBDA {
CSSPropertyID propertyID = static_cast<CSSPropertyID>(index);
ASSERT(propertyID != CSSPropertyCustom);
auto& property = m_cascade.normalProperty(propertyID);
if constexpr (trackCycles == CustomPropertyCycleTracking::Enabled) {
m_state->m_inProgressProperties.set(propertyID);
applyCascadeProperty(property);
m_state->m_inProgressProperties.clear(propertyID);
return;
}
// If we don't have any custom properties, then there can't be any cycles.
applyCascadeProperty(property);
};
if (m_cascade.propertyIsPresent().size() == static_cast<size_t>(lastProperty + 1)) {
m_cascade.propertyIsPresent().forEachSetBit(firstProperty, applyProperty);
return;
}
for (int id = firstProperty; id <= lastProperty; ++id) {
CSSPropertyID propertyID = static_cast<CSSPropertyID>(id);
if (!m_cascade.hasNormalProperty(propertyID))
continue;
applyProperty(id);
}
}
void Builder::applyCustomProperties()
{
for (auto& [name, value] : m_cascade.customProperties()) {
if (m_state->m_appliedCustomProperties.contains(name))
continue;
applyCustomPropertyImpl(name, value);
}
}
void Builder::applyCustomProperty(const AtomString& name)
{
if (m_state->m_appliedCustomProperties.contains(name))
return;
auto iterator = m_cascade.customProperties().find(name);
if (iterator == m_cascade.customProperties().end())
return;
applyCustomPropertyImpl(name, iterator->value);
}
void Builder::applyCustomPropertyImpl(const AtomString& name, const PropertyCascade::Property& property)
{
if (!property.cssValue[SelectorChecker::MatchDefault])
return;
Ref customPropertyValue = downcast<CSSCustomPropertyValue>(*property.cssValue[SelectorChecker::MatchDefault]);
bool inCycle = !m_state->m_inProgressCustomProperties.add(name).isNewEntry;
if (inCycle) {
auto isNewCycle = m_state->m_inCycleCustomProperties.add(name).isNewEntry;
if (isNewCycle) {
// Continue resolving dependencies so we detect cycles for them as well.
resolveCustomPropertyValue(customPropertyValue.get());
}
return;
}
// There may be multiple cycles through the same property. Avoid interference from any previously detected cycles.
auto savedInCycleProperties = std::exchange(m_state->m_inCycleCustomProperties, { });
auto createInvalidOrUnset = [&] -> Variant<Ref<const Style::CustomProperty>, CSSWideKeyword> {
// https://drafts.csswg.org/css-variables-2/#invalid-variables
auto* registered = m_state->document().customPropertyRegistry().get(name);
// The property is a non-registered custom property:
// The property is a registered custom property with universal syntax:
// The computed value is the guaranteed-invalid value.
if (!registered || registered->syntax.isUniversal())
return CustomProperty::createForGuaranteedInvalid(name);
// Otherwise:
// ...as if the property’s value had been specified as the unset keyword.
return CSSWideKeyword::Unset;
};
SetForScope levelScope(m_state->m_currentProperty, &property);
SetForScope scopedLinkMatchMutation(m_state->m_linkMatch, SelectorChecker::MatchDefault);
auto resolvedValue = resolveCustomPropertyValue(customPropertyValue.get());
if (!resolvedValue || m_state->m_inCycleCustomProperties.contains(name))
resolvedValue = createInvalidOrUnset();
applyCustomProperty(name, WTF::move(*resolvedValue));
AtomString takenName = m_state->m_inProgressCustomProperties.take(name);
m_state->m_appliedCustomProperties.add(WTF::move(takenName));
m_state->m_inCycleCustomProperties.addAll(WTF::move(savedInCycleProperties));
}
inline void Builder::applyCascadeProperty(const PropertyCascade::Property& property)
{
SetForScope levelScope(m_state->m_currentProperty, &property);
auto applyWithLinkMatch = [&](SelectorChecker::LinkMatchMask linkMatch) {
if (property.cssValue[linkMatch]) {
SetForScope scopedLinkMatchMutation(m_state->m_linkMatch, linkMatch);
applyProperty(property.id, *property.cssValue[linkMatch], linkMatch, property.origins[linkMatch]);
}
};
applyWithLinkMatch(SelectorChecker::MatchDefault);
if (m_state->style().insideLink() == InsideLink::NotInside)
return;
applyWithLinkMatch(SelectorChecker::MatchLink);
applyWithLinkMatch(SelectorChecker::MatchVisited);
m_state->m_linkMatch = SelectorChecker::MatchDefault;
}
bool Builder::applyRollbackCascadeProperty(const PropertyCascade& rollbackCascade, CSSPropertyID propertyID, SelectorChecker::LinkMatchMask linkMatchMask)
{
ASSERT(propertyID != CSSPropertyCustom);
auto* rollbackProperty = [&]() -> const PropertyCascade::Property* {
if (propertyID < firstLogicalGroupProperty) {
if (rollbackCascade.hasNormalProperty(propertyID))
return &rollbackCascade.normalProperty(propertyID);
return nullptr;
}
return rollbackCascade.lastPropertyResolvingLogicalPropertyPair(propertyID, m_state->style().writingMode());
}();
if (!rollbackProperty)
return false;
if (RefPtr value = rollbackProperty->cssValue[linkMatchMask]) {
SetForScope levelScope(m_state->m_currentProperty, rollbackProperty);
applyProperty(propertyID, *value, linkMatchMask, rollbackProperty->origin);
}
return true;
}
bool Builder::applyRollbackCascadeCustomProperty(const PropertyCascade& rollbackCascade, const AtomString& name)
{
auto iterator = rollbackCascade.customProperties().find(name);
if (iterator == rollbackCascade.customProperties().end())
return false;
auto& rollbackProperty = iterator->value;
if (RefPtr value = rollbackProperty.cssValue[SelectorChecker::MatchDefault]) {
Ref customPropertyValue = downcast<CSSCustomPropertyValue>(*value);
SetForScope levelScope(m_state->m_currentProperty, &rollbackProperty);
auto resolvedValue = resolveCustomPropertyValue(customPropertyValue);
if (!resolvedValue)
resolvedValue = CustomProperty::createForGuaranteedInvalid(name);
applyCustomProperty(name, WTF::move(*resolvedValue));
}
return true;
}
void Builder::applyProperty(CSSPropertyID id, CSSValue& value, SelectorChecker::LinkMatchMask linkMatchMask, PropertyCascade::Origin cascadeOrigin)
{
ASSERT_WITH_MESSAGE(!isShorthand(id), "Shorthand property id = %d wasn't expanded at parsing time", id);
ASSERT_WITH_MESSAGE(id != CSSPropertyCustom, "Custom property should be handled by applyCustomProperty");
auto valueToApply = resolveInternalAutoBaseFunction(value);
valueToApply = resolveVariableReferences(id, valueToApply);
auto& style = m_state->style();
if (CSSProperty::isDirectionAwareProperty(id)) {
CSSPropertyID newId = CSSProperty::resolveDirectionAwareProperty(id, style.writingMode());
ASSERT(newId != id);
return applyProperty(newId, valueToApply.get(), linkMatchMask, cascadeOrigin);
}
if (m_state->positionTryFallback())
id = AnchorPositionEvaluator::resolvePositionTryFallbackProperty(id, style.writingMode(), *m_state->positionTryFallback());
auto valueID = WebCore::valueID(valueToApply.get());
auto valueType = [&] {
if (valueID == CSSValueInherit)
return ApplyValueType::Inherit;
if (valueID == CSSValueInitial)
return ApplyValueType::Initial;
return ApplyValueType::Value;
}();
bool isUnset = valueID == CSSValueUnset;
bool isRevert = valueID == CSSValueRevert;
bool isRevertLayer = valueID == CSSValueRevertLayer;
bool isRevertOrRevertLayer = isRevert || isRevertLayer;
if (isRevertOrRevertLayer) {
// In @keyframes, 'revert-layer' rolls back the cascaded value to the author level.
// We can just not apply the property in order to keep the value from the base style.
if (isRevertLayer && m_state->m_isBuildingKeyframeStyle)
return;
auto* rollbackCascade = isRevert ? ensureRollbackCascadeForRevert() : ensureRollbackCascadeForRevertLayer();
if (rollbackCascade) {
// With the rollback cascade built, we need to obtain the property and apply it. If the property is
// not present, then we behave like "unset." Otherwise we apply the property instead of our own.
if (applyRollbackCascadeProperty(*rollbackCascade, id, linkMatchMask))
return;
}
}
auto isInheritedProperty = [&] {
return CSSProperty::isInheritedProperty(id);
};
auto unsetValueType = [&] {
// https://drafts.csswg.org/css-cascade-4/#inherit-initial
// The unset CSS-wide keyword acts as either inherit or initial, depending on whether the property is inherited or not.
return isInheritedProperty() ? ApplyValueType::Inherit : ApplyValueType::Initial;
};
if (isUnset || isRevertOrRevertLayer)
valueType = unsetValueType();
if (!m_state->applyPropertyToRegularStyle() && !isValidVisitedLinkProperty(id)) {
// Limit the properties that can be applied to only the ones honored by :visited.
return;
}
if (valueType == ApplyValueType::Inherit && !isInheritedProperty())
style.setHasExplicitlyInheritedProperties();
if (RefPtr paintImageValue = dynamicDowncast<CSSPaintImageValue>(valueToApply.get())) {
auto& name = paintImageValue->name();
if (RefPtr paintWorklet = const_cast<Document&>(m_state->document()).paintWorkletGlobalScopeForName(name)) {
Locker locker { paintWorklet->paintDefinitionLock() };
if (auto* registration = paintWorklet->paintDefinitionMap().get(name)) {
for (auto& property : registration->inputProperties)
style.addCustomPaintWatchProperty(property);
}
}
}
if (id == CSSPropertySize && valueType == ApplyValueType::Value) [[unlikely]] {
applyPageSizeDescriptor(valueToApply.get());
return;
}
BuilderGenerated::applyProperty(id, m_state, valueToApply.get(), valueType);
if (!isRevertOrRevertLayer)
m_state->disableNativeAppearanceIfNeeded(id, cascadeOrigin);
if (!isUnset && !isRevertOrRevertLayer && m_state->isCurrentPropertyInvalidAtComputedValueTime()) {
// https://drafts.csswg.org/css-variables-2/#invalid-variables
// A declaration can be invalid at computed-value time if...
// When this happens, the computed value is one of the following...
// Otherwise: Either the property’s inherited value or its initial value depending on whether the property
// is inherited or not, respectively, as if the property’s value had been specified as the unset keyword
BuilderGenerated::applyProperty(id, m_state, valueToApply.get(), unsetValueType());
}
}
void Builder::applyCustomProperty(const AtomString& name, Variant<Ref<const Style::CustomProperty>, CSSWideKeyword>&& parsedCustomProperty)
{
auto& style = m_state->style();
auto registeredCustomProperty = m_state->document().customPropertyRegistry().get(name);
auto applyValue = [&](Ref<const CustomProperty>&& valueToApply) {
bool isInherited = !registeredCustomProperty || registeredCustomProperty->inherits;
state().style().setCustomPropertyValue(WTF::move(valueToApply), isInherited);
};
auto applyInitial = [&] {
if (registeredCustomProperty && registeredCustomProperty->initialValue) {
applyValue(*registeredCustomProperty->initialValue);
return;
}
applyValue(CustomProperty::createForGuaranteedInvalid(name));
};
auto applyInherit = [&] {
RefPtr parentValue = state().parentStyle().inheritedCustomProperties().get(name);
if (parentValue && !(registeredCustomProperty && !registeredCustomProperty->inherits)) {
applyValue(*parentValue);
return;
}
if (RefPtr nonInheritedParentValue = state().parentStyle().nonInheritedCustomProperties().get(name)) {
applyValue(*nonInheritedParentValue);
return;
}
applyInitial();
};
return WTF::switchOn(WTF::move(parsedCustomProperty),
[&](CSSWideKeyword&& keyword) {
ApplyValueType valueType = ApplyValueType::Value;
bool isRevert = false;
bool isRevertLayer = false;
auto isInheritedProperty = [&] {
return registeredCustomProperty ? registeredCustomProperty->inherits : true;
};
auto unsetValueType = [&] {
// https://drafts.csswg.org/css-cascade-4/#inherit-initial
// The unset CSS-wide keyword acts as either inherit or initial, depending on whether the property is inherited or not.
return isInheritedProperty() ? ApplyValueType::Inherit : ApplyValueType::Initial;
};
switch (keyword) {
case CSSWideKeyword::Initial:
valueType = ApplyValueType::Initial;
break;
case CSSWideKeyword::Inherit:
valueType = ApplyValueType::Inherit;
break;
case CSSWideKeyword::Unset:
valueType = unsetValueType();
break;
case CSSWideKeyword::Revert:
isRevert = true;
valueType = unsetValueType();
break;
case CSSWideKeyword::RevertLayer:
isRevertLayer = true;
valueType = unsetValueType();
break;
}
if (isRevert || isRevertLayer) {
// In @keyframes, 'revert-layer' rolls back the cascaded value to the author level.
// We can just not apply the property in order to keep the value from the base style.
if (isRevertLayer && m_state->m_isBuildingKeyframeStyle)
return;
auto* rollbackCascade = isRevert ? ensureRollbackCascadeForRevert() : ensureRollbackCascadeForRevertLayer();
if (rollbackCascade) {
// With the rollback cascade built, we need to obtain the property and apply it. If the property is
// not present, then we behave like "unset." Otherwise we apply the property instead of our own.
if (applyRollbackCascadeCustomProperty(*rollbackCascade, name))
return;
}
}
if (!m_state->applyPropertyToRegularStyle()) {
// Limit the properties that can be applied to only the ones honored by :visited.
return;
}
if (valueType == ApplyValueType::Inherit && !isInheritedProperty())
style.setHasExplicitlyInheritedProperties();
switch (valueType) {
case ApplyValueType::Initial:
applyInitial();
break;
case ApplyValueType::Inherit:
applyInherit();
break;
case ApplyValueType::Value:
ASSERT_NOT_REACHED();
break;
};
},
[&](Ref<const CustomProperty>&& resolved) {
if (!m_state->applyPropertyToRegularStyle()) {
// Limit the properties that can be applied to only the ones honored by :visited.
return;
}
applyValue(WTF::move(resolved));
}
);
}
Ref<CSSValue> Builder::resolveInternalAutoBaseFunction(CSSValue& value)
{
RefPtr functionValue = dynamicDowncast<CSSFunctionValue>(value);
if (!functionValue)
return value;
if (functionValue->name() != CSSValueInternalAutoBase)
return value;
RefPtr result = const_cast<CSSValue*>(m_state->style().appearance() == StyleAppearance::Base ? functionValue->item(1) : functionValue->item(0));
if (!result)
return value;
return result.releaseNonNull();
}
Ref<CSSValue> Builder::resolveVariableReferences(CSSPropertyID propertyID, CSSValue& value)
{
if (!value.hasVariableReferences())
return value;
auto variableValue = [&]() -> RefPtr<CSSValue> {
if (auto* substitution = dynamicDowncast<CSSPendingSubstitutionValue>(value))
return substitution->resolveValue(*this, propertyID);
auto& variableReferenceValue = downcast<CSSVariableReferenceValue>(value);
return variableReferenceValue.resolveSingleValue(*this, propertyID);
}();
// https://drafts.csswg.org/css-variables-2/#invalid-variables
// ...as if the property’s value had been specified as the unset keyword.
if (!variableValue || m_state->m_invalidAtComputedValueTimeProperties.get(propertyID))
return CSSPrimitiveValue::create(CSSValueUnset);
return *variableValue;
}
RefPtr<const CustomProperty> Builder::resolveCustomPropertyForContainerQueries(const CSSCustomPropertyValue& value)
{
auto resolvedValue = resolveCustomPropertyValue(const_cast<CSSCustomPropertyValue&>(value));
if (!resolvedValue)
return CustomProperty::createForGuaranteedInvalid(value.name());
return WTF::switchOn(*resolvedValue,
[&](const CSSWideKeyword& keyword) -> RefPtr<const CustomProperty> {
auto name = value.name();
auto* registered = m_state->document().customPropertyRegistry().get(name);
bool isInherited = !registered || registered->inherits;
auto initial = [&]() -> RefPtr<const CustomProperty> {
if (registered)
return registered->initialValue;
return CustomProperty::createForGuaranteedInvalid(name);
};
auto inherit = [&]() -> RefPtr<const CustomProperty> {
RefPtr parentValue = isInherited
? m_state->parentStyle().inheritedCustomProperties().get(name)
: m_state->parentStyle().nonInheritedCustomProperties().get(name);
if (parentValue)
return parentValue;
return initial();
};
switch (keyword) {
case CSSWideKeyword::Initial:
return initial();
case CSSWideKeyword::Inherit:
return inherit();
case CSSWideKeyword::Unset:
return isInherited ? inherit() : initial();
case CSSWideKeyword::Revert:
case CSSWideKeyword::RevertLayer:
// https://drafts.csswg.org/css-contain-3/#style-container
// "Cascade-dependent keywords, such as revert and revert-layer, are invalid as values in a style feature,
// and cause the container style query to be false."
return nullptr;
}
RELEASE_ASSERT_NOT_REACHED();
return nullptr;
},
[](const Ref<const CustomProperty>& resolvedValue) -> RefPtr<const CustomProperty> {
return resolvedValue.copyRef();
}
);
}
std::optional<Variant<Ref<const Style::CustomProperty>, CSSWideKeyword>> Builder::resolveCustomPropertyValue(CSSCustomPropertyValue& value)
{
auto name = value.name();
if (auto keyword = value.tryCSSWideKeyword())
return { { *keyword } };
auto* registered = m_state->document().customPropertyRegistry().get(name);
auto preResolved = switchOn(value.value(),
[&](const Ref<CSSVariableReferenceValue>&) -> std::optional<Variant<Ref<const Style::CustomProperty>, CSSWideKeyword>> {
return { };
},
[&](const Ref<CSSVariableData>& data) -> std::optional<Variant<Ref<const Style::CustomProperty>, CSSWideKeyword>> {
if (!registered)
return { { CustomProperty::createForVariableData(name, data.copyRef()) } };
return { };
},
[&](const CSSWideKeyword& keyword) -> std::optional<Variant<Ref<const Style::CustomProperty>, CSSWideKeyword>> {
if (!registered)
return { { keyword } };
return { };
}
);
if (preResolved)
return preResolved;
auto resolvedData = switchOn(value.value(),
[&](const Ref<CSSVariableReferenceValue>& variableReferenceValue) -> RefPtr<CSSVariableData> {
return variableReferenceValue->resolveVariableReferences(*this);
},
[&](const Ref<CSSVariableData>& data) -> RefPtr<CSSVariableData> {
return data.ptr();
},
[&](const CSSWideKeyword&) -> RefPtr<CSSVariableData> {
return nullptr;
}
);
if (!resolvedData)
return { };
if (!registered) {
// CSS-wide keywords are allowed in var() fallbacks of unregistered properties.
if (auto keyword = CSSPropertyParser::parseCSSWideKeyword(resolvedData->tokens()))
return { { *keyword } };
return { { CustomProperty::createForVariableData(name, *resolvedData) } };
}
auto dependencies = CSSPropertyParser::collectParsedCustomPropertyValueDependencies(registered->syntax, resolvedData->tokens(), resolvedData->context());
// https://drafts.css-houdini.org/css-properties-values-api/#dependency-cycles
bool hasCycles = false;
bool isFontDependent = false;
auto checkDependencies = [&](auto& propertyDependencies) {
for (auto property : propertyDependencies) {
if (m_state->m_inProgressProperties.get(property)) {
m_state->m_invalidAtComputedValueTimeProperties.set(property);
hasCycles = true;
}
if (property == CSSPropertyFontSize)
isFontDependent = true;
}
};
checkDependencies(dependencies.properties);
if (m_state->element() == m_state->document().documentElement())
checkDependencies(dependencies.rootProperties);
if (hasCycles)
return { };
if (isFontDependent)
m_state->updateFont();
return CSSPropertyParser::parseTypedCustomPropertyValue(name, registered->syntax, resolvedData->tokens(), m_state, resolvedData->context());
}
void Builder::applyPageSizeDescriptor(CSSValue& value)
{
m_state->style().setPageSize(Style::ComputedStyle::initialPageSize());
auto convertedPageSize = toStyleFromCSSValue<PageSize>(m_state, value);
if (m_state->isCurrentPropertyInvalidAtComputedValueTime())
return;
m_state->style().setPageSize(WTF::move(convertedPageSize));
}
const PropertyCascade* Builder::ensureRollbackCascadeForRevert()
{
auto rollbackOrigin = m_state->m_currentProperty->origin;
switch (rollbackOrigin) {
case PropertyCascade::Origin::PositionFallback:
// "Similar to the Animation Origin, use of the revert value acts as if the property was part of the Author Origin."
// https://drafts.csswg.org/css-anchor-position-1/#fallback-rule
case PropertyCascade::Origin::Author:
rollbackOrigin = PropertyCascade::Origin::User;
break;
case PropertyCascade::Origin::User:
rollbackOrigin = PropertyCascade::Origin::UserAgent;
break;
case PropertyCascade::Origin::UserAgent:
return nullptr;
}
auto key = makeRollbackCascadeKey(rollbackOrigin);
return m_rollbackCascades.ensure(key, [&] {
return makeUnique<const PropertyCascade>(m_cascade, rollbackOrigin);
}).iterator->value.get();
}
const PropertyCascade* Builder::ensureRollbackCascadeForRevertLayer()
{
auto& property = *m_state->m_currentProperty;
auto rollbackLayerPriority = property.cascadeLayerPriority;
if (!rollbackLayerPriority)
return ensureRollbackCascadeForRevert();
ASSERT(property.fromStyleAttribute == FromStyleAttribute::No || property.cascadeLayerPriority == RuleSet::cascadeLayerPriorityForUnlayered);
// Style attribute reverts to the regular author style.
if (property.fromStyleAttribute == FromStyleAttribute::No)
--rollbackLayerPriority;
auto key = makeRollbackCascadeKey(property.origin, property.styleScopeOrdinal, rollbackLayerPriority);
return m_rollbackCascades.ensure(key, [&] {
return makeUnique<const PropertyCascade>(m_cascade, property.origin, property.styleScopeOrdinal, rollbackLayerPriority);
}).iterator->value.get();
}
auto Builder::makeRollbackCascadeKey(PropertyCascade::Origin cascadeOrigin, ScopeOrdinal scopeOrdinal, CascadeLayerPriority cascadeLayerPriority) -> RollbackCascadeKey
{
static constexpr auto isNonEmptyValue = true;
return { static_cast<unsigned>(cascadeOrigin), static_cast<unsigned>(scopeOrdinal), static_cast<unsigned>(cascadeLayerPriority), isNonEmptyValue };
}
}
}