Title: [260671] trunk
Revision
260671
Author
grao...@webkit.org
Date
2020-04-24 14:42:50 -0700 (Fri, 24 Apr 2020)

Log Message

[Web Animations] Ensure calling Web Animations APIs override future CSS Animations style properties
https://bugs.webkit.org/show_bug.cgi?id=210988

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

Mark all WPT tests related to Web Animations APIs overrides for CSS Animations as PASS, save for one
failing assertion which is caused by AnimationTimeline::updateCSSAnimations() not updating the animation
when the underlying @keyframes rule changed. This is due to Animation::animationsMatch() not checking on
the actual keyframes, tracked by webkit.org/b/210989.

* web-platform-tests/css/css-animations/AnimationEffect-updateTiming.tentative-expected.txt:
* web-platform-tests/css/css-animations/CSSAnimation-effect.tentative-expected.txt:
* web-platform-tests/css/css-animations/CSSAnimation-pausing.tentative-expected.txt:
* web-platform-tests/css/css-animations/KeyframeEffect-setKeyframes.tentative-expected.txt:

Source/WebCore:

The CSS Animations Level 2 spec specifies how the Web Animations APIs and the CSS Animations style
properties should interact in https://drafts.csswg.org/css-animations-2/#animations. This patch
implements the specified behavior and this is reflected by progress on the relevant WPT tests.

The gist of this change is that once a Web Animations API is called on an animation created using
CSS Animations, any changes made to related CSS Animations style properties on the target element
will be ignored so that the overrides applied via the Web Animations API remain in effect.

For instance, calling pause() or play() in a way that changes the playback state of the CSS Animation
will mean that future changes to the CSS animation-play-state property are ignored.

To do this we make more IDL properties and methods use dedicated methods to distinguish between the
bindings entry-point and internal usage of the same methods to integrate the behavior only when the
API itself is being used.

* animation/AnimationEffect.cpp:
(WebCore::AnimationEffect::getBindingsTiming const): Ensure we flush styles when animation.effect.getTiming()
is called.
(WebCore::AnimationEffect::getBindingsComputedTiming const): Ensure we flush styles when
animation.effect.getComputedTiming() is called.
(WebCore::AnimationEffect::bindingsUpdateTiming): Notify the associated CSSAnimation object, if any, when
animation.effect.updateTiming() is called such that the CSSAnimation may apply the relevant overrides.
* animation/AnimationEffect.h:
* animation/AnimationEffect.idl:
* animation/CSSAnimation.cpp:
(WebCore::CSSAnimation::syncPropertiesWithBackingAnimation): Only apply new values of CSS Animations style
properties if there are no overrides for them resulting from calling related Web Animations APIs.
(WebCore::CSSAnimation::bindingsPlay): Mark animation-play-state as overridden if play() is called.
(WebCore::CSSAnimation::bindingsPause): Mark animation-play-state as overridden if pause() is called.
(WebCore::CSSAnimation::setBindingsEffect): Mark all animation style properties, except for animation-name
and animation-play-state as overridden if animation.effect is set.
(WebCore::CSSAnimation::setBindingsStartTime): Mark animation-play-state as overridden if animation.startTime
is set.
(WebCore::CSSAnimation::bindingsReverse): Mark animation-play-state as overridden if reverse() is called.
(WebCore::CSSAnimation::effectTimingWasUpdatedUsingBindings): Mark each CSS property associated with a key
found on the timing object passed to animation.effect.updateTiming() as overridden.
(WebCore::CSSAnimation::effectKeyframesWereSetUsingBindings): Mark animation-timing-function as overridden
if animation.effect.setKeyframes() is called.
* animation/CSSAnimation.h:
* animation/DeclarativeAnimation.cpp:
(WebCore::DeclarativeAnimation::bindingsStartTime const):
(WebCore::DeclarativeAnimation::setBindingsStartTime):
(WebCore::DeclarativeAnimation::startTime const): Deleted.
(WebCore::DeclarativeAnimation::setStartTime): Deleted.
* animation/DeclarativeAnimation.h:
* animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::getBindingsKeyframes): Ensure we flush styles when animation.effect.getKeyframes()
is called.
(WebCore::KeyframeEffect::getKeyframes): Only use the CSS-originated animation path if we don't have JS-originated
keyframes.
(WebCore::KeyframeEffect::setBindingsKeyframes): Notify the associated CSSAnimation object, if any, when
animation.effect.setKeyframes() is called such that the CSSAnimation may apply the relevant overrides.
(WebCore::KeyframeEffect::processKeyframes): Correctly return early if part of the processing yields an exception.
* animation/KeyframeEffect.h:
* animation/KeyframeEffect.idl:
* animation/WebAnimation.cpp:
(WebCore::WebAnimation::setBindingsEffect):
(WebCore::WebAnimation::setBindingsStartTime):
(WebCore::WebAnimation::bindingsReverse):
* animation/WebAnimation.h:
(WebCore::WebAnimation::bindingsEffect const):
(WebCore::WebAnimation::bindingsStartTime const):
* animation/WebAnimation.idl:

Modified Paths

Diff

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (260670 => 260671)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2020-04-24 21:42:50 UTC (rev 260671)
@@ -1,3 +1,20 @@
+2020-04-24  Antoine Quint  <grao...@apple.com>
+
+        [Web Animations] Ensure calling Web Animations APIs override future CSS Animations style properties
+        https://bugs.webkit.org/show_bug.cgi?id=210988
+
+        Reviewed by Dean Jackson.
+
+        Mark all WPT tests related to Web Animations APIs overrides for CSS Animations as PASS, save for one
+        failing assertion which is caused by AnimationTimeline::updateCSSAnimations() not updating the animation
+        when the underlying @keyframes rule changed. This is due to Animation::animationsMatch() not checking on
+        the actual keyframes, tracked by webkit.org/b/210989.
+
+        * web-platform-tests/css/css-animations/AnimationEffect-updateTiming.tentative-expected.txt:
+        * web-platform-tests/css/css-animations/CSSAnimation-effect.tentative-expected.txt:
+        * web-platform-tests/css/css-animations/CSSAnimation-pausing.tentative-expected.txt:
+        * web-platform-tests/css/css-animations/KeyframeEffect-setKeyframes.tentative-expected.txt:
+
 2020-04-24  Alexey Shvayka  <shvaikal...@gmail.com>
 
         Re-sync wpt/WebIDL/ecmascript-binding and wpt/custom-elements/htmlconstructor from upstream

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/AnimationEffect-updateTiming.tentative-expected.txt (260670 => 260671)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/AnimationEffect-updateTiming.tentative-expected.txt	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/AnimationEffect-updateTiming.tentative-expected.txt	2020-04-24 21:42:50 UTC (rev 260671)
@@ -1,7 +1,7 @@
 
-FAIL AnimationEffect.updateTiming({ duration }) causes changes to the animation-duration to be ignored assert_equals: Delay should be the value set by style expected 6000 but got 0
-FAIL AnimationEffect.updateTiming({ iterations, direction }) causes changes to the animation-iteration-count and animation-direction to be ignored assert_equals: Delay should be the value set by style expected 6000 but got 0
-FAIL AnimationEffect.updateTiming({ delay, fill }) causes changes to the animation-delay and animation-fill-mode to be ignored assert_equals: Iterations should be the value set by style expected 6 but got 1
-FAIL AnimationEffect.updateTiming() does override to changes from animation-* properties if there is an error assert_equals: Duration should be the value set by style expected 4000 but got 100000
+PASS AnimationEffect.updateTiming({ duration }) causes changes to the animation-duration to be ignored 
+PASS AnimationEffect.updateTiming({ iterations, direction }) causes changes to the animation-iteration-count and animation-direction to be ignored 
+PASS AnimationEffect.updateTiming({ delay, fill }) causes changes to the animation-delay and animation-fill-mode to be ignored 
+PASS AnimationEffect.updateTiming() does override to changes from animation-* properties if there is an error 
 PASS AnimationEffect properties that do not map to animation-* properties should not be changed when animation-* style is updated 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/CSSAnimation-effect.tentative-expected.txt (260670 => 260671)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/CSSAnimation-effect.tentative-expected.txt	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/CSSAnimation-effect.tentative-expected.txt	2020-04-24 21:42:50 UTC (rev 260671)
@@ -5,5 +5,5 @@
 PASS A play-pending animation's effect whose effect is replaced still exits the pending state 
 PASS CSS animation events are dispatched at the original element even after setting an effect with a different target element 
 PASS After replacing a finished animation's effect with a longer one it fires an animationstart event 
-FAIL Replacing the effect of a CSSAnimation causes subsequent changes to corresponding animation-* properties to be ignored undefined is not an object (evaluating 'animation.effect.getKeyframes()[0].left')
+PASS Replacing the effect of a CSSAnimation causes subsequent changes to corresponding animation-* properties to be ignored 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/CSSAnimation-pausing.tentative-expected.txt (260670 => 260671)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/CSSAnimation-pausing.tentative-expected.txt	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/CSSAnimation-pausing.tentative-expected.txt	2020-04-24 21:42:50 UTC (rev 260671)
@@ -1,11 +1,11 @@
 
-FAIL play() overrides animation-play-state assert_equals: Should still be running even after flipping the animation-play-state expected "running" but got "paused"
+PASS play() overrides animation-play-state 
 PASS play() does NOT override the animation-play-state if there was an error 
 PASS pause() overrides animation-play-state 
-FAIL reverse() overrides animation-play-state when it starts playing the animation assert_equals: Should still be running even after flipping the animation-play-state expected "running" but got "paused"
+PASS reverse() overrides animation-play-state when it starts playing the animation 
 PASS reverse() does NOT override animation-play-state if the animation is already running 
-FAIL Setting the startTime to null overrides animation-play-state if the animation is already running assert_equals: Should still be paused even after flipping the animation-play-state expected "paused" but got "running"
-FAIL Setting the startTime to non-null overrides animation-play-state if the animation is paused assert_equals: Should still be running even after flipping the animation-play-state expected "running" but got "paused"
+PASS Setting the startTime to null overrides animation-play-state if the animation is already running 
+PASS Setting the startTime to non-null overrides animation-play-state if the animation is paused 
 PASS Setting the startTime to non-null does NOT override the animation-play-state if the animation is already running 
 PASS Setting the current time completes a pending pause 
 

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-setKeyframes.tentative-expected.txt (260670 => 260671)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-setKeyframes.tentative-expected.txt	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-setKeyframes.tentative-expected.txt	2020-04-24 21:42:50 UTC (rev 260671)
@@ -1,5 +1,5 @@
 
-FAIL KeyframeEffect.setKeyframes() causes subsequent changes to @keyframes rules to be ignored undefined is not an object (evaluating 'Object.keys(actual)')
-FAIL KeyframeEffect.setKeyframes() causes subsequent changes to animation-timing-function to be ignored undefined is not an object (evaluating 'animation.effect.getKeyframes()[0].easing')
-FAIL KeyframeEffect.setKeyframes() should NOT cause subsequent changes to @keyframes rules to be ignored if it threw undefined is not an object (evaluating 'Object.keys(actual)')
+PASS KeyframeEffect.setKeyframes() causes subsequent changes to @keyframes rules to be ignored 
+PASS KeyframeEffect.setKeyframes() causes subsequent changes to animation-timing-function to be ignored 
+FAIL KeyframeEffect.setKeyframes() should NOT cause subsequent changes to @keyframes rules to be ignored if it threw assert_equals: value for 'left' on Keyframes reflect the value set via style should match expected "300px" but got "100px"
 

Modified: trunk/Source/WebCore/ChangeLog (260670 => 260671)


--- trunk/Source/WebCore/ChangeLog	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/ChangeLog	2020-04-24 21:42:50 UTC (rev 260671)
@@ -1,3 +1,74 @@
+2020-04-24  Antoine Quint  <grao...@apple.com>
+
+        [Web Animations] Ensure calling Web Animations APIs override future CSS Animations style properties
+        https://bugs.webkit.org/show_bug.cgi?id=210988
+
+        Reviewed by Dean Jackson.
+
+        The CSS Animations Level 2 spec specifies how the Web Animations APIs and the CSS Animations style
+        properties should interact in https://drafts.csswg.org/css-animations-2/#animations. This patch
+        implements the specified behavior and this is reflected by progress on the relevant WPT tests.
+
+        The gist of this change is that once a Web Animations API is called on an animation created using
+        CSS Animations, any changes made to related CSS Animations style properties on the target element
+        will be ignored so that the overrides applied via the Web Animations API remain in effect.
+
+        For instance, calling pause() or play() in a way that changes the playback state of the CSS Animation
+        will mean that future changes to the CSS animation-play-state property are ignored.
+
+        To do this we make more IDL properties and methods use dedicated methods to distinguish between the
+        bindings entry-point and internal usage of the same methods to integrate the behavior only when the
+        API itself is being used.
+
+        * animation/AnimationEffect.cpp:
+        (WebCore::AnimationEffect::getBindingsTiming const): Ensure we flush styles when animation.effect.getTiming()
+        is called.
+        (WebCore::AnimationEffect::getBindingsComputedTiming const): Ensure we flush styles when
+        animation.effect.getComputedTiming() is called.
+        (WebCore::AnimationEffect::bindingsUpdateTiming): Notify the associated CSSAnimation object, if any, when
+        animation.effect.updateTiming() is called such that the CSSAnimation may apply the relevant overrides.
+        * animation/AnimationEffect.h:
+        * animation/AnimationEffect.idl:
+        * animation/CSSAnimation.cpp:
+        (WebCore::CSSAnimation::syncPropertiesWithBackingAnimation): Only apply new values of CSS Animations style
+        properties if there are no overrides for them resulting from calling related Web Animations APIs.
+        (WebCore::CSSAnimation::bindingsPlay): Mark animation-play-state as overridden if play() is called.
+        (WebCore::CSSAnimation::bindingsPause): Mark animation-play-state as overridden if pause() is called.
+        (WebCore::CSSAnimation::setBindingsEffect): Mark all animation style properties, except for animation-name
+        and animation-play-state as overridden if animation.effect is set.
+        (WebCore::CSSAnimation::setBindingsStartTime): Mark animation-play-state as overridden if animation.startTime
+        is set.
+        (WebCore::CSSAnimation::bindingsReverse): Mark animation-play-state as overridden if reverse() is called.
+        (WebCore::CSSAnimation::effectTimingWasUpdatedUsingBindings): Mark each CSS property associated with a key
+        found on the timing object passed to animation.effect.updateTiming() as overridden.
+        (WebCore::CSSAnimation::effectKeyframesWereSetUsingBindings): Mark animation-timing-function as overridden
+        if animation.effect.setKeyframes() is called.
+        * animation/CSSAnimation.h:
+        * animation/DeclarativeAnimation.cpp:
+        (WebCore::DeclarativeAnimation::bindingsStartTime const):
+        (WebCore::DeclarativeAnimation::setBindingsStartTime):
+        (WebCore::DeclarativeAnimation::startTime const): Deleted.
+        (WebCore::DeclarativeAnimation::setStartTime): Deleted.
+        * animation/DeclarativeAnimation.h:
+        * animation/KeyframeEffect.cpp:
+        (WebCore::KeyframeEffect::getBindingsKeyframes): Ensure we flush styles when animation.effect.getKeyframes()
+        is called.
+        (WebCore::KeyframeEffect::getKeyframes): Only use the CSS-originated animation path if we don't have JS-originated
+        keyframes.
+        (WebCore::KeyframeEffect::setBindingsKeyframes): Notify the associated CSSAnimation object, if any, when
+        animation.effect.setKeyframes() is called such that the CSSAnimation may apply the relevant overrides.
+        (WebCore::KeyframeEffect::processKeyframes): Correctly return early if part of the processing yields an exception.
+        * animation/KeyframeEffect.h:
+        * animation/KeyframeEffect.idl:
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::setBindingsEffect):
+        (WebCore::WebAnimation::setBindingsStartTime):
+        (WebCore::WebAnimation::bindingsReverse):
+        * animation/WebAnimation.h:
+        (WebCore::WebAnimation::bindingsEffect const):
+        (WebCore::WebAnimation::bindingsStartTime const):
+        * animation/WebAnimation.idl:
+
 2020-04-24  Chris Dumez  <cdu...@apple.com>
 
         ASSERTION FAILED: m_wrapper under HTMLMediaElement::setIsPlayingToWirelessTarget

Modified: trunk/Source/WebCore/animation/AnimationEffect.cpp (260670 => 260671)


--- trunk/Source/WebCore/animation/AnimationEffect.cpp	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/AnimationEffect.cpp	2020-04-24 21:42:50 UTC (rev 260671)
@@ -26,8 +26,10 @@
 #include "config.h"
 #include "AnimationEffect.h"
 
+#include "CSSAnimation.h"
 #include "FillMode.h"
 #include "JSComputedEffectTiming.h"
+#include "WebAnimation.h"
 #include "WebAnimationUtilities.h"
 
 namespace WebCore {
@@ -41,6 +43,13 @@
 {
 }
 
+EffectTiming AnimationEffect::getBindingsTiming() const
+{
+    if (is<DeclarativeAnimation>(animation()))
+        downcast<DeclarativeAnimation>(*animation()).flushPendingStyleChanges();
+    return getTiming();
+}
+
 EffectTiming AnimationEffect::getTiming() const
 {
     EffectTiming timing;
@@ -151,6 +160,13 @@
     return { localTime, activeTime, m_endTime, m_activeDuration, phase };
 }
 
+ComputedEffectTiming AnimationEffect::getBindingsComputedTiming() const
+{
+    if (is<DeclarativeAnimation>(animation()))
+        downcast<DeclarativeAnimation>(*animation()).flushPendingStyleChanges();
+    return getComputedTiming();
+}
+
 ComputedEffectTiming AnimationEffect::getComputedTiming() const
 {
     // The Web Animations spec introduces a number of animation effect time-related definitions that refer
@@ -336,6 +352,14 @@
     return computedTiming;
 }
 
+ExceptionOr<void> AnimationEffect::bindingsUpdateTiming(Optional<OptionalEffectTiming> timing)
+{
+    auto retVal = updateTiming(timing);
+    if (!retVal.hasException() && timing && is<CSSAnimation>(animation()))
+        downcast<CSSAnimation>(*animation()).effectTimingWasUpdatedUsingBindings(*timing);
+    return retVal;
+}
+
 ExceptionOr<void> AnimationEffect::updateTiming(Optional<OptionalEffectTiming> timing)
 {
     // 6.5.4. Updating the timing of an AnimationEffect

Modified: trunk/Source/WebCore/animation/AnimationEffect.h (260670 => 260671)


--- trunk/Source/WebCore/animation/AnimationEffect.h	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/AnimationEffect.h	2020-04-24 21:42:50 UTC (rev 260671)
@@ -53,9 +53,12 @@
 
     virtual bool isKeyframeEffect() const { return false; }
 
+    EffectTiming getBindingsTiming() const;
     EffectTiming getTiming() const;
     BasicEffectTiming getBasicTiming() const;
+    ComputedEffectTiming getBindingsComputedTiming() const;
     ComputedEffectTiming getComputedTiming() const;
+    ExceptionOr<void> bindingsUpdateTiming(Optional<OptionalEffectTiming>);
     ExceptionOr<void> updateTiming(Optional<OptionalEffectTiming>);
 
     virtual void apply(RenderStyle&) = 0;

Modified: trunk/Source/WebCore/animation/AnimationEffect.idl (260670 => 260671)


--- trunk/Source/WebCore/animation/AnimationEffect.idl	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/AnimationEffect.idl	2020-04-24 21:42:50 UTC (rev 260671)
@@ -28,7 +28,7 @@
     Exposed=Window,
     CustomToJSObject
 ] interface AnimationEffect {
-    EffectTiming getTiming();
-    ComputedEffectTiming getComputedTiming();
-    [MayThrowException] void updateTiming(optional OptionalEffectTiming timing);
+    [ImplementedAs=getBindingsTiming] EffectTiming getTiming();
+    [ImplementedAs=getBindingsComputedTiming] ComputedEffectTiming getComputedTiming();
+    [MayThrowException, ImplementedAs=bindingsUpdateTiming] void updateTiming(optional OptionalEffectTiming timing);
 };

Modified: trunk/Source/WebCore/animation/CSSAnimation.cpp (260670 => 260671)


--- trunk/Source/WebCore/animation/CSSAnimation.cpp	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/CSSAnimation.cpp	2020-04-24 21:42:50 UTC (rev 260671)
@@ -67,50 +67,61 @@
 
     auto previousTiming = animationEffect->getComputedTiming();
 
-    switch (animation.fillMode()) {
-    case AnimationFillMode::None:
-        animationEffect->setFill(FillMode::None);
-        break;
-    case AnimationFillMode::Backwards:
-        animationEffect->setFill(FillMode::Backwards);
-        break;
-    case AnimationFillMode::Forwards:
-        animationEffect->setFill(FillMode::Forwards);
-        break;
-    case AnimationFillMode::Both:
-        animationEffect->setFill(FillMode::Both);
-        break;
+    if (!m_overriddenProperties.contains(Property::FillMode)) {
+        switch (animation.fillMode()) {
+        case AnimationFillMode::None:
+            animationEffect->setFill(FillMode::None);
+            break;
+        case AnimationFillMode::Backwards:
+            animationEffect->setFill(FillMode::Backwards);
+            break;
+        case AnimationFillMode::Forwards:
+            animationEffect->setFill(FillMode::Forwards);
+            break;
+        case AnimationFillMode::Both:
+            animationEffect->setFill(FillMode::Both);
+            break;
+        }
     }
 
-    switch (animation.direction()) {
-    case Animation::AnimationDirectionNormal:
-        animationEffect->setDirection(PlaybackDirection::Normal);
-        break;
-    case Animation::AnimationDirectionAlternate:
-        animationEffect->setDirection(PlaybackDirection::Alternate);
-        break;
-    case Animation::AnimationDirectionReverse:
-        animationEffect->setDirection(PlaybackDirection::Reverse);
-        break;
-    case Animation::AnimationDirectionAlternateReverse:
-        animationEffect->setDirection(PlaybackDirection::AlternateReverse);
-        break;
+    if (!m_overriddenProperties.contains(Property::Direction)) {
+        switch (animation.direction()) {
+        case Animation::AnimationDirectionNormal:
+            animationEffect->setDirection(PlaybackDirection::Normal);
+            break;
+        case Animation::AnimationDirectionAlternate:
+            animationEffect->setDirection(PlaybackDirection::Alternate);
+            break;
+        case Animation::AnimationDirectionReverse:
+            animationEffect->setDirection(PlaybackDirection::Reverse);
+            break;
+        case Animation::AnimationDirectionAlternateReverse:
+            animationEffect->setDirection(PlaybackDirection::AlternateReverse);
+            break;
+        }
     }
 
-    auto iterationCount = animation.iterationCount();
-    animationEffect->setIterations(iterationCount == Animation::IterationCountInfinite ? std::numeric_limits<double>::infinity() : iterationCount);
+    if (!m_overriddenProperties.contains(Property::IterationCount)) {
+        auto iterationCount = animation.iterationCount();
+        animationEffect->setIterations(iterationCount == Animation::IterationCountInfinite ? std::numeric_limits<double>::infinity() : iterationCount);
+    }
 
-    animationEffect->setDelay(Seconds(animation.delay()));
-    animationEffect->setIterationDuration(Seconds(animation.duration()));
+    if (!m_overriddenProperties.contains(Property::Delay))
+        animationEffect->setDelay(Seconds(animation.delay()));
+
+    if (!m_overriddenProperties.contains(Property::Duration))
+        animationEffect->setIterationDuration(Seconds(animation.duration()));
+
     animationEffect->updateStaticTimingProperties();
     effectTimingDidChange(previousTiming);
 
     // Synchronize the play state
-    if (animation.playState() == AnimationPlayState::Playing && playState() == WebAnimation::PlayState::Paused) {
-        if (!m_stickyPaused)
+    if (!m_overriddenProperties.contains(Property::PlayState)) {
+        if (animation.playState() == AnimationPlayState::Playing && playState() == WebAnimation::PlayState::Paused)
             play();
-    } else if (animation.playState() == AnimationPlayState::Paused && playState() == WebAnimation::PlayState::Running)
-        pause();
+        else if (animation.playState() == AnimationPlayState::Paused && playState() == WebAnimation::PlayState::Running)
+            pause();
+    }
 
     unsuspendEffectInvalidation();
 }
@@ -117,16 +128,121 @@
 
 ExceptionOr<void> CSSAnimation::bindingsPlay()
 {
-    m_stickyPaused = false;
-    return DeclarativeAnimation::bindingsPlay();
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After a successful call to play() or pause() on a CSSAnimation, any subsequent change to the animation-play-state will
+    // no longer cause the CSSAnimation to be played or paused.
+
+    auto retVal = DeclarativeAnimation::bindingsPlay();
+    if (!retVal.hasException())
+        m_overriddenProperties.add(Property::PlayState);
+    return retVal;
 }
 
 ExceptionOr<void> CSSAnimation::bindingsPause()
 {
-    m_stickyPaused = true;
-    return DeclarativeAnimation::bindingsPause();
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After a successful call to play() or pause() on a CSSAnimation, any subsequent change to the animation-play-state will
+    // no longer cause the CSSAnimation to be played or paused.
+
+    auto retVal = DeclarativeAnimation::bindingsPause();
+    if (!retVal.hasException())
+        m_overriddenProperties.add(Property::PlayState);
+    return retVal;
 }
 
+void CSSAnimation::setBindingsEffect(RefPtr<AnimationEffect>&& newEffect)
+{
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After successfully setting the effect of a CSSAnimation to null or some AnimationEffect other than the original KeyframeEffect,
+    // all subsequent changes to animation properties other than animation-name or animation-play-state will not be reflected in that
+    // animation. Similarly, any change to matching @keyframes rules will not be reflected in that animation. However, if the last
+    // matching @keyframes rule is removed the animation must still be canceled.
+
+    auto* previousEffect = effect();
+    DeclarativeAnimation::setBindingsEffect(WTFMove(newEffect));
+    if (effect() != previousEffect) {
+        m_overriddenProperties.add(Property::Duration);
+        m_overriddenProperties.add(Property::TimingFunction);
+        m_overriddenProperties.add(Property::IterationCount);
+        m_overriddenProperties.add(Property::Direction);
+        m_overriddenProperties.add(Property::Delay);
+        m_overriddenProperties.add(Property::FillMode);
+    }
+}
+
+void CSSAnimation::setBindingsStartTime(Optional<double> startTime)
+{
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After a successful call to reverse() on a CSSAnimation or after successfully setting the startTime on a CSSAnimation,
+    // if, as a result of that call the play state of the CSSAnimation changes to or from the paused play state, any subsequent
+    // change to the animation-play-state will no longer cause the CSSAnimation to be played or paused.
+
+    auto previousPlayState = playState();
+    DeclarativeAnimation::setBindingsStartTime(startTime);
+    auto currentPlayState = playState();
+    if (currentPlayState != previousPlayState && (currentPlayState == PlayState::Paused || previousPlayState == PlayState::Paused))
+        m_overriddenProperties.add(Property::PlayState);
+}
+
+ExceptionOr<void> CSSAnimation::bindingsReverse()
+{
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After a successful call to reverse() on a CSSAnimation or after successfully setting the startTime on a CSSAnimation,
+    // if, as a result of that call the play state of the CSSAnimation changes to or from the paused play state, any subsequent
+    // change to the animation-play-state will no longer cause the CSSAnimation to be played or paused.
+
+    auto previousPlayState = playState();
+    auto retVal = DeclarativeAnimation::bindingsReverse();
+    if (!retVal.hasException()) {
+        auto currentPlayState = playState();
+        if (currentPlayState != previousPlayState && (currentPlayState == PlayState::Paused || previousPlayState == PlayState::Paused))
+            m_overriddenProperties.add(Property::PlayState);
+    }
+    return retVal;
+}
+
+void CSSAnimation::effectTimingWasUpdatedUsingBindings(OptionalEffectTiming timing)
+{
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After a successful call to updateTiming() on the KeyframeEffect associated with a CSSAnimation, for each property
+    // included in the timing parameter, any subsequent change to a corresponding animation property will not be reflected
+    // in that animation.
+
+    if (timing.duration)
+        m_overriddenProperties.add(Property::Duration);
+
+    if (timing.iterations)
+        m_overriddenProperties.add(Property::IterationCount);
+
+    if (timing.delay)
+        m_overriddenProperties.add(Property::Delay);
+
+    if (!timing.easing.isNull())
+        m_overriddenProperties.add(Property::TimingFunction);
+
+    if (timing.fill)
+        m_overriddenProperties.add(Property::FillMode);
+
+    if (timing.direction)
+        m_overriddenProperties.add(Property::Direction);
+}
+
+void CSSAnimation::effectKeyframesWereSetUsingBindings()
+{
+    // https://drafts.csswg.org/css-animations-2/#animations
+
+    // After a successful call to setKeyframes() on the KeyframeEffect associated with a CSSAnimation, any subsequent change to
+    // matching @keyframes rules or the resolved value of the animation-timing-function property for the target element will not
+    // be reflected in that animation.
+    m_overriddenProperties.add(Property::TimingFunction);
+}
+
 Ref<AnimationEventBase> CSSAnimation::createEvent(const AtomString& eventType, double elapsedTime, const String& pseudoId, Optional<Seconds> timelineTime)
 {
     return AnimationEvent::create(eventType, m_animationName, elapsedTime, pseudoId, timelineTime, this);

Modified: trunk/Source/WebCore/animation/CSSAnimation.h (260670 => 260671)


--- trunk/Source/WebCore/animation/CSSAnimation.h	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/CSSAnimation.h	2020-04-24 21:42:50 UTC (rev 260671)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "DeclarativeAnimation.h"
+#include <wtf/OptionSet.h>
 #include <wtf/Ref.h>
 
 namespace WebCore {
@@ -43,8 +44,8 @@
     bool isCSSAnimation() const override { return true; }
     const String& animationName() const { return m_animationName; }
 
-    ExceptionOr<void> bindingsPlay() final;
-    ExceptionOr<void> bindingsPause() final;
+    void effectTimingWasUpdatedUsingBindings(OptionalEffectTiming);
+    void effectKeyframesWereSetUsingBindings();
 
 private:
     CSSAnimation(Element&, const Animation&);
@@ -52,8 +53,25 @@
     void syncPropertiesWithBackingAnimation() final;
     Ref<AnimationEventBase> createEvent(const AtomString& eventType, double elapsedTime, const String& pseudoId, Optional<Seconds> timelineTime) final;
 
+    ExceptionOr<void> bindingsPlay() final;
+    ExceptionOr<void> bindingsPause() final;
+    void setBindingsEffect(RefPtr<AnimationEffect>&&) final;
+    void setBindingsStartTime(Optional<double>) final;
+    ExceptionOr<void> bindingsReverse() final;
+
+    enum class Property : uint8_t {
+        Name = 1 << 0,
+        Duration = 1 << 1,
+        TimingFunction = 1 << 2,
+        IterationCount = 1 << 3,
+        Direction = 1 << 4,
+        PlayState = 1 << 5,
+        Delay = 1 << 6,
+        FillMode = 1 << 7
+    };
+
     String m_animationName;
-    bool m_stickyPaused { false };
+    OptionSet<Property> m_overriddenProperties;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/animation/DeclarativeAnimation.cpp (260670 => 260671)


--- trunk/Source/WebCore/animation/DeclarativeAnimation.cpp	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/DeclarativeAnimation.cpp	2020-04-24 21:42:50 UTC (rev 260671)
@@ -129,13 +129,13 @@
 {
 }
 
-Optional<double> DeclarativeAnimation::startTime() const
+Optional<double> DeclarativeAnimation::bindingsStartTime() const
 {
     flushPendingStyleChanges();
     return WebAnimation::startTime();
 }
 
-void DeclarativeAnimation::setStartTime(Optional<double> startTime)
+void DeclarativeAnimation::setBindingsStartTime(Optional<double> startTime)
 {
     flushPendingStyleChanges();
     return WebAnimation::setStartTime(startTime);

Modified: trunk/Source/WebCore/animation/DeclarativeAnimation.h (260670 => 260671)


--- trunk/Source/WebCore/animation/DeclarativeAnimation.h	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/DeclarativeAnimation.h	2020-04-24 21:42:50 UTC (rev 260671)
@@ -50,8 +50,8 @@
     void setBackingAnimation(const Animation&);
     void cancelFromStyle();
 
-    Optional<double> startTime() const final;
-    void setStartTime(Optional<double>) final;
+    Optional<double> bindingsStartTime() const final;
+    void setBindingsStartTime(Optional<double>) override;
     Optional<double> bindingsCurrentTime() const final;
     ExceptionOr<void> setBindingsCurrentTime(Optional<double>) final;
     WebAnimation::PlayState bindingsPlayState() const final;
@@ -69,6 +69,8 @@
 
     bool canHaveGlobalPosition() final;
 
+    void flushPendingStyleChanges() const;
+
 protected:
     DeclarativeAnimation(Element&, const Animation&);
 
@@ -81,7 +83,6 @@
 
 private:
     void disassociateFromOwningElement();
-    void flushPendingStyleChanges() const;
     AnimationEffectPhase phaseWithoutEffect() const;
     void enqueueDOMEvent(const AtomString&, Seconds);
 

Modified: trunk/Source/WebCore/animation/KeyframeEffect.cpp (260670 => 260671)


--- trunk/Source/WebCore/animation/KeyframeEffect.cpp	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/KeyframeEffect.cpp	2020-04-24 21:42:50 UTC (rev 260671)
@@ -574,6 +574,13 @@
     setBlendingKeyframes(keyframeList);
 }
 
+Vector<Strong<JSObject>> KeyframeEffect::getBindingsKeyframes(JSGlobalObject& lexicalGlobalObject)
+{
+    if (is<DeclarativeAnimation>(animation()))
+        downcast<DeclarativeAnimation>(*animation()).flushPendingStyleChanges();
+    return getKeyframes(lexicalGlobalObject);
+}
+
 Vector<Strong<JSObject>> KeyframeEffect::getKeyframes(JSGlobalObject& lexicalGlobalObject)
 {
     // https://drafts.csswg.org/web-animations-1/#dom-keyframeeffectreadonly-getkeyframes
@@ -589,7 +596,7 @@
     // 2. Let keyframes be the result of applying the procedure to compute missing keyframe offsets to the keyframes for this keyframe effect.
 
     // 3. For each keyframe in keyframes perform the following steps:
-    if (is<DeclarativeAnimation>(animation())) {
+    if (m_parsedKeyframes.isEmpty() && m_blendingKeyframesSource != BlendingKeyframesSource::WebAnimation) {
         auto* target = m_target.get();
         auto* renderer = this->renderer();
 
@@ -683,6 +690,14 @@
     return result;
 }
 
+ExceptionOr<void> KeyframeEffect::setBindingsKeyframes(JSGlobalObject& lexicalGlobalObject, Strong<JSObject>&& keyframesInput)
+{
+    auto retVal = setKeyframes(lexicalGlobalObject, WTFMove(keyframesInput));
+    if (!retVal.hasException() && is<CSSAnimation>(animation()))
+        downcast<CSSAnimation>(*animation()).effectKeyframesWereSetUsingBindings();
+    return retVal;
+}
+
 ExceptionOr<void> KeyframeEffect::setKeyframes(JSGlobalObject& lexicalGlobalObject, Strong<JSObject>&& keyframesInput)
 {
     auto processKeyframesResult = processKeyframes(lexicalGlobalObject, WTFMove(keyframesInput));
@@ -711,10 +726,15 @@
 
     // 5. Perform the steps corresponding to the first matching condition from below,
     Vector<String> unusedEasings;
-    if (!method.isUndefined())
-        processIterableKeyframes(lexicalGlobalObject, WTFMove(keyframesInput), WTFMove(method), parsedKeyframes);
-    else
-        processPropertyIndexedKeyframes(lexicalGlobalObject, WTFMove(keyframesInput), parsedKeyframes, unusedEasings);
+    if (!method.isUndefined()) {
+        auto retVal = processIterableKeyframes(lexicalGlobalObject, WTFMove(keyframesInput), WTFMove(method), parsedKeyframes);
+        if (retVal.hasException())
+            return retVal.releaseException();
+    } else {
+        auto retVal = processPropertyIndexedKeyframes(lexicalGlobalObject, WTFMove(keyframesInput), parsedKeyframes, unusedEasings);
+        if (retVal.hasException())
+            return retVal.releaseException();
+    }
 
     // 6. If processed keyframes is not loosely sorted by offset, throw a TypeError and abort these steps.
     // 7. If there exist any keyframe in processed keyframes whose keyframe offset is non-null and less than

Modified: trunk/Source/WebCore/animation/KeyframeEffect.h (260670 => 260671)


--- trunk/Source/WebCore/animation/KeyframeEffect.h	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/KeyframeEffect.h	2020-04-24 21:42:50 UTC (rev 260671)
@@ -109,7 +109,9 @@
     const String pseudoElement() const;
     ExceptionOr<void> setPseudoElement(const String&);
 
+    Vector<JSC::Strong<JSC::JSObject>> getBindingsKeyframes(JSC::JSGlobalObject&);
     Vector<JSC::Strong<JSC::JSObject>> getKeyframes(JSC::JSGlobalObject&);
+    ExceptionOr<void> setBindingsKeyframes(JSC::JSGlobalObject&, JSC::Strong<JSC::JSObject>&&);
     ExceptionOr<void> setKeyframes(JSC::JSGlobalObject&, JSC::Strong<JSC::JSObject>&&);
 
     IterationCompositeOperation iterationComposite() const { return m_iterationCompositeOperation; }

Modified: trunk/Source/WebCore/animation/KeyframeEffect.idl (260670 => 260671)


--- trunk/Source/WebCore/animation/KeyframeEffect.idl	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/KeyframeEffect.idl	2020-04-24 21:42:50 UTC (rev 260671)
@@ -38,8 +38,8 @@
     [MayThrowException] attribute CSSOMString? pseudoElement;
     [EnabledAtRuntime=WebAnimationsCompositeOperations] attribute IterationCompositeOperation iterationComposite;
     [EnabledAtRuntime=WebAnimationsCompositeOperations] attribute CompositeOperation composite;
-    [CallWith=GlobalObject] sequence<object> getKeyframes();
-    [MayThrowException, CallWith=GlobalObject] void setKeyframes(object? keyframes);
+    [CallWith=GlobalObject, ImplementedAs=getBindingsKeyframes] sequence<object> getKeyframes();
+    [MayThrowException, CallWith=GlobalObject, ImplementedAs=setBindingsKeyframes] void setKeyframes(object? keyframes);
 };
 
 dictionary BasePropertyIndexedKeyframe {

Modified: trunk/Source/WebCore/animation/WebAnimation.cpp (260670 => 260671)


--- trunk/Source/WebCore/animation/WebAnimation.cpp	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/WebAnimation.cpp	2020-04-24 21:42:50 UTC (rev 260671)
@@ -145,6 +145,11 @@
     InspectorInstrumentation::didChangeWebAnimationEffectTiming(*this);
 }
 
+void WebAnimation::setBindingsEffect(RefPtr<AnimationEffect>&& newEffect)
+{
+    setEffect(WTFMove(newEffect));
+}
+
 void WebAnimation::setEffect(RefPtr<AnimationEffect>&& newEffect)
 {
     // 3.4.3. Setting the target effect of an animation
@@ -297,6 +302,11 @@
     return secondsToWebAnimationsAPITime(m_startTime.value());
 }
 
+void WebAnimation::setBindingsStartTime(Optional<double> startTime)
+{
+    setStartTime(startTime);
+}
+
 void WebAnimation::setStartTime(Optional<double> startTime)
 {
     // 3.4.6 The procedure to set the start time of animation, animation, to new start time, is as follows:
@@ -1097,6 +1107,11 @@
     return { };
 }
 
+ExceptionOr<void> WebAnimation::bindingsReverse()
+{
+    return reverse();
+}
+
 ExceptionOr<void> WebAnimation::reverse()
 {
     LOG_WITH_STREAM(Animations, stream << "WebAnimation " << this << " reverse (current time is " << currentTime() << ")");

Modified: trunk/Source/WebCore/animation/WebAnimation.h (260670 => 260671)


--- trunk/Source/WebCore/animation/WebAnimation.h	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/WebAnimation.h	2020-04-24 21:42:50 UTC (rev 260671)
@@ -66,6 +66,8 @@
     const String& id() const { return m_id; }
     void setId(const String& id) { m_id = id; }
 
+    AnimationEffect* bindingsEffect() const { return effect(); }
+    virtual void setBindingsEffect(RefPtr<AnimationEffect>&&);
     AnimationEffect* effect() const { return m_effect.get(); }
     void setEffect(RefPtr<AnimationEffect>&&);
     AnimationTimeline* timeline() const { return m_timeline.get(); }
@@ -98,12 +100,15 @@
     ExceptionOr<void> play();
     void updatePlaybackRate(double);
     ExceptionOr<void> pause();
+    virtual ExceptionOr<void> bindingsReverse();
     ExceptionOr<void> reverse();
     void persist();
     ExceptionOr<void> commitStyles();
 
-    virtual Optional<double> startTime() const;
-    virtual void setStartTime(Optional<double>);
+    virtual Optional<double> bindingsStartTime() const { return startTime(); }
+    virtual void setBindingsStartTime(Optional<double>);
+    Optional<double> startTime() const;
+    void setStartTime(Optional<double>);
     virtual Optional<double> bindingsCurrentTime() const;
     virtual ExceptionOr<void> setBindingsCurrentTime(Optional<double>);
     virtual PlayState bindingsPlayState() const { return playState(); }

Modified: trunk/Source/WebCore/animation/WebAnimation.idl (260670 => 260671)


--- trunk/Source/WebCore/animation/WebAnimation.idl	2020-04-24 21:41:32 UTC (rev 260670)
+++ trunk/Source/WebCore/animation/WebAnimation.idl	2020-04-24 21:42:50 UTC (rev 260671)
@@ -44,9 +44,9 @@
     CustomToJSObject
 ] interface WebAnimation : EventTarget {
     attribute DOMString id;
-    attribute AnimationEffect? effect;
+    [ImplementedAs=bindingsEffect] attribute AnimationEffect? effect;
     [RuntimeConditionallyReadWrite=WebAnimationsMutableTimelines] attribute AnimationTimeline? timeline;
-    attribute double? startTime;
+    [ImplementedAs=bindingsStartTime] attribute double? startTime;
     [MayThrowException, ImplementedAs=bindingsCurrentTime] attribute double? currentTime;
     attribute double playbackRate;
     [ImplementedAs=bindingsPlayState] readonly attribute AnimationPlayState playState;
@@ -62,7 +62,7 @@
     [MayThrowException, ImplementedAs=bindingsPlay] void play();
     [MayThrowException, ImplementedAs=bindingsPause] void pause();
     void updatePlaybackRate(double playbackRate);
-    [MayThrowException] void reverse();
+    [MayThrowException, ImplementedAs=bindingsReverse] void reverse();
     void persist();
     [MayThrowException] void commitStyles();
 };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to