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

Apply custom property animations

This change enables CSS and Web animations to apply animated
effects on CSS custom properties.

This change involves a restructure of style resolving detailed
in the design doc:
https://docs.google.com/a/chromium.org/document/d/1V27q30H-pQZVbzHCJjdFThBSeuvxkGLdGmb1hLWBAiY

This is a reland of https://codereview.chromium.org/2309963002
with fixes for when the baseComputedStyle optimisation is active.
The revert was due to crbug.com/669790 where behaviour differs
depending on whether asserts are enabled, thus none of the try
bots picked up on the bug.

BUG=644148

Review-Url: https://codereview.chromium.org/2532953008
Cr-Commit-Position: refs/heads/master@{#436235}
parent bf2e505f
<script src="../resources/testharness.js"></script> <script src="../../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script> <script src="../../resources/testharnessreport.js"></script>
<div id="target"></div> <div id="target"></div>
<script> <script>
promise_test(() => { promise_test(() => {
......
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@keyframes test {
from { --x: { background-color: green; }; }
to { --x: { background-color: lime; }; }
}
div {
--x: {
animation: test 10s linear;
background-color: red;
};
@apply --x;
}
#targetA {
animation-delay: -2.5s;
}
#targetB {
animation-delay: -7.5s;
}
</style>
<div id="targetA"></div>
<div id="targetB"></div>
<script>
test(() => {
assert_equals(getComputedStyle(targetA).backgroundColor, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(targetB).backgroundColor, 'rgb(0, 255, 0)');
}, 'CSS Animations on custom properties should be reflected in @apply references');
</script>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#target {
background-color: var(--x);
}
</style>
<div id="target"></div>
<script>
test(() => {
var animation = target.animate({'--x': ['green', 'lime']}, 1000);
animation.currentTime = 250;
assert_equals(getComputedStyle(target).getPropertyValue('--x'), 'green');
assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 128, 0)');
animation.currentTime = 750;
assert_equals(getComputedStyle(target).getPropertyValue('--x'), 'lime');
assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 255, 0)');
}, 'element.animate() animations should apply custom properties');
</script>
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@keyframes test {
from { --x: green; }
to { --x: lime; }
}
#parentA, #parentB {
--x: red;
}
#targetA, #targetB {
background-color: var(--x);
}
#parentA {
animation: test 10s -2s;
}
#parentB {
animation: test 10s -8s;
}
</style>
<div id="parentA">
<div id="targetA"></div>
</div>
<div id="parentB">
<div id="targetB"></div>
</div>
<script>
test(() => {
assert_equals(getComputedStyle(targetA).getPropertyValue('--x'), ' green');
assert_equals(getComputedStyle(targetB).getPropertyValue('--x'), ' lime');
}, 'CSS Animations on custom properties should get inherited');
test(() => {
assert_equals(getComputedStyle(targetA).backgroundColor, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(targetB).backgroundColor, 'rgb(0, 255, 0)');
}, 'CSS Animations on custom properties that get inherited should be reflected in var() references');
</script>
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@keyframes test {
from { --x: green; }
to { --x: lime; }
}
div {
--x: red;
background-color: var(--x);
}
#targetA {
animation: test 10s -2.5s;
}
#targetB {
animation: test 10s -7.5s;
}
</style>
<div id="targetA"></div>
<div id="targetB"></div>
<script>
test(() => {
assert_equals(getComputedStyle(targetA).getPropertyValue('--x'), ' green');
assert_equals(getComputedStyle(targetB).getPropertyValue('--x'), ' lime');
}, 'CSS Animations on custom properties should be applied');
test(() => {
assert_equals(getComputedStyle(targetA).backgroundColor, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(targetB).backgroundColor, 'rgb(0, 255, 0)');
}, 'CSS Animations on custom properties should be reflected in var() references');
</script>
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@keyframes test {
from { --x: green; }
to { --x: lime; }
}
div {
--x: red;
--y: var(--x);
background-color: var(--y);
}
#targetA {
animation: test 10s -2.5s;
}
#targetB {
animation: test 10s -7.5s;
}
</style>
<div id="targetA"></div>
<div id="targetB"></div>
<script>
test(() => {
assert_equals(getComputedStyle(targetA).getPropertyValue('--y'), ' green');
assert_equals(getComputedStyle(targetB).getPropertyValue('--y'), ' lime');
}, 'CSS Animations on var() chained custom properties should be applied');
test(() => {
assert_equals(getComputedStyle(targetA).backgroundColor, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(targetB).backgroundColor, 'rgb(0, 255, 0)');
}, 'CSS Animations on var() chained custom properties should be reflected in var() references');
</script>
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
@keyframes custom-property-animation {
from { --from: blue; --to: cyan; }
to { --from: green; --to: lime; }
}
@keyframes standard-property-animation {
from { background-color: var(--from); }
to { background-color: var(--to); }
}
div {
--from: red;
--to: red;
animation-name: standard-property-animation, custom-property-animation;
animation-duration: 10s;
animation-timing-function: linear;
}
#targetA {
animation-delay: -2.5s;
}
#targetB {
animation-delay: -7.5s;
}
</style>
<div id="targetA"></div>
<div id="targetB"></div>
<script>
test(() => {
assert_equals(getComputedStyle(targetA).getPropertyValue('--from'), ' blue');
assert_equals(getComputedStyle(targetA).getPropertyValue('--to'), ' cyan');
assert_equals(getComputedStyle(targetB).getPropertyValue('--from'), ' green');
assert_equals(getComputedStyle(targetB).getPropertyValue('--to'), ' lime');
}, 'CSS Animations on custom properties should be applied');
test(() => {
assert_equals(getComputedStyle(targetA).backgroundColor, 'rgb(0, 64, 255)');
assert_equals(getComputedStyle(targetB).backgroundColor, 'rgb(0, 223, 0)');
}, 'CSS Animations on custom properties should be reflected in var() references in animation keyframes');
</script>
<script src="../resources/testharness.js"></script> <script src="../../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script> <script src="../../resources/testharnessreport.js"></script>
<body></body> <body></body>
<script> <script>
function createTarget() { function createTarget() {
......
This is a testharness.js-based test.
FAIL Animation tainted values are omitted in CSS property animation-name assert_equals: Tainted value tainted set on --tainted by animation expected "tainted" but got ""
FAIL Chained animation tainted values are omitted in CSS property animation-name assert_equals: Tainted value tainted set on --tainted-first by animation expected "tainted" but got ""
FAIL Inherited animation tainted values are omitted in CSS property animation-name assert_equals: Tainted value tainted set on --tainted by animation expected "tainted" but got ""
FAIL Animation tainted values trigger var fallbacks in CSS property animation-name assert_equals: Tainted value tainted set on --tainted by animation expected "tainted" but got ""
FAIL Animation tainted fallback values are omitted in CSS property animation-name assert_equals: Tainted value tainted set on --tainted by animation expected "tainted" but got ""
Harness: the test ran to completion.
...@@ -100,6 +100,16 @@ bool EffectStack::hasActiveAnimationsOnCompositor( ...@@ -100,6 +100,16 @@ bool EffectStack::hasActiveAnimationsOnCompositor(
return false; return false;
} }
bool EffectStack::affectsProperties(PropertyHandleFilter filter) const {
for (const auto& sampledEffect : m_sampledEffects) {
for (const auto& interpolation : sampledEffect->interpolations()) {
if (filter(interpolation->getProperty()))
return true;
}
}
return false;
}
ActiveInterpolationsMap EffectStack::activeInterpolations( ActiveInterpolationsMap EffectStack::activeInterpolations(
EffectStack* effectStack, EffectStack* effectStack,
const HeapVector<Member<const InertEffect>>* newAnimations, const HeapVector<Member<const InertEffect>>* newAnimations,
......
...@@ -63,6 +63,7 @@ class CORE_EXPORT EffectStack { ...@@ -63,6 +63,7 @@ class CORE_EXPORT EffectStack {
bool hasActiveAnimationsOnCompositor(CSSPropertyID) const; bool hasActiveAnimationsOnCompositor(CSSPropertyID) const;
using PropertyHandleFilter = bool (*)(const PropertyHandle&); using PropertyHandleFilter = bool (*)(const PropertyHandle&);
bool affectsProperties(PropertyHandleFilter) const;
static ActiveInterpolationsMap activeInterpolations( static ActiveInterpolationsMap activeInterpolations(
EffectStack*, EffectStack*,
const HeapVector<Member<const InertEffect>>* newAnimations, const HeapVector<Member<const InertEffect>>* newAnimations,
......
...@@ -231,19 +231,15 @@ bool CSSAnimations::isTransitionAnimationForInspector( ...@@ -231,19 +231,15 @@ bool CSSAnimations::isTransitionAnimationForInspector(
return false; return false;
} }
void CSSAnimations::calculateUpdate(const Element* animatingElement, void CSSAnimations::calculateCompositorAndTransitionUpdate(
Element& element, const Element* animatingElement,
const ComputedStyle& style, Element& element,
ComputedStyle* parentStyle, const ComputedStyle& style,
CSSAnimationUpdate& animationUpdate, ComputedStyle* parentStyle,
StyleResolver* resolver) { CSSAnimationUpdate& animationUpdate) {
calculateCompositorAnimationUpdate(animationUpdate, animatingElement, element, calculateCompositorAnimationUpdate(animationUpdate, animatingElement, element,
style, parentStyle); style, parentStyle);
calculateAnimationUpdate(animationUpdate, animatingElement, element, style,
parentStyle, resolver);
calculateAnimationActiveInterpolations(animationUpdate, animatingElement);
calculateTransitionUpdate(animationUpdate, animatingElement, style); calculateTransitionUpdate(animationUpdate, animatingElement, style);
calculateTransitionActiveInterpolations(animationUpdate, animatingElement);
} }
static const KeyframeEffectModelBase* getKeyframeEffectModelBase( static const KeyframeEffectModelBase* getKeyframeEffectModelBase(
...@@ -324,8 +320,10 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate& update, ...@@ -324,8 +320,10 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate& update,
// If we're in an animation style change, no animations can have started, been // If we're in an animation style change, no animations can have started, been
// cancelled or changed play state. When DCHECK is enabled, we verify this // cancelled or changed play state. When DCHECK is enabled, we verify this
// optimization. // optimization.
if (isAnimationStyleChange) if (isAnimationStyleChange) {
calculateAnimationActiveInterpolations(update, animatingElement);
return; return;
}
#endif #endif
const CSSAnimationData* animationData = style.animations(); const CSSAnimationData* animationData = style.animations();
...@@ -427,6 +425,8 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate& update, ...@@ -427,6 +425,8 @@ void CSSAnimations::calculateAnimationUpdate(CSSAnimationUpdate& update,
*cssAnimations->m_runningAnimations[i]->animation); *cssAnimations->m_runningAnimations[i]->animation);
} }
} }
calculateAnimationActiveInterpolations(update, animatingElement);
} }
void CSSAnimations::snapshotCompositorKeyframes( void CSSAnimations::snapshotCompositorKeyframes(
...@@ -826,6 +826,7 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate& update, ...@@ -826,6 +826,7 @@ void CSSAnimations::calculateTransitionUpdate(CSSAnimationUpdate& update,
} }
} }
} }
calculateTransitionActiveInterpolations(update, animatingElement);
} }
void CSSAnimations::cancel() { void CSSAnimations::cancel() {
...@@ -1094,6 +1095,18 @@ bool CSSAnimations::isAffectedByKeyframesFromScope(const Element& element, ...@@ -1094,6 +1095,18 @@ bool CSSAnimations::isAffectedByKeyframesFromScope(const Element& element,
return toShadowRoot(treeScope.rootNode()).host() == element; return toShadowRoot(treeScope.rootNode()).host() == element;
} }
bool CSSAnimations::isCustomPropertyHandle(const PropertyHandle& property) {
return property.isCSSProperty() &&
property.cssProperty() == CSSPropertyVariable;
}
bool CSSAnimations::isAnimatingCustomProperties(
const ElementAnimations* elementAnimations) {
return elementAnimations &&
elementAnimations->effectStack().affectsProperties(
isCustomPropertyHandle);
}
DEFINE_TRACE(CSSAnimations) { DEFINE_TRACE(CSSAnimations) {
visitor->trace(m_transitions); visitor->trace(m_transitions);
visitor->trace(m_pendingUpdate); visitor->trace(m_pendingUpdate);
......
...@@ -63,12 +63,20 @@ class CSSAnimations final { ...@@ -63,12 +63,20 @@ class CSSAnimations final {
static const StylePropertyShorthand& propertiesForTransitionAll(); static const StylePropertyShorthand& propertiesForTransitionAll();
static bool isAnimationAffectingProperty(CSSPropertyID); static bool isAnimationAffectingProperty(CSSPropertyID);
static bool isAffectedByKeyframesFromScope(const Element&, const TreeScope&); static bool isAffectedByKeyframesFromScope(const Element&, const TreeScope&);
static void calculateUpdate(const Element* animatingElement, static bool isAnimatingCustomProperties(const ElementAnimations*);
Element&, static bool isCustomPropertyHandle(const PropertyHandle&);
const ComputedStyle&, static void calculateAnimationUpdate(CSSAnimationUpdate&,
ComputedStyle* parentStyle, const Element* animatingElement,
CSSAnimationUpdate&, Element&,
StyleResolver*); const ComputedStyle&,
ComputedStyle* parentStyle,
StyleResolver*);
static void calculateCompositorAndTransitionUpdate(
const Element* animatingElement,
Element&,
const ComputedStyle&,
ComputedStyle* parentStyle,
CSSAnimationUpdate&);
static void snapshotCompositorKeyframes(Element&, static void snapshotCompositorKeyframes(Element&,
CSSAnimationUpdate&, CSSAnimationUpdate&,
const ComputedStyle&, const ComputedStyle&,
...@@ -148,12 +156,6 @@ class CSSAnimations final { ...@@ -148,12 +156,6 @@ class CSSAnimations final {
Element&, Element&,
const ComputedStyle&, const ComputedStyle&,
const ComputedStyle* parentStyle); const ComputedStyle* parentStyle);
static void calculateAnimationUpdate(CSSAnimationUpdate&,
const Element* animatingElement,
Element&,
const ComputedStyle&,
ComputedStyle* parentStyle,
StyleResolver*);
static void calculateTransitionUpdate(CSSAnimationUpdate&, static void calculateTransitionUpdate(CSSAnimationUpdate&,
const Element* animatingElement, const Element* animatingElement,
const ComputedStyle&); const ComputedStyle&);
......
...@@ -199,10 +199,33 @@ class CORE_EXPORT StyleResolver final ...@@ -199,10 +199,33 @@ class CORE_EXPORT StyleResolver final
void collectTreeBoundaryCrossingRulesV0CascadeOrder(const Element&, void collectTreeBoundaryCrossingRulesV0CascadeOrder(const Element&,
ElementRuleCollector&); ElementRuleCollector&);
void applyMatchedProperties(StyleResolverState&, const MatchResult&); struct CacheSuccess {
bool applyAnimatedProperties(StyleResolverState&, STACK_ALLOCATED();
const Element* animatingElement); bool isInheritedCacheHit;
void applyCallbackSelectors(StyleResolverState&); bool isNonInheritedCacheHit;
unsigned cacheHash;
Member<const CachedMatchedProperties> cachedMatchedProperties;
CacheSuccess(bool isInheritedCacheHit,
bool isNonInheritedCacheHit,
unsigned cacheHash,
const CachedMatchedProperties* cachedMatchedProperties)
: isInheritedCacheHit(isInheritedCacheHit),
isNonInheritedCacheHit(isNonInheritedCacheHit),
cacheHash(cacheHash),
cachedMatchedProperties(cachedMatchedProperties) {}
bool isFullCacheHit() const {
return isInheritedCacheHit && isNonInheritedCacheHit;
}
bool shouldApplyInheritedOnly() const {
return isNonInheritedCacheHit && !isInheritedCacheHit;
}
void setFailed() {
isInheritedCacheHit = false;
isNonInheritedCacheHit = false;
}
};
// These flags indicate whether an apply pass for a given CSSPropertyPriority // These flags indicate whether an apply pass for a given CSSPropertyPriority
// and isImportant is required. // and isImportant is required.
...@@ -228,6 +251,30 @@ class CORE_EXPORT StyleResolver final ...@@ -228,6 +251,30 @@ class CORE_EXPORT StyleResolver final
UpdateNeedsApplyPass = true, UpdateNeedsApplyPass = true,
}; };
void applyMatchedPropertiesAndCustomPropertyAnimations(
StyleResolverState&,
const MatchResult&,
const Element* animatingElement);
CacheSuccess applyMatchedCache(StyleResolverState&, const MatchResult&);
void applyCustomProperties(StyleResolverState&,
const MatchResult&,
bool applyAnimations,
const CacheSuccess&,
NeedsApplyPass&);
void applyMatchedAnimationProperties(StyleResolverState&,
const MatchResult&,
const CacheSuccess&,
NeedsApplyPass&);
void applyMatchedStandardProperties(StyleResolverState&,
const MatchResult&,
const CacheSuccess&,
NeedsApplyPass&);
void calculateAnimationUpdate(StyleResolverState&,
const Element* animatingElement);
bool applyAnimatedStandardProperties(StyleResolverState&, const Element*);
void applyCallbackSelectors(StyleResolverState&);
template <CSSPropertyPriority priority, ShouldUpdateNeedsApplyPass> template <CSSPropertyPriority priority, ShouldUpdateNeedsApplyPass>
void applyMatchedProperties(StyleResolverState&, void applyMatchedProperties(StyleResolverState&,
const MatchedPropertiesRange&, const MatchedPropertiesRange&,
......
...@@ -41,6 +41,8 @@ StyleResolverState::StyleResolverState( ...@@ -41,6 +41,8 @@ StyleResolverState::StyleResolverState(
// TODO(jchaffraix): We should make m_parentStyle const // TODO(jchaffraix): We should make m_parentStyle const
// (https://crbug.com/468152) // (https://crbug.com/468152)
m_parentStyle(const_cast<ComputedStyle*>(parentStyle)), m_parentStyle(const_cast<ComputedStyle*>(parentStyle)),
m_isAnimationInterpolationMapReady(false),
m_isAnimatingCustomProperties(false),
m_applyPropertyToRegularStyle(true), m_applyPropertyToRegularStyle(true),
m_applyPropertyToVisitedLinkStyle(false), m_applyPropertyToVisitedLinkStyle(false),
m_hasDirAutoAttribute(false), m_hasDirAutoAttribute(false),
......
...@@ -100,6 +100,20 @@ class CORE_EXPORT StyleResolverState { ...@@ -100,6 +100,20 @@ class CORE_EXPORT StyleResolverState {
CSSAnimationUpdate& animationUpdate() { return m_animationUpdate; } CSSAnimationUpdate& animationUpdate() { return m_animationUpdate; }
bool isAnimationInterpolationMapReady() const {
return m_isAnimationInterpolationMapReady;
}
void setIsAnimationInterpolationMapReady() {
m_isAnimationInterpolationMapReady = true;
}
bool isAnimatingCustomProperties() const {
return m_isAnimatingCustomProperties;
}
void setIsAnimatingCustomProperties(bool value) {
m_isAnimatingCustomProperties = value;
}
void setParentStyle(PassRefPtr<ComputedStyle> parentStyle) { void setParentStyle(PassRefPtr<ComputedStyle> parentStyle) {
m_parentStyle = parentStyle; m_parentStyle = parentStyle;
} }
...@@ -201,6 +215,8 @@ class CORE_EXPORT StyleResolverState { ...@@ -201,6 +215,8 @@ class CORE_EXPORT StyleResolverState {
RefPtr<ComputedStyle> m_parentStyle; RefPtr<ComputedStyle> m_parentStyle;
CSSAnimationUpdate m_animationUpdate; CSSAnimationUpdate m_animationUpdate;
bool m_isAnimationInterpolationMapReady;
bool m_isAnimatingCustomProperties;
bool m_applyPropertyToRegularStyle; bool m_applyPropertyToRegularStyle;
bool m_applyPropertyToVisitedLinkStyle; bool m_applyPropertyToVisitedLinkStyle;
......
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