[Web Animations] Implement the play() and pause() methods on Animation
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Dec 2017 17:39:40 +0000 (17:39 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Dec 2017 17:39:40 +0000 (17:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178932
<rdar://problem/35271069>

Reviewed by Eric Carlson.

Source/WebCore:

We implement the play() and pause() methods of the Animation interface with full spec text defining
the normative behavior of those methods and code matching those steps. Playing and pausing animations
incur running a play or pause task when conditions are met, specifically here when the timeline is ready.
So we add the notion of pending tasks and provide a proper implementation of pending() which we had
introduced in an earlier patch with a constant false return value.

Note that the play() method exposes an auto-rewinding flag which we always set to true, but other specs,
namely CSS Animations, do not require the rewinding behavior, so we expose it for future use.

* animation/WebAnimation.cpp:
(WebCore::WebAnimation::play):
(WebCore::WebAnimation::setTimeToRunPendingPlayTask):
(WebCore::WebAnimation::runPendingPlayTask):
(WebCore::WebAnimation::pause):
(WebCore::WebAnimation::setTimeToRunPendingPauseTask):
(WebCore::WebAnimation::runPendingPauseTask):
(WebCore::WebAnimation::updatePendingTasks):
* animation/WebAnimation.h:
* animation/WebAnimation.idl:

LayoutTests:

Rebase some WPT expectations with progressions due to exposing the play() and pause() methods.

* TestExpectations: Temporarily mark a test as flaky as it logs an unexpected current time which
may change between runs.
* http/wpt/web-animations/interfaces/Animation/idlharness-expected.txt:
* http/wpt/web-animations/interfaces/Animation/startTime-expected.txt:
* http/wpt/web-animations/interfaces/KeyframeEffect/setTarget-expected.txt:
* http/wpt/web-animations/timing-model/animations/current-time-expected.txt:
* http/wpt/web-animations/timing-model/animations/set-the-target-effect-of-an-animation-expected.txt:
* http/wpt/web-animations/timing-model/animations/set-the-timeline-of-an-animation-expected.txt:

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/wpt/web-animations/interfaces/Animation/idlharness-expected.txt
LayoutTests/http/wpt/web-animations/interfaces/Animation/startTime-expected.txt
LayoutTests/http/wpt/web-animations/interfaces/KeyframeEffect/setTarget-expected.txt
LayoutTests/http/wpt/web-animations/timing-model/animations/current-time-expected.txt
LayoutTests/http/wpt/web-animations/timing-model/animations/set-the-target-effect-of-an-animation-expected.txt
LayoutTests/http/wpt/web-animations/timing-model/animations/set-the-timeline-of-an-animation-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/animation/WebAnimation.cpp
Source/WebCore/animation/WebAnimation.h
Source/WebCore/animation/WebAnimation.idl

index e10f721..c05672a 100644 (file)
@@ -1,3 +1,22 @@
+2017-12-13  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement the play() and pause() methods on Animation
+        https://bugs.webkit.org/show_bug.cgi?id=178932
+        <rdar://problem/35271069>
+
+        Reviewed by Eric Carlson.
+
+        Rebase some WPT expectations with progressions due to exposing the play() and pause() methods.
+
+        * TestExpectations: Temporarily mark a test as flaky as it logs an unexpected current time which
+        may change between runs.
+        * http/wpt/web-animations/interfaces/Animation/idlharness-expected.txt:
+        * http/wpt/web-animations/interfaces/Animation/startTime-expected.txt:
+        * http/wpt/web-animations/interfaces/KeyframeEffect/setTarget-expected.txt:
+        * http/wpt/web-animations/timing-model/animations/current-time-expected.txt:
+        * http/wpt/web-animations/timing-model/animations/set-the-target-effect-of-an-animation-expected.txt:
+        * http/wpt/web-animations/timing-model/animations/set-the-timeline-of-an-animation-expected.txt:
+
 2017-12-14  Romain Bellessort  <romain.bellessort@crf.canon.fr>
 
         [Readable Streams API] Remove properties tests covered by WPT
index f4e8dc0..0932c18 100644 (file)
@@ -1640,6 +1640,7 @@ webkit.org/b/179069 imported/w3c/web-platform-tests/html/semantics/embedded-cont
 
 webkit.org/b/179287 http/wpt/web-animations/interfaces/AnimationTimeline/document-timeline.html [ Pass Failure ]
 webkit.org/b/179287 http/wpt/web-animations/timing-model/animations/set-the-animation-start-time.html [ Pass Failure ]
+webkit.org/b/179287 http/wpt/web-animations/interfaces/Animation/startTime.html [ Pass Failure ]
 
 webkit.org/b/177440 imported/w3c/web-platform-tests/html/browsers/origin/relaxing-the-same-origin-restriction/document_domain_setter_null.tentative.html [ Pass Failure ]
 
index caba72a..e3d10b4 100644 (file)
@@ -18,8 +18,8 @@ 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
-FAIL Animation interface: operation play() assert_own_property: interface prototype object missing non-static operation expected property "play" missing
-FAIL Animation interface: operation pause() assert_own_property: interface prototype object missing non-static operation expected property "pause" missing
+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
 PASS Animation must be primary interface of new Animation() 
 PASS Stringification of new Animation() 
@@ -36,7 +36,7 @@ PASS Animation interface: new Animation() must inherit property "onfinish" with
 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
-FAIL Animation interface: new Animation() must inherit property "play()" with the proper type assert_inherits: property "play" not found in prototype chain
-FAIL Animation interface: new Animation() must inherit property "pause()" with the proper type assert_inherits: property "pause" not found in prototype chain
+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 49f7029..922bb18 100644 (file)
@@ -1,7 +1,7 @@
 
 PASS startTime of a newly created (idle) animation is unresolved 
-FAIL startTime of a play-pending animation is unresolved animation.play is not a function. (In 'animation.play()', 'animation.play' is undefined)
-FAIL startTime of a pause-pending animation is unresolved animation.pause is not a function. (In 'animation.pause()', 'animation.pause' is undefined)
+FAIL startTime of a play-pending animation is unresolved assert_equals: startTime is unresolved expected (object) null but got (number) 61.900000000000006
+PASS startTime of a pause-pending animation is unresolved 
 FAIL startTime of a play-pending animation created using Element.animate shortcut is unresolved createDiv(t).animate is not a function. (In 'createDiv(t).animate(null)', 'createDiv(t).animate' is undefined)
 FAIL startTime is resolved when running createDiv(t).animate is not a function. (In 'createDiv(t).animate(null, 100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
 FAIL startTime and currentTime are unresolved when animation is cancelled createDiv(t).animate is not a function. (In 'createDiv(t).animate(null, 100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
index 8c94980..d7a6ca8 100644 (file)
@@ -1,6 +1,6 @@
 
 FAIL Test setting target before constructing the associated animation Attempted to assign to readonly property.
-FAIL Test setting target from null to a valid target anim.play is not a function. (In 'anim.play()', 'anim.play' is undefined)
+FAIL Test setting target from null to a valid target Attempted to assign to readonly property.
 FAIL Test setting target from a valid target to null div.animate is not a function. (In 'div.animate(gKeyFrames, 100 * MS_PER_SEC)', 'div.animate' is undefined)
 FAIL Test setting target from a valid target to another target a.animate is not a function. (In 'a.animate(gKeyFrames, 100 * MS_PER_SEC)', 'a.animate' is undefined)
 
index c6a663c..3252334 100644 (file)
@@ -1,7 +1,7 @@
 
 Harness Error (TIMEOUT), message = null
 
-FAIL The current time returns the hold time when set animation.play is not a function. (In 'animation.play()', 'animation.play' is undefined)
+PASS The current time returns the hold time when set 
 TIMEOUT The current time is unresolved when there is no associated timeline (and no hold time is set) Test timed out
 PASS The current time is unresolved when the start time is unresolved (and no hold time is set) 
 PASS The current time is calculated from the timeline time, start time and playback rate 
index 1ed93aa..02d1e22 100644 (file)
@@ -1,8 +1,8 @@
 
 FAIL If new effect is null and old effect is not null, we reset the pending tasks and ready promise is rejected createDiv(t).animate is not a function. (In 'createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
                                   100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
-FAIL If animation has a pending pause task, reschedule that task to run as soon as animation is ready. anim.pause is not a function. (In 'anim.pause()', 'anim.pause' is undefined)
-FAIL If animation has a pending play task, reschedule that task to run as soon as animation is ready to play new effect. anim.play is not a function. (In 'anim.play()', 'anim.play' is undefined)
+FAIL If animation has a pending pause task, reschedule that task to run as soon as animation is ready. assert_equals: expected "pending" but got "paused"
+FAIL If animation has a pending play task, reschedule that task to run as soon as animation is ready to play new effect. assert_equals: expected "pending" but got "finished"
 FAIL When setting the effect of an animation to the effect of an existing animation, the existing animation's target effect should be set to null. createDiv(t).animate is not a function. (In 'createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
                                    100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
 FAIL After setting the target effect of animation to the target effect of an existing animation, the target effect's timing is updated to reflect the current time of the new animation. createDiv(t).animate is not a function. (In 'createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
index 48a42ab..fea2436 100644 (file)
@@ -4,17 +4,17 @@ FAIL After setting timeline on animation paused outside active interval it is st
 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"
-FAIL After setting timeline on a play-pending animation it is still pending animation.play is not a function. (In 'animation.play()', 'animation.play' is undefined)
-FAIL After setting timeline on a play-pending animation it begins playing after pending animation.play is not a function. (In 'animation.play()', 'animation.play' is undefined)
-FAIL After setting timeline on a pause-pending animation it is still pending animation.pause is not a function. (In 'animation.pause()', 'animation.pause' is undefined)
-FAIL After setting timeline on a pause-pending animation it becomes paused after pending animation.pause is not a function. (In 'animation.pause()', 'animation.pause' is undefined)
+FAIL After setting timeline on a play-pending animation it is still pending assert_equals: expected "pending" but got "finished"
+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 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 
 FAIL After clearing timeline on play-pending animation it is still pending createDiv(t).animate is not a function. (In 'createDiv(t).animate(null, 100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
 FAIL After clearing and re-setting timeline on play-pending animation it begins to play createDiv(t).animate is not a function. (In 'createDiv(t).animate(null, 100 * MS_PER_SEC)', 'createDiv(t).animate' is undefined)
-FAIL After clearing timeline on a pause-pending animation it is still pending animation.pause is not a function. (In 'animation.pause()', 'animation.pause' is undefined)
-FAIL After clearing and re-setting timeline on a pause-pending animation it becomes paused animation.pause is not a function. (In 'animation.pause()', 'animation.pause' is undefined)
-FAIL After clearing and re-setting timeline on an animation in the middle of an aborted pause, it continues playing using the same start time animation.pause is not a function. (In 'animation.pause()', 'animation.pause' is undefined)
+FAIL After clearing timeline on a pause-pending animation it is still pending assert_equals: expected "pending" but got "paused"
+FAIL After clearing and re-setting timeline on a pause-pending animation it becomes paused assert_equals: expected "pending" but got "paused"
+FAIL After clearing and re-setting timeline on an animation in the middle of an aborted pause, it continues playing using the same start time assert_equals: expected "pending" but got "finished"
 
index ff1cb4a..2a8751c 100644 (file)
@@ -1,3 +1,31 @@
+2017-12-13  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] Implement the play() and pause() methods on Animation
+        https://bugs.webkit.org/show_bug.cgi?id=178932
+        <rdar://problem/35271069>
+
+        Reviewed by Eric Carlson.
+
+        We implement the play() and pause() methods of the Animation interface with full spec text defining
+        the normative behavior of those methods and code matching those steps. Playing and pausing animations
+        incur running a play or pause task when conditions are met, specifically here when the timeline is ready.
+        So we add the notion of pending tasks and provide a proper implementation of pending() which we had
+        introduced in an earlier patch with a constant false return value.
+
+        Note that the play() method exposes an auto-rewinding flag which we always set to true, but other specs,
+        namely CSS Animations, do not require the rewinding behavior, so we expose it for future use.
+
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::play):
+        (WebCore::WebAnimation::setTimeToRunPendingPlayTask):
+        (WebCore::WebAnimation::runPendingPlayTask):
+        (WebCore::WebAnimation::pause):
+        (WebCore::WebAnimation::setTimeToRunPendingPauseTask):
+        (WebCore::WebAnimation::runPendingPauseTask):
+        (WebCore::WebAnimation::updatePendingTasks):
+        * animation/WebAnimation.h:
+        * animation/WebAnimation.idl:
+
 2017-12-14  Frederic Wang  <fwang@igalia.com>
 
         Make GraphicsLayer::dumpProperties dump m_offsetFromRenderer
index f8a7847..f5b1d2a 100644 (file)
@@ -408,6 +408,212 @@ void WebAnimation::finishNotificationSteps()
     enqueueAnimationPlaybackEvent(eventNames().finishEvent, currentTime(), m_timeline ? m_timeline->currentTime() : std::nullopt);
 }
 
+ExceptionOr<void> WebAnimation::play()
+{
+    return play(AutoRewind::Yes);
+}
+
+ExceptionOr<void> WebAnimation::play(AutoRewind autoRewind)
+{
+    // 3.4.10. Playing an animation
+    // https://drafts.csswg.org/web-animations-1/#play-an-animation
+
+    auto localTime = currentTime();
+    auto endTime = effectEndTime();
+
+    // 1. Let aborted pause be a boolean flag that is true if animation has a pending pause task, and false otherwise.
+    bool abortedPause = hasPendingPauseTask();
+
+    // 2. Let has pending ready promise be a boolean flag that is initially false.
+    bool hasPendingReadyPromise = false;
+
+    // 3. Perform the steps corresponding to the first matching condition from the following, if any:
+    if (m_playbackRate > 0 && autoRewind == AutoRewind::Yes && (!localTime || localTime.value() < 0_s || localTime.value() >= endTime)) {
+        // If animation playback rate > 0, the auto-rewind flag is true and either animation's:
+        //     - current time is unresolved, or
+        //     - current time < zero, or
+        //     - current time ≥ target effect end,
+        // Set animation's hold time to zero.
+        m_holdTime = 0_s;
+    } else if (m_playbackRate < 0 && autoRewind == AutoRewind::Yes && (!localTime || localTime.value() <= 0_s || localTime.value() > endTime)) {
+        // If animation playback rate < 0, the auto-rewind flag is true and either animation's:
+        //     - current time is unresolved, or
+        //     - current time ≤ zero, or
+        //     - current time > target effect end
+        // If target effect end is positive infinity, throw an InvalidStateError and abort these steps.
+        if (endTime == Seconds::infinity())
+            return Exception { InvalidStateError };
+        m_holdTime = endTime;
+    } else if (!m_playbackRate && !localTime) {
+        // If animation playback rate = 0 and animation's current time is unresolved,
+        // Set animation's hold time to zero.
+        m_holdTime = 0_s;
+    }
+
+    // 4. If animation has a pending play task or a pending pause task,
+    if (hasPendingPauseTask()) {
+        // 1. Cancel that task.
+        setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
+        // 2. Set has pending ready promise to true.
+        hasPendingReadyPromise = true;
+    }
+
+    // 5. If animation's hold time is unresolved and aborted pause is false, abort this procedure.
+    if (!m_holdTime && !abortedPause)
+        return { };
+
+    // 6. If animation's hold time is resolved, let its start time be unresolved.
+    if (m_holdTime)
+        setStartTime(std::nullopt);
+
+    // 7. If has pending ready promise is false, let animation's current ready promise be a new (pending) Promise object.
+    if (!hasPendingReadyPromise)
+        m_readyPromise.clear();
+
+    // 8. Schedule a task to run as soon as animation is ready.
+    setTimeToRunPendingPlayTask(TimeToRunPendingTask::WhenReady);
+
+    // 9. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false.
+    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
+
+    return { };
+}
+
+void WebAnimation::setTimeToRunPendingPlayTask(TimeToRunPendingTask timeToRunPendingTask)
+{
+    m_timeToRunPendingPlayTask = timeToRunPendingTask;
+    updatePendingTasks();
+}
+
+void WebAnimation::runPendingPlayTask()
+{
+    // 3.4.10. Playing an animation, step 8.
+    // https://drafts.csswg.org/web-animations-1/#play-an-animation
+
+    m_timeToRunPendingPlayTask = TimeToRunPendingTask::NotScheduled;
+
+    // 1. Let ready time be the time value of the timeline associated with animation at the moment when animation became ready.
+    auto readyTime = m_timeline->currentTime();
+
+    // 2. If animation's start time is unresolved, perform the following steps:
+    if (!startTime()) {
+        // 1. Let new start time be the result of evaluating ready time - hold time / animation playback rate for animation.
+        // If the animation playback rate is zero, let new start time be simply ready time.
+        auto newStartTime = readyTime.value();
+        if (m_playbackRate)
+            newStartTime -= m_holdTime.value() / m_playbackRate;
+        // 2. If animation's playback rate is not 0, make animation's hold time unresolved.
+        if (m_playbackRate)
+            m_holdTime = std::nullopt;
+        // 3. Set the animation start time of animation to new start time.
+        setStartTime(newStartTime);
+    }
+
+    // 3. Resolve animation's current ready promise with animation.
+    m_readyPromise.resolve(*this);
+
+    // 4. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false.
+    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
+}
+
+ExceptionOr<void> WebAnimation::pause()
+{
+    // 3.4.11. Pausing an animation
+    // https://drafts.csswg.org/web-animations-1/#pause-an-animation
+
+    // 1. If animation has a pending pause task, abort these steps.
+    if (hasPendingPauseTask())
+        return { };
+
+    // 2. If the play state of animation is paused, abort these steps.
+    if (playState() == PlayState::Paused)
+        return { };
+
+    auto localTime = currentTime();
+
+    // 3. If the animation's current time is unresolved, perform the steps according to the first matching condition from below:
+    if (!localTime) {
+        if (m_playbackRate >= 0) {
+            // If animation's playback rate is ≥ 0, let animation's hold time be zero.
+            m_holdTime = 0_s;
+        } else if (effectEndTime() == Seconds::infinity()) {
+            // Otherwise, if target effect end for animation is positive infinity, throw an InvalidStateError and abort these steps.
+            return Exception { InvalidStateError };
+        } else {
+            // Otherwise, let animation's hold time be target effect end.
+            m_holdTime = effectEndTime();
+        }
+    }
+
+    // 4. Let has pending ready promise be a boolean flag that is initially false.
+    bool hasPendingReadyPromise = false;
+
+    // 5. If animation has a pending play task, cancel that task and let has pending ready promise be true.
+    if (hasPendingPlayTask()) {
+        setTimeToRunPendingPlayTask(TimeToRunPendingTask::NotScheduled);
+        hasPendingReadyPromise = true;
+    }
+
+    // 6. If has pending ready promise is false, set animation's current ready promise to a new (pending) Promise object.
+    if (!hasPendingReadyPromise)
+        m_readyPromise.clear();
+
+    // 7. Schedule a task to be executed at the first possible moment after the user agent has performed any processing necessary
+    //    to suspend the playback of animation's target effect, if any.
+    setTimeToRunPendingPauseTask(TimeToRunPendingTask::ASAP);
+
+    // 8. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the synchronously notify flag set to false.
+    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
+
+    return { };
+}
+
+void WebAnimation::setTimeToRunPendingPauseTask(TimeToRunPendingTask timeToRunPendingTask)
+{
+    m_timeToRunPendingPauseTask = timeToRunPendingTask;
+    updatePendingTasks();
+}
+
+void WebAnimation::runPendingPauseTask()
+{
+    // 3.4.11. Pausing an animation, step 7.
+    // https://drafts.csswg.org/web-animations-1/#pause-an-animation
+
+    m_timeToRunPendingPauseTask = TimeToRunPendingTask::NotScheduled;
+
+    // 1. Let ready time be the time value of the timeline associated with animation at the moment when the user agent
+    //    completed processing necessary to suspend playback of animation's target effect.
+    auto readyTime = m_timeline->currentTime();
+    auto animationStartTime = startTime();
+
+    // 2. If animation's start time is resolved and its hold time is not resolved, let animation's hold time be the result of
+    //    evaluating (ready time - start time) × playback rate.
+    //    Note: The hold time might be already set if the animation is finished, or if the animation is pending, waiting to begin
+    //    playback. In either case we want to preserve the hold time as we enter the paused state.
+    if (animationStartTime && !m_holdTime)
+        m_holdTime = (readyTime.value() - animationStartTime.value()) * m_playbackRate;
+
+    // 3. Make animation's start time unresolved.
+    setStartTime(std::nullopt);
+
+    // 4. Resolve animation's current ready promise with animation.
+    m_readyPromise.resolve(*this);
+
+    // 5. Run the procedure to update an animation's finished state for animation with the did seek flag set to false, and the
+    //    synchronously notify flag set to false.
+    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
+}
+
+void WebAnimation::updatePendingTasks()
+{
+    if (m_timeToRunPendingPauseTask == TimeToRunPendingTask::ASAP && m_timeline)
+        runPendingPauseTask();
+
+    // FIXME: This should only happen if we're ready, at the moment we think we're ready if we have a timeline.
+    if (m_timeToRunPendingPlayTask == TimeToRunPendingTask::WhenReady && m_timeline)
+        runPendingPlayTask();
+}
+
 Seconds WebAnimation::timeToNextRequiredTick(Seconds timelineTime) const
 {
     if (!m_timeline || !m_startTime || !m_effect || !m_playbackRate)
index e629fc0..4b500f6 100644 (file)
@@ -70,8 +70,7 @@ public:
     enum class PlayState { Idle, Pending, Running, Paused, Finished };
     PlayState playState() const;
 
-    // FIXME: return a live value once we support pending pause and play tasks.
-    bool pending() const { return false; }
+    bool pending() const { return hasPendingPauseTask() || hasPendingPlayTask(); }
 
     using ReadyPromise = DOMPromiseProxyWithResolveCallback<IDLInterface<WebAnimation>>;
     ReadyPromise& ready() { return m_readyPromise; }
@@ -79,6 +78,9 @@ public:
     using FinishedPromise = DOMPromiseProxyWithResolveCallback<IDLInterface<WebAnimation>>;
     FinishedPromise& finished() { return m_finishedPromise; }
 
+    ExceptionOr<void> play();
+    ExceptionOr<void> pause();
+
     Seconds timeToNextRequiredTick(Seconds) const;
     void resolve(RenderStyle&);
     void acceleratedRunningStateDidChange();
@@ -97,6 +99,8 @@ private:
     explicit WebAnimation(Document&);
 
     enum class RespectHoldTime { Yes, No };
+    enum class AutoRewind { Yes, No };
+    enum class TimeToRunPendingTask { NotScheduled, ASAP, WhenReady };
 
     void enqueueAnimationPlaybackEvent(const AtomicString&, std::optional<Seconds>, std::optional<Seconds>);
     Seconds effectEndTime() const;
@@ -106,6 +110,14 @@ private:
     void finishNotificationSteps();
     void scheduleMicrotaskIfNeeded();
     void performMicrotask();
+    void setTimeToRunPendingPauseTask(TimeToRunPendingTask);
+    void setTimeToRunPendingPlayTask(TimeToRunPendingTask);
+    bool hasPendingPauseTask() const { return m_timeToRunPendingPauseTask != TimeToRunPendingTask::NotScheduled; }
+    bool hasPendingPlayTask() const { return m_timeToRunPendingPlayTask != TimeToRunPendingTask::NotScheduled; }
+    void updatePendingTasks();
+    ExceptionOr<void> play(AutoRewind);
+    void runPendingPauseTask();
+    void runPendingPlayTask();
     
     RefPtr<AnimationEffect> m_effect;
     RefPtr<AnimationTimeline> m_timeline;
@@ -118,6 +130,8 @@ private:
     bool m_scheduledMicrotask;
     ReadyPromise m_readyPromise;
     FinishedPromise m_finishedPromise;
+    TimeToRunPendingTask m_timeToRunPendingPlayTask { TimeToRunPendingTask::NotScheduled };
+    TimeToRunPendingTask m_timeToRunPendingPauseTask { TimeToRunPendingTask::NotScheduled };
 
     // ActiveDOMObject.
     const char* activeDOMObjectName() const final;
index a3a8d45..cc53a41 100644 (file)
@@ -48,4 +48,6 @@ enum AnimationPlayState {
     attribute EventHandler onfinish;
     readonly attribute Promise<WebAnimation> ready;
     readonly attribute Promise<WebAnimation> finished;
+    [MayThrowException] void play();
+    [MayThrowException] void pause();
 };