Index: third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp |
diff --git a/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp |
index 0aa388db0bf3dca304d164fae5093570908ffe4c..56f5e359f37fd2718294bd0ffc6af5596e641c25 100644 |
--- a/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp |
+++ b/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp |
@@ -6,9 +6,16 @@ |
#include "core/StylePropertyShorthand.h" |
#include "core/animation/StringKeyframe.h" |
+#include "core/css/CSSCustomPropertyDeclaration.h" |
+#include "core/css/CSSValue.h" |
#include "core/css/CSSVariableReferenceValue.h" |
+#include "core/css/ComputedStyleCSSValueMapping.h" |
+#include "core/css/PropertyRegistry.h" |
+#include "core/css/parser/CSSTokenizer.h" |
#include "core/css/resolver/CSSVariableResolver.h" |
+#include "core/css/resolver/StyleBuilder.h" |
#include "core/css/resolver/StyleResolverState.h" |
+#include "core/style/DataEquivalency.h" |
#include "platform/RuntimeEnabledFeatures.h" |
#include "wtf/PtrUtil.h" |
#include <memory> |
@@ -50,6 +57,45 @@ class ResolvedVariableChecker : public InterpolationType::ConversionChecker { |
Persistent<const CSSValue> m_resolvedValue; |
}; |
+class InheritedCustomPropertyChecker |
+ : public InterpolationType::ConversionChecker { |
+ public: |
+ static std::unique_ptr<InheritedCustomPropertyChecker> create( |
+ const AtomicString& property, |
+ bool isInheritedProperty, |
+ const CSSValue* inheritedValue, |
+ const CSSValue* initialValue) { |
+ return WTF::wrapUnique(new InheritedCustomPropertyChecker( |
+ property, isInheritedProperty, inheritedValue, initialValue)); |
+ } |
+ |
+ private: |
+ InheritedCustomPropertyChecker(const AtomicString& name, |
+ bool isInheritedProperty, |
+ const CSSValue* inheritedValue, |
+ const CSSValue* initialValue) |
+ : m_name(name), |
+ m_isInheritedProperty(isInheritedProperty), |
+ m_inheritedValue(inheritedValue), |
+ m_initialValue(initialValue) {} |
+ |
+ bool isValid(const InterpolationEnvironment& environment, |
+ const InterpolationValue&) const final { |
+ const CSSValue* inheritedValue = |
+ environment.state().parentStyle()->getRegisteredVariable( |
+ m_name, m_isInheritedProperty); |
+ if (!inheritedValue) { |
+ inheritedValue = m_initialValue.get(); |
+ } |
+ return m_inheritedValue->equals(*inheritedValue); |
+ } |
+ |
+ const AtomicString& m_name; |
+ const bool m_isInheritedProperty; |
+ Persistent<const CSSValue> m_inheritedValue; |
+ Persistent<const CSSValue> m_initialValue; |
+}; |
+ |
CSSInterpolationType::CSSInterpolationType(PropertyHandle property) |
: InterpolationType(property) { |
DCHECK(!isShorthandProperty(cssProperty())); |
@@ -76,16 +122,22 @@ InterpolationValue CSSInterpolationType::maybeConvertSingleInternal( |
const InterpolationValue& underlying, |
ConversionCheckers& conversionCheckers) const { |
const CSSValue* value = toCSSPropertySpecificKeyframe(keyframe).value(); |
+ const StyleResolverState& state = environment.state(); |
if (!value) |
return maybeConvertNeutral(underlying, conversionCheckers); |
+ if (getProperty().isCSSCustomProperty()) { |
+ return maybeConvertCustomPropertyDeclaration( |
+ toCSSCustomPropertyDeclaration(*value), state, conversionCheckers); |
+ } |
+ |
if (value->isVariableReferenceValue() || |
value->isPendingSubstitutionValue()) { |
bool omitAnimationTainted = false; |
const CSSValue* resolvedValue = |
CSSVariableResolver::resolveVariableReferences( |
- environment.state(), cssProperty(), *value, omitAnimationTainted); |
+ state, cssProperty(), *value, omitAnimationTainted); |
conversionCheckers.push_back( |
ResolvedVariableChecker::create(cssProperty(), value, resolvedValue)); |
value = resolvedValue; |
@@ -93,32 +145,174 @@ InterpolationValue CSSInterpolationType::maybeConvertSingleInternal( |
if (value->isInitialValue() || |
(value->isUnsetValue() && |
- !CSSPropertyMetadata::isInheritedProperty(cssProperty()))) |
- return maybeConvertInitial(environment.state(), conversionCheckers); |
+ !CSSPropertyMetadata::isInheritedProperty(cssProperty()))) { |
+ return maybeConvertInitial(state, conversionCheckers); |
+ } |
if (value->isInheritedValue() || |
(value->isUnsetValue() && |
- CSSPropertyMetadata::isInheritedProperty(cssProperty()))) |
- return maybeConvertInherit(environment.state(), conversionCheckers); |
+ CSSPropertyMetadata::isInheritedProperty(cssProperty()))) { |
+ return maybeConvertInherit(state, conversionCheckers); |
+ } |
+ |
+ return maybeConvertValue(*value, state, conversionCheckers); |
+} |
+ |
+const PropertyRegistry::Registration* getRegistration( |
+ const StyleResolverState& state, |
+ const AtomicString& propertyName) { |
+ const PropertyRegistry* registry = state.document().propertyRegistry(); |
+ if (registry) { |
+ return registry->registration(propertyName); |
+ } |
+ return nullptr; |
+} |
+ |
+InterpolationValue CSSInterpolationType::maybeConvertCustomPropertyDeclaration( |
+ const CSSCustomPropertyDeclaration& declaration, |
+ const StyleResolverState& state, |
+ ConversionCheckers& conversionCheckers) const { |
+ InterpolationValue result = maybeConvertCustomPropertyDeclarationInternal( |
+ declaration, state, conversionCheckers); |
+ if (result) { |
+ return result; |
+ } |
+ |
+ // TODO(alancutter): Make explicit and assert in code that this falls back to |
+ // the default CSSValueInterpolationType handler. |
+ // This might involve making the "catch-all" InterpolationType explicit |
+ // e.g. add bool InterpolationType::isCatchAll(). |
+ return maybeConvertValue(declaration, state, conversionCheckers); |
+} |
+ |
+InterpolationValue |
+CSSInterpolationType::maybeConvertCustomPropertyDeclarationInternal( |
+ const CSSCustomPropertyDeclaration& declaration, |
+ const StyleResolverState& state, |
+ ConversionCheckers& conversionCheckers) const { |
+ const AtomicString& name = declaration.name(); |
+ DCHECK_EQ(getProperty().customPropertyName(), name); |
+ |
+ const PropertyRegistry::Registration* registration = |
+ getRegistration(state, name); |
+ |
+ if (!declaration.value()) { |
+ // Unregistered custom properties inherit: |
+ // https://www.w3.org/TR/css-variables-1/#defining-variables |
+ bool isInheritedProperty = registration ? registration->inherits() : true; |
+ DCHECK(declaration.isInitial(isInheritedProperty) || |
+ declaration.isInherit(isInheritedProperty)); |
+ |
+ if (!registration) { |
+ return nullptr; |
+ } |
+ |
+ if (declaration.isInitial(isInheritedProperty)) { |
+ return maybeConvertValue(*registration->initial(), state, |
+ conversionCheckers); |
+ } |
+ |
+ DCHECK(declaration.isInherit(isInheritedProperty)); |
+ const CSSValue* inheritedValue = |
+ state.parentStyle()->getRegisteredVariable(name, isInheritedProperty); |
+ if (!inheritedValue) { |
+ inheritedValue = registration->initial(); |
+ } |
+ conversionCheckers.push_back(InheritedCustomPropertyChecker::create( |
+ name, isInheritedProperty, inheritedValue, registration->initial())); |
+ return maybeConvertValue(*inheritedValue, state, conversionCheckers); |
+ } |
- return maybeConvertValue(*value, environment.state(), conversionCheckers); |
+ if (declaration.value()->needsVariableResolution()) { |
+ // TODO(alancutter): Support smooth interpolation with var() values for |
+ // registered custom properties. This requires integrating animated custom |
+ // property value application with the CSSVariableResolver to apply them in |
+ // the appropriate order defined by the chain of var() dependencies. |
+ // All CSSInterpolationTypes should fail convertion here except for |
+ // CSSValueInterpolationType. |
+ return nullptr; |
+ } |
+ |
+ if (registration) { |
+ const CSSValue* parsedValue = |
+ declaration.value()->parseForSyntax(registration->syntax()); |
+ if (parsedValue) { |
+ return maybeConvertValue(*parsedValue, state, conversionCheckers); |
+ } |
+ } |
+ |
+ return nullptr; |
} |
InterpolationValue CSSInterpolationType::maybeConvertUnderlyingValue( |
const InterpolationEnvironment& environment) const { |
- // TODO(alancutter): Add support for converting underlying registered custom |
- // property values. |
- return maybeConvertStandardPropertyUnderlyingValue(environment.state()); |
+ const StyleResolverState& state = environment.state(); |
+ if (!getProperty().isCSSCustomProperty()) { |
+ return maybeConvertStandardPropertyUnderlyingValue(state); |
+ } |
+ |
+ const AtomicString& name = getProperty().customPropertyName(); |
+ const PropertyRegistry::Registration* registration = |
+ getRegistration(state, name); |
+ if (!registration) { |
+ return nullptr; |
+ } |
+ const CSSValue* underlyingValue = |
+ state.style()->getRegisteredVariable(name, registration->inherits()); |
+ if (!underlyingValue) { |
+ underlyingValue = registration->initial(); |
+ } |
+ // TODO(alancutter): Remove the need for passing in conversion checkers. |
+ ConversionCheckers dummyConversionCheckers; |
+ return maybeConvertValue(*underlyingValue, environment.state(), |
+ dummyConversionCheckers); |
} |
void CSSInterpolationType::apply( |
const InterpolableValue& interpolableValue, |
const NonInterpolableValue* nonInterpolableValue, |
InterpolationEnvironment& environment) const { |
- // TODO(alancutter): Add support for applying registered custom property |
- // values. |
- return applyStandardPropertyValue(interpolableValue, nonInterpolableValue, |
- environment.state()); |
+ StyleResolverState& state = environment.state(); |
+ |
+ if (getProperty().isCSSCustomProperty()) { |
+ applyCustomPropertyValue(interpolableValue, nonInterpolableValue, state); |
+ return; |
+ } |
+ applyStandardPropertyValue(interpolableValue, nonInterpolableValue, state); |
+} |
+ |
+void CSSInterpolationType::applyCustomPropertyValue( |
+ const InterpolableValue& interpolableValue, |
+ const NonInterpolableValue* nonInterpolableValue, |
+ StyleResolverState& state) const { |
+ DCHECK(getProperty().isCSSCustomProperty()); |
+ |
+ const CSSValue* cssValue = |
+ createCSSValue(interpolableValue, nonInterpolableValue, state); |
+ if (cssValue->isCustomPropertyDeclaration()) { |
+ StyleBuilder::applyProperty(cssProperty(), state, *cssValue); |
+ return; |
+ } |
+ |
+ // TODO(alancutter): Defer tokenization of the CSSValue until it is needed. |
+ String stringValue = cssValue->cssText(); |
+ CSSTokenizer tokenizer(stringValue); |
+ bool isAnimationTainted = true; |
+ bool needsVariableResolution = false; |
+ RefPtr<CSSVariableData> variableData = CSSVariableData::create( |
+ tokenizer.tokenRange(), isAnimationTainted, needsVariableResolution); |
+ ComputedStyle& style = *state.style(); |
+ const AtomicString& propertyName = getProperty().customPropertyName(); |
+ const PropertyRegistry::Registration* registration = |
+ getRegistration(state, propertyName); |
+ DCHECK(registration); |
+ if (registration->inherits()) { |
+ style.setResolvedInheritedVariable(propertyName, variableData.release(), |
+ cssValue); |
+ } else { |
+ style.setResolvedNonInheritedVariable(propertyName, variableData.release(), |
+ cssValue); |
+ } |
} |
} // namespace blink |