Commit ee866964 authored by alancutter's avatar alancutter Committed by Commit bot

Support interpolation of animatable shorthand properties containing var()

This change adds support for handling pending substitution values to CSS
animations to allow for smooth interpolation between shorthand values
with var() references in them.

BUG=647123

Review-Url: https://codereview.chromium.org/2340893003
Cr-Commit-Position: refs/heads/master@{#419096}
parent 1f9dabc1
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<style>
body {
--x: green;
--y: lime;
}
@keyframes test {
from { background: var(--x); }
to { background: var(--y); }
}
#cssAnimations {
animation: test 1s -0.5s linear paused;
}
</style>
<div id="cssAnimations"></div>
<div id="webAnimations"></div>
<script>
test(() => {
assert_equals(getComputedStyle(cssAnimations).backgroundColor, 'rgb(0, 192, 0)');
}, 'CSS Animations should interpolate shorthand properties with variable references in them.');
test(() => {
var animation = webAnimations.animate({background: ['var(--x)', 'var(--y)']}, 1);
animation.currentTime = 0.5;
assert_equals(getComputedStyle(webAnimations).backgroundColor, 'rgb(0, 192, 0)');
}, 'Web Animations should interpolate shorthand properties with variable references in them.');
</script>
...@@ -17,13 +17,13 @@ namespace blink { ...@@ -17,13 +17,13 @@ namespace blink {
class ResolvedVariableChecker : public InterpolationType::ConversionChecker { class ResolvedVariableChecker : public InterpolationType::ConversionChecker {
public: public:
static std::unique_ptr<ResolvedVariableChecker> create(CSSPropertyID property, const CSSVariableReferenceValue* variableReference, const CSSValue* resolvedValue) static std::unique_ptr<ResolvedVariableChecker> create(CSSPropertyID property, const CSSValue* variableReference, const CSSValue* resolvedValue)
{ {
return wrapUnique(new ResolvedVariableChecker(property, variableReference, resolvedValue)); return wrapUnique(new ResolvedVariableChecker(property, variableReference, resolvedValue));
} }
private: private:
ResolvedVariableChecker(CSSPropertyID property, const CSSVariableReferenceValue* variableReference, const CSSValue* resolvedValue) ResolvedVariableChecker(CSSPropertyID property, const CSSValue* variableReference, const CSSValue* resolvedValue)
: m_property(property) : m_property(property)
, m_variableReference(variableReference) , m_variableReference(variableReference)
, m_resolvedValue(resolvedValue) , m_resolvedValue(resolvedValue)
...@@ -37,10 +37,16 @@ private: ...@@ -37,10 +37,16 @@ private:
} }
CSSPropertyID m_property; CSSPropertyID m_property;
Persistent<const CSSVariableReferenceValue> m_variableReference; Persistent<const CSSValue> m_variableReference;
Persistent<const CSSValue> m_resolvedValue; Persistent<const CSSValue> m_resolvedValue;
}; };
CSSInterpolationType::CSSInterpolationType(CSSPropertyID property)
: InterpolationType(PropertyHandle(property))
{
DCHECK(!isShorthandProperty(cssProperty()));
}
InterpolationValue CSSInterpolationType::maybeConvertSingle(const PropertySpecificKeyframe& keyframe, const InterpolationEnvironment& environment, const InterpolationValue& underlying, ConversionCheckers& conversionCheckers) const InterpolationValue CSSInterpolationType::maybeConvertSingle(const PropertySpecificKeyframe& keyframe, const InterpolationEnvironment& environment, const InterpolationValue& underlying, ConversionCheckers& conversionCheckers) const
{ {
const CSSValue* resolvedCSSValueOwner; const CSSValue* resolvedCSSValueOwner;
...@@ -49,13 +55,9 @@ InterpolationValue CSSInterpolationType::maybeConvertSingle(const PropertySpecif ...@@ -49,13 +55,9 @@ InterpolationValue CSSInterpolationType::maybeConvertSingle(const PropertySpecif
if (!value) if (!value)
return maybeConvertNeutral(underlying, conversionCheckers); return maybeConvertNeutral(underlying, conversionCheckers);
// TODO(alancutter): Support animation of var() in shorthand properties. if (value->isVariableReferenceValue() || value->isPendingSubstitutionValue()) {
if (value->isPendingSubstitutionValue()) resolvedCSSValueOwner = CSSVariableResolver::resolveVariableReferences(environment.state(), cssProperty(), *value);
return nullptr; conversionCheckers.append(ResolvedVariableChecker::create(cssProperty(), value, resolvedCSSValueOwner));
if (value->isVariableReferenceValue() && !isShorthandProperty(cssProperty())) {
resolvedCSSValueOwner = CSSVariableResolver::resolveVariableReferences(environment.state(), cssProperty(), toCSSVariableReferenceValue(*value));
conversionCheckers.append(ResolvedVariableChecker::create(cssProperty(), toCSSVariableReferenceValue(value), resolvedCSSValueOwner));
value = resolvedCSSValueOwner; value = resolvedCSSValueOwner;
} }
......
...@@ -7,15 +7,12 @@ ...@@ -7,15 +7,12 @@
#include "core/animation/InterpolationEnvironment.h" #include "core/animation/InterpolationEnvironment.h"
#include "core/animation/InterpolationType.h" #include "core/animation/InterpolationType.h"
#include "core/css/CSSPropertyMetadata.h"
namespace blink { namespace blink {
class CSSInterpolationType : public InterpolationType { class CSSInterpolationType : public InterpolationType {
protected: protected:
CSSInterpolationType(CSSPropertyID property) CSSInterpolationType(CSSPropertyID);
: InterpolationType(PropertyHandle(property))
{ }
CSSPropertyID cssProperty() const { return getProperty().cssProperty(); } CSSPropertyID cssProperty() const { return getProperty().cssProperty(); }
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "core/animation/SVGRectInterpolationType.h" #include "core/animation/SVGRectInterpolationType.h"
#include "core/animation/SVGTransformListInterpolationType.h" #include "core/animation/SVGTransformListInterpolationType.h"
#include "core/animation/SVGValueInterpolationType.h" #include "core/animation/SVGValueInterpolationType.h"
#include "core/css/CSSPropertyMetadata.h"
#include "wtf/PtrUtil.h" #include "wtf/PtrUtil.h"
#include <memory> #include <memory>
......
...@@ -55,8 +55,7 @@ PropertyHandleSet StringKeyframe::properties() const ...@@ -55,8 +55,7 @@ PropertyHandleSet StringKeyframe::properties() const
PropertyHandleSet properties; PropertyHandleSet properties;
for (unsigned i = 0; i < m_cssPropertyMap->propertyCount(); ++i) { for (unsigned i = 0; i < m_cssPropertyMap->propertyCount(); ++i) {
StylePropertySet::PropertyReference propertyReference = m_cssPropertyMap->propertyAt(i); StylePropertySet::PropertyReference propertyReference = m_cssPropertyMap->propertyAt(i);
DCHECK( DCHECK(!isShorthandProperty(propertyReference.id()))
!isShorthandProperty(propertyReference.id()) || propertyReference.value().isVariableReferenceValue())
<< "Web Animations: Encountered unexpanded shorthand CSS property (" << propertyReference.id() << ")."; << "Web Animations: Encountered unexpanded shorthand CSS property (" << propertyReference.id() << ").";
properties.add(PropertyHandle(propertyReference.id(), false)); properties.add(PropertyHandle(propertyReference.id(), false));
} }
......
...@@ -153,10 +153,22 @@ bool CSSVariableResolver::resolveTokenRange(CSSParserTokenRange range, ...@@ -153,10 +153,22 @@ bool CSSVariableResolver::resolveTokenRange(CSSParserTokenRange range,
return success; return success;
} }
const CSSValue* CSSVariableResolver::resolveVariableReferences(const StyleResolverState& state, CSSPropertyID id, const CSSVariableReferenceValue& value) const CSSValue* CSSVariableResolver::resolveVariableReferences(const StyleResolverState& state, CSSPropertyID id, const CSSValue& value)
{ {
ASSERT(!isShorthandProperty(id)); ASSERT(!isShorthandProperty(id));
if (value.isPendingSubstitutionValue())
return resolvePendingSubstitutions(state, id, toCSSPendingSubstitutionValue(value));
if (value.isVariableReferenceValue())
return resolveVariableReferences(state, id, toCSSVariableReferenceValue(value));
NOTREACHED();
return nullptr;
}
const CSSValue* CSSVariableResolver::resolveVariableReferences(const StyleResolverState& state, CSSPropertyID id, const CSSVariableReferenceValue& value)
{
CSSVariableResolver resolver(state); CSSVariableResolver resolver(state);
Vector<CSSParserToken> tokens; Vector<CSSParserToken> tokens;
if (!resolver.resolveTokenRange(value.variableDataValue()->tokens(), tokens)) if (!resolver.resolveTokenRange(value.variableDataValue()->tokens(), tokens))
...@@ -167,10 +179,10 @@ const CSSValue* CSSVariableResolver::resolveVariableReferences(const StyleResolv ...@@ -167,10 +179,10 @@ const CSSValue* CSSVariableResolver::resolveVariableReferences(const StyleResolv
return result; return result;
} }
const CSSValue* CSSVariableResolver::resolvePendingSubstitutions(StyleResolverState& state, CSSPropertyID id, const CSSPendingSubstitutionValue& pendingValue) const CSSValue* CSSVariableResolver::resolvePendingSubstitutions(const StyleResolverState& state, CSSPropertyID id, const CSSPendingSubstitutionValue& pendingValue)
{ {
// Longhands from shorthand references follow this path. // Longhands from shorthand references follow this path.
HeapHashMap<CSSPropertyID, Member<const CSSValue>>& propertyCache = state.parsedPropertiesForPendingSubstitution(pendingValue); HeapHashMap<CSSPropertyID, Member<const CSSValue>>& propertyCache = state.parsedPropertiesForPendingSubstitutionCache(pendingValue);
const CSSValue* value = propertyCache.get(id); const CSSValue* value = propertyCache.get(id);
if (!value) { if (!value) {
......
...@@ -25,16 +25,18 @@ class CSSVariableResolver { ...@@ -25,16 +25,18 @@ class CSSVariableResolver {
STACK_ALLOCATED(); STACK_ALLOCATED();
public: public:
static void resolveVariableDefinitions(const StyleResolverState&); static void resolveVariableDefinitions(const StyleResolverState&);
static const CSSValue* resolvePendingSubstitutions(StyleResolverState&, CSSPropertyID, const CSSPendingSubstitutionValue&);
// Shorthand properties are not supported. // Shorthand properties are not supported.
static const CSSValue* resolveVariableReferences(const StyleResolverState&, CSSPropertyID, const CSSVariableReferenceValue&); static const CSSValue* resolveVariableReferences(const StyleResolverState&, CSSPropertyID, const CSSValue&);
DECLARE_TRACE(); DECLARE_TRACE();
private: private:
CSSVariableResolver(const StyleResolverState&); CSSVariableResolver(const StyleResolverState&);
static const CSSValue* resolvePendingSubstitutions(const StyleResolverState&, CSSPropertyID, const CSSPendingSubstitutionValue&);
static const CSSValue* resolveVariableReferences(const StyleResolverState&, CSSPropertyID, const CSSVariableReferenceValue&);
// These return false if we encounter a reference to an invalid variable with no fallback // These return false if we encounter a reference to an invalid variable with no fallback
// Resolves a range which may contain var() references or @apply rules // Resolves a range which may contain var() references or @apply rules
......
...@@ -112,9 +112,7 @@ void StyleBuilder::applyProperty(CSSPropertyID id, StyleResolverState& state, co ...@@ -112,9 +112,7 @@ void StyleBuilder::applyProperty(CSSPropertyID id, StyleResolverState& state, co
if (id != CSSPropertyVariable if (id != CSSPropertyVariable
&& (value.isVariableReferenceValue() || value.isPendingSubstitutionValue())) { && (value.isVariableReferenceValue() || value.isPendingSubstitutionValue())) {
const CSSValue* resolvedValue = value.isVariableReferenceValue() ? const CSSValue* resolvedValue = CSSVariableResolver::resolveVariableReferences(state, id, value);
CSSVariableResolver::resolveVariableReferences(state, id, toCSSVariableReferenceValue(value)) :
CSSVariableResolver::resolvePendingSubstitutions(state, id, toCSSPendingSubstitutionValue(value));
applyProperty(id, state, *resolvedValue); applyProperty(id, state, *resolvedValue);
if (!state.style()->hasVariableReferenceFromNonInheritedProperty() && !CSSPropertyMetadata::isInheritedProperty(id)) if (!state.style()->hasVariableReferenceFromNonInheritedProperty() && !CSSPropertyMetadata::isInheritedProperty(id))
......
...@@ -84,12 +84,12 @@ StylePropertySet* StyleResolverState::customPropertySetForApplyAtRule(const Stri ...@@ -84,12 +84,12 @@ StylePropertySet* StyleResolverState::customPropertySetForApplyAtRule(const Stri
return m_customPropertySetsForApplyAtRule.get(string); return m_customPropertySetsForApplyAtRule.get(string);
} }
HeapHashMap<CSSPropertyID, Member<const CSSValue>>& StyleResolverState::parsedPropertiesForPendingSubstitution(const CSSPendingSubstitutionValue& value) HeapHashMap<CSSPropertyID, Member<const CSSValue>>& StyleResolverState::parsedPropertiesForPendingSubstitutionCache(const CSSPendingSubstitutionValue& value) const
{ {
HeapHashMap<CSSPropertyID, Member<const CSSValue>>* map = m_parsedPropertiesForPendingSubstitution.get(&value); HeapHashMap<CSSPropertyID, Member<const CSSValue>>* map = m_parsedPropertiesForPendingSubstitutionCache.get(&value);
if (!map) { if (!map) {
map = new HeapHashMap<CSSPropertyID, Member<const CSSValue>>; map = new HeapHashMap<CSSPropertyID, Member<const CSSValue>>;
m_parsedPropertiesForPendingSubstitution.set(&value, map); m_parsedPropertiesForPendingSubstitutionCache.set(&value, map);
} }
return *map; return *map;
} }
......
...@@ -161,7 +161,7 @@ public: ...@@ -161,7 +161,7 @@ public:
void setCustomPropertySetForApplyAtRule(const String&, StylePropertySet*); void setCustomPropertySetForApplyAtRule(const String&, StylePropertySet*);
StylePropertySet* customPropertySetForApplyAtRule(const String&); StylePropertySet* customPropertySetForApplyAtRule(const String&);
HeapHashMap<CSSPropertyID, Member<const CSSValue>>& parsedPropertiesForPendingSubstitution(const CSSPendingSubstitutionValue&); HeapHashMap<CSSPropertyID, Member<const CSSValue>>& parsedPropertiesForPendingSubstitutionCache(const CSSPendingSubstitutionValue&) const;
private: private:
ElementResolveContext m_elementContext; ElementResolveContext m_elementContext;
...@@ -190,7 +190,7 @@ private: ...@@ -190,7 +190,7 @@ private:
HeapHashMap<String, Member<StylePropertySet>> m_customPropertySetsForApplyAtRule; HeapHashMap<String, Member<StylePropertySet>> m_customPropertySetsForApplyAtRule;
HeapHashMap<Member<const CSSPendingSubstitutionValue>, Member<HeapHashMap<CSSPropertyID, Member<const CSSValue>>>> m_parsedPropertiesForPendingSubstitution; mutable HeapHashMap<Member<const CSSPendingSubstitutionValue>, Member<HeapHashMap<CSSPropertyID, Member<const CSSValue>>>> m_parsedPropertiesForPendingSubstitutionCache;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment