[Web Animations] Implement the finish() method on Animation
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Dec 2017 20:08:21 +0000 (20:08 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Dec 2017 20:08:21 +0000 (20:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180822
<rdar://problem/36053282>

Reviewed by Dean Jackson.

Source/WebCore:

We implement the finish() method on the Animation interface with full spec text defining
the normative behavior of those methods and code matching those steps. Implementing the
finish() method required implementing the notion of "silently setting the current time",
which the Web Animations spec defines as well.

* animation/WebAnimation.cpp:
(WebCore::WebAnimation::silentlySetCurrentTime):
(WebCore::WebAnimation::setCurrentTime):
(WebCore::WebAnimation::finish):
* animation/WebAnimation.h:
* animation/WebAnimation.idl:

LayoutTests:

Rebase some WPT expectations with progressions due to exposing the finish() method.
We're also removing a WebKit-only test that is no longer relevant and started failing
with compliant behavior.

* http/wpt/web-animations/interfaces/Animation/idlharness-expected.txt:
* http/wpt/web-animations/timing-model/animations/set-the-timeline-of-an-animation-expected.txt:
* http/wpt/wk-web-animations/timing-model/animation-playback-rate-expected.txt: Removed.
* http/wpt/wk-web-animations/timing-model/animation-playback-rate.html: Removed.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@225917 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/wpt/web-animations/interfaces/Animation/idlharness-expected.txt
LayoutTests/http/wpt/web-animations/timing-model/animations/set-the-timeline-of-an-animation-expected.txt
LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate-expected.txt [deleted file]
LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate.html [deleted file]
Source/WebCore/ChangeLog
Source/WebCore/animation/WebAnimation.cpp
Source/WebCore/animation/WebAnimation.h
Source/WebCore/animation/WebAnimation.idl

index 8d46ec9..5869974 100644 (file)
@@ -1,3 +1,20 @@
+2017-12-14  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement the finish() method on Animation
+        https://bugs.webkit.org/show_bug.cgi?id=180822
+        <rdar://problem/36053282>
+
+        Reviewed by Dean Jackson.
+
+        Rebase some WPT expectations with progressions due to exposing the finish() method.
+        We're also removing a WebKit-only test that is no longer relevant and started failing
+        with compliant behavior.
+
+        * http/wpt/web-animations/interfaces/Animation/idlharness-expected.txt:
+        * http/wpt/web-animations/timing-model/animations/set-the-timeline-of-an-animation-expected.txt:
+        * http/wpt/wk-web-animations/timing-model/animation-playback-rate-expected.txt: Removed.
+        * http/wpt/wk-web-animations/timing-model/animation-playback-rate.html: Removed.
+
 2017-12-14  Chris Dumez  <cdumez@apple.com>
 
         Service worker script fetching currently always uses the network cache
index e3d10b4..abf2064 100644 (file)
@@ -17,7 +17,7 @@ PASS Animation interface: attribute finished
 PASS Animation interface: attribute onfinish 
 FAIL Animation interface: attribute oncancel assert_true: The prototype object must have a property "oncancel" expected true got false
 FAIL Animation interface: operation cancel() assert_own_property: interface prototype object missing non-static operation expected property "cancel" missing
-FAIL Animation interface: operation finish() assert_own_property: interface prototype object missing non-static operation expected property "finish" missing
+PASS Animation interface: operation finish() 
 PASS Animation interface: operation play() 
 PASS Animation interface: operation pause() 
 FAIL Animation interface: operation reverse() assert_own_property: interface prototype object missing non-static operation expected property "reverse" missing
@@ -35,7 +35,7 @@ PASS Animation interface: new Animation() must inherit property "finished" with
 PASS Animation interface: new Animation() must inherit property "onfinish" with the proper type 
 FAIL Animation interface: new Animation() must inherit property "oncancel" with the proper type assert_inherits: property "oncancel" not found in prototype chain
 FAIL Animation interface: new Animation() must inherit property "cancel()" with the proper type assert_inherits: property "cancel" not found in prototype chain
-FAIL Animation interface: new Animation() must inherit property "finish()" with the proper type assert_inherits: property "finish" not found in prototype chain
+PASS Animation interface: new Animation() must inherit property "finish()" with the proper type 
 PASS Animation interface: new Animation() must inherit property "play()" with the proper type 
 PASS Animation interface: new Animation() must inherit property "pause()" with the proper type 
 FAIL Animation interface: new Animation() must inherit property "reverse()" with the proper type assert_inherits: property "reverse" not found in prototype chain
index fea2436..00d7f8d 100644 (file)
@@ -1,6 +1,6 @@
 
-FAIL After setting timeline on paused animation it is still paused assert_equals: expected "paused" but got "finished"
-FAIL After setting timeline on animation paused outside active interval it is still paused assert_equals: expected "paused" but got "finished"
+PASS After setting timeline on paused animation it is still paused 
+PASS After setting timeline on animation paused outside active interval it is still paused 
 PASS After setting timeline on an idle animation without a start time it is still idle 
 FAIL After setting timeline on an idle animation with a start time it is running assert_equals: expected "idle" but got "finished"
 FAIL After setting timeline on an idle animation with a sufficiently ancient start time it is finished assert_equals: expected "idle" but got "finished"
@@ -8,7 +8,7 @@ FAIL After setting timeline on a play-pending animation it is still pending asse
 FAIL After setting timeline on a play-pending animation it begins playing after pending assert_equals: expected "pending" but got "finished"
 FAIL After setting timeline on a pause-pending animation it is still pending assert_equals: expected "pending" but got "paused"
 FAIL After setting timeline on a pause-pending animation it becomes paused after pending assert_equals: expected "pending" but got "paused"
-FAIL After clearing timeline on paused animation it is still paused assert_equals: expected "paused" but got "finished"
+PASS After clearing timeline on paused animation it is still paused 
 PASS After clearing timeline on finished animation it is idle 
 FAIL After clearing timeline on running animation it is idle assert_equals: expected "running" but got "finished"
 PASS After clearing timeline on idle animation it is still idle 
diff --git a/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate-expected.txt b/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate-expected.txt
deleted file mode 100644 (file)
index d6e8368..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-PASS Playback rate defaults to 1 
-PASS Correctly compute the current time based on the playback rate 
-PASS Correctly compute the start time based on the playback rate when setting current time 
-PASS Correctly compute the start time based on a playback rate < 1 when setting current time 
-PASS Correctly compute the start time based on a playback rate < 0 
-PASS Correctly compute a current time of 0 when playback rate is 0 
-
diff --git a/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate.html b/LayoutTests/http/wpt/wk-web-animations/timing-model/animation-playback-rate.html
deleted file mode 100644 (file)
index d8a19a4..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Tests for playback rate</title>
-<link rel="help" href="https://w3c.github.io/web-animations/#updating-the-playback-rate-of-an-animation">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body>
-<div id="log"></div>
-<script>
-'use strict';
-
-internals.pauseTimeline(document.timeline);
-
-test(t => {
-  const animation = new Animation;
-
-  assert_equals(animation.playbackRate, 1);
-}, 'Playback rate defaults to 1');
-
-test(t => {
-  const animation = new Animation;
-  animation.startTime = 1000;
-  animation.playbackRate = 2;
-  internals.setTimelineCurrentTime(document.timeline, 3000);
-
-  assert_equals(animation.startTime, 1000);
-  assert_equals(animation.currentTime, 4000);
-  assert_equals(animation.playbackRate, 2);
-}, 'Correctly compute the current time based on the playback rate');
-
-test(t => {
-  const animation = new Animation;
-  animation.startTime = 2000;
-  animation.playbackRate = 2;
-  internals.setTimelineCurrentTime(document.timeline, 4000);
-
-  animation.currentTime = 3000;
-
-  assert_equals(animation.startTime, 2500);
-  assert_equals(animation.currentTime, 3000);
-  assert_equals(animation.playbackRate, 2);
-}, 'Correctly compute the start time based on the playback rate when setting current time');
-
-test(t => {
-  const animation = new Animation;
-  animation.startTime = 2000;
-  animation.playbackRate = 0.5;
-  internals.setTimelineCurrentTime(document.timeline, 4000);
-
-  animation.currentTime = 1000;
-
-  assert_equals(animation.startTime, 2000);
-  assert_equals(animation.currentTime, 1000);
-  assert_equals(animation.playbackRate, 0.5);
-}, 'Correctly compute the start time based on a playback rate < 1 when setting current time');
-
-test(t => {
-  const animation = new Animation;
-  animation.currentTime = 1000;
-  animation.playbackRate = -1;
-  internals.setTimelineCurrentTime(document.timeline, 4000);
-
-  assert_equals(animation.startTime, 5000);
-  assert_equals(animation.currentTime, 1000);
-  assert_equals(animation.playbackRate, -1);
-}, 'Correctly compute the start time based on a playback rate < 0');
-
-test(t => {
-  const animation = new Animation;
-  animation.playbackRate = 0;
-  animation.startTime = 0;
-  internals.setTimelineCurrentTime(document.timeline, 1000);
-
-  assert_equals(animation.startTime, 0);
-  assert_equals(animation.currentTime, 0);
-  assert_equals(animation.playbackRate, 0);
-}, 'Correctly compute a current time of 0 when playback rate is 0');
-
-</script>
-</body>
index 9c92717..8cf4ddb 100644 (file)
@@ -1,3 +1,23 @@
+2017-12-14  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement the finish() method on Animation
+        https://bugs.webkit.org/show_bug.cgi?id=180822
+        <rdar://problem/36053282>
+
+        Reviewed by Dean Jackson.
+
+        We implement the finish() method on the Animation interface with full spec text defining
+        the normative behavior of those methods and code matching those steps. Implementing the
+        finish() method required implementing the notion of "silently setting the current time",
+        which the Web Animations spec defines as well.
+
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::silentlySetCurrentTime):
+        (WebCore::WebAnimation::setCurrentTime):
+        (WebCore::WebAnimation::finish):
+        * animation/WebAnimation.h:
+        * animation/WebAnimation.idl:
+
 2017-12-14  Chris Dumez  <cdumez@apple.com>
 
         Service worker script fetching currently always uses the network cache
index f5b1d2a..172fecd 100644 (file)
@@ -203,23 +203,70 @@ std::optional<Seconds> WebAnimation::currentTime(RespectHoldTime respectHoldTime
     return (m_timeline->currentTime().value() - m_startTime.value()) * m_playbackRate;
 }
 
-void WebAnimation::setCurrentTime(std::optional<Seconds> seekTime)
+ExceptionOr<void> WebAnimation::silentlySetCurrentTime(std::optional<Seconds> seekTime)
 {
-    // FIXME: account for hold time when we support it (webkit.org/b/178932),
-    // including situations where playbackRate is 0.
+    // 3.4.5. Setting the current time of an animation
+    // https://drafts.csswg.org/web-animations-1/#setting-the-current-time-of-an-animation
 
-    if (!m_timeline) {
-        setStartTime(std::nullopt);
-        return;
+    // 1. If seek time is an unresolved time value, then perform the following steps.
+    if (!seekTime) {
+        // 1. If the current time is resolved, then throw a TypeError.
+        if (currentTime())
+            return Exception { TypeError };
+        // 2. Abort these steps.
+        return { };
     }
 
-    auto timelineTime = m_timeline->currentTime();
-    if (!timelineTime) {
+    // 2. Update either animation's hold time or start time as follows:
+    // If any of the following conditions are true:
+    //     - animation's hold time is resolved, or
+    //     - animation's start time is unresolved, or
+    //     - animation has no associated timeline or the associated timeline is inactive, or
+    //     - animation's playback rate is 0,
+    // Set animation's hold time to seek time.
+    // Otherwise, set animation's start time to the result of evaluating timeline time - (seek time / playback rate)
+    // where timeline time is the current time value of timeline associated with animation.
+    if (m_holdTime || !startTime() || !m_timeline || !m_timeline->currentTime() || !m_playbackRate)
+        m_holdTime = seekTime;
+    else
+        setStartTime(m_timeline->currentTime().value() - (seekTime.value() / m_playbackRate));
+
+    // 3. If animation has no associated timeline or the associated timeline is inactive, make animation's start time unresolved.
+    if (!m_timeline || !m_timeline->currentTime())
         setStartTime(std::nullopt);
-        return;
+
+    // 4. Make animation's previous current time unresolved.
+    m_previousCurrentTime = std::nullopt;
+
+    return { };
+}
+
+ExceptionOr<void> WebAnimation::setCurrentTime(std::optional<Seconds> seekTime)
+{
+    // 3.4.5. Setting the current time of an animation
+    // https://drafts.csswg.org/web-animations-1/#setting-the-current-time-of-an-animation
+
+    // 1. Run the steps to silently set the current time of animation to seek time.
+    auto silentResult = silentlySetCurrentTime(seekTime);
+    if (silentResult.hasException())
+        return silentResult.releaseException();
+
+    // 2. If animation has a pending pause task, synchronously complete the pause operation by performing the following steps:
+    if (hasPendingPauseTask()) {
+        // 1. Set animation's hold time to seek time.
+        m_holdTime = seekTime;
+        // 2. Make animation's start time unresolved.
+        setStartTime(std::nullopt);
+        // 3. Cancel the pending pause task.
+        setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
+        // 4. Resolve animation's current ready promise with animation.
+        m_readyPromise.resolve(*this);
     }
 
-    setStartTime(timelineTime.value() - (seekTime.value() / m_playbackRate));
+    // 3. Run the procedure to update an animation's finished state for animation with the did seek flag set to true, and the synchronously notify flag set to false.
+    updateFinishedState(DidSeek::Yes, SynchronouslyNotify::No);
+
+    return { };
 }
 
 void WebAnimation::setPlaybackRate(double newPlaybackRate)
@@ -289,6 +336,52 @@ void WebAnimation::enqueueAnimationPlaybackEvent(const AtomicString& type, std::
     }
 }
 
+ExceptionOr<void> WebAnimation::finish()
+{
+    // 3.4.15. Finishing an animation
+    // https://drafts.csswg.org/web-animations-1/#finishing-an-animation-section
+
+    // An animation can be advanced to the natural end of its current playback direction by using the procedure to finish an animation for animation defined below:
+    //
+    // 1. If animation playback rate is zero, or if animation playback rate > 0 and target effect end is infinity, throw an InvalidStateError and abort these steps.
+    if (!m_playbackRate || (m_playbackRate > 0 && effectEndTime() == Seconds::infinity()))
+        return Exception { InvalidStateError };
+
+    // 2. Set limit as follows:
+    // If animation playback rate > 0, let limit be target effect end.
+    // Otherwise, let limit be zero.
+    auto limit = m_playbackRate > 0 ? effectEndTime() : 0_s;
+
+    // 3. Silently set the current time to limit.
+    silentlySetCurrentTime(limit);
+
+    // 4. If animation's start time is unresolved and animation has an associated active timeline, let the start time be the result of
+    //    evaluating timeline time - (limit / playback rate) where timeline time is the current time value of the associated timeline.
+    if (!startTime() && m_timeline && m_timeline->currentTime())
+        setStartTime(m_timeline->currentTime().value() - (limit / m_playbackRate));
+
+    // 5. If there is a pending pause task and start time is resolved,
+    if (hasPendingPauseTask() && startTime()) {
+        // 1. Let the hold time be unresolved.
+        m_holdTime = std::nullopt;
+        // 2. Cancel the pending pause task.
+        setTimeToRunPendingPauseTask(TimeToRunPendingTask::NotScheduled);
+        // 3. Resolve the current ready promise of animation with animation.
+        m_readyPromise.resolve(*this);
+    }
+
+    // 6. If there is a pending play task and start time is resolved, cancel that task and resolve the current ready promise of animation with animation.
+    if (hasPendingPlayTask() && startTime()) {
+        setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
+        m_readyPromise.resolve(*this);
+    }
+
+    // 7. Run the procedure to update an animation's finished state animation with the did seek flag set to true, and the synchronously notify flag set to true.
+    updateFinishedState(DidSeek::Yes, SynchronouslyNotify::Yes);
+
+    return { };
+}
+
 void WebAnimation::updateFinishedState(DidSeek didSeek, SynchronouslyNotify synchronouslyNotify)
 {
     // 3.4.14. Updating the finished state
index 4b500f6..72bcdc1 100644 (file)
@@ -62,7 +62,7 @@ public:
     std::optional<double> bindingsCurrentTime() const;
     ExceptionOr<void> setBindingsCurrentTime(std::optional<double>);
     std::optional<Seconds> currentTime() const;
-    void setCurrentTime(std::optional<Seconds>);
+    ExceptionOr<void> setCurrentTime(std::optional<Seconds>);
 
     double playbackRate() const { return m_playbackRate; }
     void setPlaybackRate(double);
@@ -78,6 +78,7 @@ public:
     using FinishedPromise = DOMPromiseProxyWithResolveCallback<IDLInterface<WebAnimation>>;
     FinishedPromise& finished() { return m_finishedPromise; }
 
+    ExceptionOr<void> finish();
     ExceptionOr<void> play();
     ExceptionOr<void> pause();
 
@@ -107,6 +108,7 @@ private:
     WebAnimation& readyPromiseResolve();
     WebAnimation& finishedPromiseResolve();
     std::optional<Seconds> currentTime(RespectHoldTime) const;
+    ExceptionOr<void> silentlySetCurrentTime(std::optional<Seconds>);
     void finishNotificationSteps();
     void scheduleMicrotaskIfNeeded();
     void performMicrotask();
index cc53a41..cb30bc3 100644 (file)
@@ -48,6 +48,7 @@ enum AnimationPlayState {
     attribute EventHandler onfinish;
     readonly attribute Promise<WebAnimation> ready;
     readonly attribute Promise<WebAnimation> finished;
+    [MayThrowException] void finish();
     [MayThrowException] void play();
     [MayThrowException] void pause();
 };