summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-animations/timing-model/animations
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/web-animations/timing-model/animations
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/web-animations/timing-model/animations')
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animations/current-time.html65
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animations/set-the-animation-start-time.html207
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animations/set-the-target-effect-of-an-animation.html95
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animations/set-the-timeline-of-an-animation.html276
-rw-r--r--testing/web-platform/tests/web-animations/timing-model/animations/updating-the-finished-state.html331
5 files changed, 974 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-animations/timing-model/animations/current-time.html b/testing/web-platform/tests/web-animations/timing-model/animations/current-time.html
new file mode 100644
index 000000000..efc7ba78b
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animations/current-time.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for current time</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#current-time">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ animation.play();
+ assert_equals(animation.currentTime, 0,
+ 'Current time returns the hold time set when entering the play-pending ' +
+ 'state');
+}, 'The current time returns the hold time when set');
+
+promise_test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+
+ return animation.ready.then(function() {
+ assert_equals(animation.currentTime, null);
+ });
+}, 'The current time is unresolved when there is no associated timeline ' +
+ '(and no hold time is set)');
+
+// FIXME: Test that the current time is unresolved when we have an inactive
+// timeline if we find a way of creating an inactive timeline!
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ animation.startTime = null;
+ assert_equals(animation.currentTime, null);
+}, 'The current time is unresolved when the start time is unresolved ' +
+ '(and no hold time is set)');
+
+promise_test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ animation.playbackRate = 2;
+ animation.startTime = document.timeline.currentTime - 25 * MS_PER_SEC;
+
+ var timelineTime = document.timeline.currentTime;
+ var startTime = animation.startTime;
+ var playbackRate = animation.playbackRate;
+ assert_times_equal(animation.currentTime,
+ (timelineTime - startTime) * playbackRate,
+ 'Animation has a unresolved start time');
+}, 'The current time is calculated from the timeline time, start time and ' +
+ 'playback rate');
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animations/set-the-animation-start-time.html b/testing/web-platform/tests/web-animations/timing-model/animations/set-the-animation-start-time.html
new file mode 100644
index 000000000..8b74c92a4
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animations/set-the-animation-start-time.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Setting the start time tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#set-the-animation-start-time">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t)
+{
+ // It should only be possible to set *either* the start time or the current
+ // time for an animation that does not have an active timeline.
+
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+
+ assert_equals(animation.currentTime, null, 'Intial current time');
+ assert_equals(animation.startTime, null, 'Intial start time');
+
+ animation.currentTime = 1000;
+ assert_equals(animation.currentTime, 1000,
+ 'Setting the current time succeeds');
+ assert_equals(animation.startTime, null,
+ 'Start time remains null after setting current time');
+
+ animation.startTime = 1000;
+ assert_equals(animation.startTime, 1000,
+ 'Setting the start time succeeds');
+ assert_equals(animation.currentTime, null,
+ 'Setting the start time clears the current time');
+
+ animation.startTime = null;
+ assert_equals(animation.startTime, null,
+ 'Setting the start time to an unresolved time succeeds');
+ assert_equals(animation.currentTime, null, 'The current time is unaffected');
+
+}, 'Setting the start time of an animation without an active timeline');
+
+test(function(t)
+{
+ // Setting an unresolved start time on an animation without an active
+ // timeline should not clear the current time.
+
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+
+ assert_equals(animation.currentTime, null, 'Intial current time');
+ assert_equals(animation.startTime, null, 'Intial start time');
+
+ animation.currentTime = 1000;
+ assert_equals(animation.currentTime, 1000,
+ 'Setting the current time succeeds');
+ assert_equals(animation.startTime, null,
+ 'Start time remains null after setting current time');
+
+ animation.startTime = null;
+ assert_equals(animation.startTime, null, 'Start time remains unresolved');
+ assert_equals(animation.currentTime, 1000, 'Current time is unaffected');
+
+}, 'Setting an unresolved start time an animation without an active timeline'
+ + ' does not clear the current time');
+
+test(function(t)
+{
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ // So long as a hold time is set, querying the current time will return
+ // the hold time.
+
+ // Since the start time is unresolved at this point, setting the current time
+ // will set the hold time
+ animation.currentTime = 1000;
+ assert_equals(animation.currentTime, 1000,
+ 'The current time is calculated from the hold time');
+
+ // If we set the start time, however, we should clear the hold time.
+ animation.startTime = document.timeline.currentTime - 2000;
+ assert_times_equal(animation.currentTime, 2000,
+ 'The current time is calculated from the start time,'
+ + ' not the hold time');
+
+ // Sanity check
+ assert_equals(animation.playState, 'running',
+ 'Animation reports it is running after setting a resolved'
+ + ' start time');
+}, 'Setting the start time clears the hold time');
+
+test(function(t)
+{
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ // Set up a running animation (i.e. both start time and current time
+ // are resolved).
+ animation.startTime = document.timeline.currentTime - 1000;
+ assert_equals(animation.playState, 'running');
+ assert_times_equal(animation.currentTime, 1000,
+ 'Current time is resolved for a running animation')
+
+ // Clear start time
+ animation.startTime = null;
+ assert_times_equal(animation.currentTime, 1000,
+ 'Hold time is set after start time is made unresolved');
+ assert_equals(animation.playState, 'paused',
+ 'Animation reports it is paused after setting an unresolved'
+ + ' start time');
+}, 'Setting an unresolved start time sets the hold time');
+
+promise_test(function(t)
+{
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ var readyPromiseCallbackCalled = false;
+ animation.ready.then(function() { readyPromiseCallbackCalled = true; } );
+
+ // Put the animation in the play-pending state
+ animation.play();
+
+ // Sanity check
+ assert_equals(animation.playState, 'pending',
+ 'Animation is in play-pending state');
+
+ // Setting the start time should resolve the 'ready' promise, i.e.
+ // it should schedule a microtask to run the promise callbacks.
+ animation.startTime = document.timeline.currentTime;
+ assert_false(readyPromiseCallbackCalled,
+ 'Ready promise callback is not called synchronously');
+
+ // If we schedule another microtask then it should run immediately after
+ // the ready promise resolution microtask.
+ return Promise.resolve().then(function() {
+ assert_true(readyPromiseCallbackCalled,
+ 'Ready promise callback called after setting startTime');
+ });
+}, 'Setting the start time resolves a pending ready promise');
+
+promise_test(function(t)
+{
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ var readyPromiseCallbackCalled = false;
+ animation.ready.then(function() { readyPromiseCallbackCalled = true; } );
+
+ // Put the animation in the pause-pending state
+ animation.startTime = document.timeline.currentTime;
+ animation.pause();
+
+ // Sanity check
+ assert_equals(animation.playState, 'pending',
+ 'Animation is in pause-pending state');
+
+ // Setting the start time should resolve the 'ready' promise although
+ // the resolution callbacks when be run in a separate microtask.
+ animation.startTime = null;
+ assert_false(readyPromiseCallbackCalled,
+ 'Ready promise callback is not called synchronously');
+
+ return Promise.resolve().then(function() {
+ assert_true(readyPromiseCallbackCalled,
+ 'Ready promise callback called after setting startTime');
+ });
+}, 'Setting the start time resolves a pending pause task');
+
+promise_test(function(t)
+{
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+
+ // Set start time such that the current time is past the end time
+ animation.startTime = document.timeline.currentTime
+ - 110 * MS_PER_SEC;
+ assert_equals(animation.playState, 'finished',
+ 'Seeked to finished state using the startTime');
+
+ // If the 'did seek' flag is true, the current time should be greater than
+ // the effect end.
+ assert_greater_than(animation.currentTime,
+ animation.effect.getComputedTiming().endTime,
+ 'Setting the start time updated the finished state with'
+ + ' the \'did seek\' flag set to true');
+
+ // Furthermore, that time should persist if we have correctly updated
+ // the hold time
+ var finishedCurrentTime = animation.currentTime;
+ return waitForAnimationFrames(1).then(function() {
+ assert_equals(animation.currentTime, finishedCurrentTime,
+ 'Current time does not change after seeking past the effect'
+ + ' end time by setting the current time');
+ });
+}, 'Setting the start time updates the finished state');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animations/set-the-target-effect-of-an-animation.html b/testing/web-platform/tests/web-animations/timing-model/animations/set-the-target-effect-of-an-animation.html
new file mode 100644
index 000000000..4c51f0141
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animations/set-the-target-effect-of-an-animation.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Setting the target effect tests</title>
+<link rel='help' href='https://w3c.github.io/web-animations/#setting-the-target-effect'>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='../../testcommon.js'></script>
+<body>
+<div id='log'></div>
+<script>
+'use strict';
+
+promise_test(function(t) {
+ var anim = createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
+ 100 * MS_PER_SEC);
+ assert_equals(anim.playState, 'pending');
+
+ var retPromise = anim.ready.then(function() {
+ assert_unreached('ready promise is fulfilled');
+ }).catch(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'ready promise is rejected with AbortError');
+ });
+
+ anim.effect = null;
+ assert_equals(anim.playState, 'paused');
+
+ return retPromise;
+}, 'If new effect is null and old effect is not null, we reset the pending ' +
+ 'tasks and ready promise is rejected');
+
+promise_test(function(t) {
+ var anim = new Animation();
+ anim.pause();
+ assert_equals(anim.playState, 'pending');
+
+ anim.effect = new KeyframeEffectReadOnly(createDiv(t),
+ { marginLeft: [ '0px', '100px' ] },
+ 100 * MS_PER_SEC);
+ assert_equals(anim.playState, 'pending');
+
+ return anim.ready.then(function() {
+ assert_equals(anim.playState, 'paused');
+ });
+}, 'If animation has a pending pause task, reschedule that task to run ' +
+ 'as soon as animation is ready.');
+
+promise_test(function(t) {
+ var anim = new Animation();
+ anim.play();
+ assert_equals(anim.playState, 'pending');
+
+ anim.effect = new KeyframeEffectReadOnly(createDiv(t),
+ { marginLeft: [ '0px', '100px' ] },
+ 100 * MS_PER_SEC);
+ assert_equals(anim.playState, 'pending');
+
+ return anim.ready.then(function() {
+ assert_equals(anim.playState, 'running');
+ });
+}, 'If animation has a pending play task, reschedule that task to run ' +
+ 'as soon as animation is ready to play new effect.');
+
+promise_test(function(t) {
+ var animA = createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
+ 100 * MS_PER_SEC);
+ var animB = new Animation();
+
+ return animA.ready.then(function() {
+ animB.effect = animA.effect;
+ assert_equals(animA.effect, null);
+ assert_equals(animA.playState, 'finished');
+ });
+}, '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.');
+
+test(function(t) {
+ var animA = createDiv(t).animate({ marginLeft: [ '0px', '100px' ] },
+ 100 * MS_PER_SEC);
+ var animB = new Animation();
+ var effect = animA.effect;
+ animA.currentTime = 50 * MS_PER_SEC;
+ animB.currentTime = 20 * MS_PER_SEC;
+ assert_equals(effect.getComputedTiming().progress, 0.5,
+ 'Original timing comes from first animation');
+ animB.effect = effect;
+ assert_equals(effect.getComputedTiming().progress, 0.2,
+ 'After setting the effect on a different animation, ' +
+ 'it uses the new animation\'s timing');
+}, '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.');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animations/set-the-timeline-of-an-animation.html b/testing/web-platform/tests/web-animations/timing-model/animations/set-the-timeline-of-an-animation.html
new file mode 100644
index 000000000..c540fe2ca
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animations/set-the-timeline-of-an-animation.html
@@ -0,0 +1,276 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Setting the timeline tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#setting-the-timeline">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+// ---------------------------------------------------------------------
+//
+// Tests from no timeline to timeline
+//
+// ---------------------------------------------------------------------
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.currentTime = 50 * MS_PER_SEC;
+ assert_equals(animation.playState, 'paused');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'paused');
+ assert_times_equal(animation.currentTime, 50 * MS_PER_SEC);
+}, 'After setting timeline on paused animation it is still paused');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.currentTime = 200 * MS_PER_SEC;
+ assert_equals(animation.playState, 'paused');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'paused');
+ assert_times_equal(animation.currentTime, 200 * MS_PER_SEC);
+}, 'After setting timeline on animation paused outside active interval'
+ + ' it is still paused');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ assert_equals(animation.playState, 'idle');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'idle');
+}, 'After setting timeline on an idle animation without a start time'
+ + ' it is still idle');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.startTime = document.timeline.currentTime;
+ assert_equals(animation.playState, 'idle');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'running');
+}, 'After setting timeline on an idle animation with a start time'
+ + ' it is running');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.startTime = document.timeline.currentTime - 200 * MS_PER_SEC;
+ assert_equals(animation.playState, 'idle');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'finished');
+}, 'After setting timeline on an idle animation with a sufficiently ancient'
+ + ' start time it is finished');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.play();
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'pending');
+}, 'After setting timeline on a play-pending animation it is still pending');
+
+promise_test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.play();
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = document.timeline;
+
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'running');
+ });
+}, 'After setting timeline on a play-pending animation it begins playing'
+ + ' after pending');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.startTime = document.timeline.currentTime;
+ animation.pause();
+ animation.timeline = null;
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'pending');
+}, 'After setting timeline on a pause-pending animation it is still pending');
+
+promise_test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ null);
+ animation.startTime = document.timeline.currentTime;
+ animation.pause();
+ animation.timeline = null;
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = document.timeline;
+
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'paused');
+ });
+}, 'After setting timeline on a pause-pending animation it becomes paused'
+ + ' after pending');
+
+// ---------------------------------------------------------------------
+//
+// Tests from timeline to no timeline
+//
+// ---------------------------------------------------------------------
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ animation.currentTime = 50 * MS_PER_SEC;
+ assert_equals(animation.playState, 'paused');
+
+ animation.timeline = null;
+
+ assert_equals(animation.playState, 'paused');
+ assert_times_equal(animation.currentTime, 50 * MS_PER_SEC);
+}, 'After clearing timeline on paused animation it is still paused');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ var initialStartTime = document.timeline.currentTime - 200 * MS_PER_SEC;
+ animation.startTime = initialStartTime;
+ assert_equals(animation.playState, 'finished');
+
+ animation.timeline = null;
+
+ assert_equals(animation.playState, 'idle');
+ assert_times_equal(animation.startTime, initialStartTime);
+}, 'After clearing timeline on finished animation it is idle');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ var initialStartTime = document.timeline.currentTime - 50 * MS_PER_SEC;
+ animation.startTime = initialStartTime;
+ assert_equals(animation.playState, 'running');
+
+ animation.timeline = null;
+
+ assert_equals(animation.playState, 'idle');
+ assert_times_equal(animation.startTime, initialStartTime);
+}, 'After clearing timeline on running animation it is idle');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ assert_equals(animation.playState, 'idle');
+
+ animation.timeline = null;
+
+ assert_equals(animation.playState, 'idle');
+ assert_equals(animation.startTime, null);
+}, 'After clearing timeline on idle animation it is still idle');
+
+test(function(t) {
+ var animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = null;
+
+ assert_equals(animation.playState, 'pending');
+}, 'After clearing timeline on play-pending animation it is still pending');
+
+promise_test(function(t) {
+ var animation = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = null;
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'pending');
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'running');
+ });
+}, 'After clearing and re-setting timeline on play-pending animation it'
+ + ' begins to play');
+
+test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ animation.startTime = document.timeline.currentTime;
+ animation.pause();
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = null;
+
+ assert_equals(animation.playState, 'pending');
+}, 'After clearing timeline on a pause-pending animation it is still pending');
+
+promise_test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ animation.startTime = document.timeline.currentTime;
+ animation.pause();
+ assert_equals(animation.playState, 'pending');
+
+ animation.timeline = null;
+ animation.timeline = document.timeline;
+
+ assert_equals(animation.playState, 'pending');
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'paused');
+ });
+}, 'After clearing and re-setting timeline on a pause-pending animation it'
+ + ' becomes paused');
+
+promise_test(function(t) {
+ var animation =
+ new Animation(new KeyframeEffect(createDiv(t), null, 100 * MS_PER_SEC),
+ document.timeline);
+ var initialStartTime = document.timeline.currentTime - 50 * MS_PER_SEC;
+ animation.startTime = initialStartTime;
+ animation.pause();
+ animation.play();
+
+ animation.timeline = null;
+ animation.timeline = document.timeline;
+ assert_equals(animation.playState, 'pending');
+
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'running');
+ assert_times_equal(animation.startTime, initialStartTime);
+ });
+}, 'After clearing and re-setting timeline on an animation in the middle of'
+ + ' an aborted pause, it continues playing using the same start time');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/web-animations/timing-model/animations/updating-the-finished-state.html b/testing/web-platform/tests/web-animations/timing-model/animations/updating-the-finished-state.html
new file mode 100644
index 000000000..0b77443f2
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/timing-model/animations/updating-the-finished-state.html
@@ -0,0 +1,331 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for updating the finished state of an animation</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#updating-the-finished-state">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+//
+// NOTE TO THE POOR PERSON WHO HAS TO MERGE THIS WITH THE TEST OF THE SAME
+// NAME FROM BLINK
+//
+// There is a pull request from Blink at:
+//
+// https://github.com/w3c/web-platform-tests/pull/3328
+//
+// which this file will surely conflict with.
+//
+// However, those tests cover a different part of the same algorithm. They
+// are mostly concerned with testing events and promises rather than the
+// timing part of the algorithm.
+//
+// The tests below cover the first part of the algorithm. So, please keep both
+// sets of tests and delete this comment. Preferably put the tests in this
+// file first.
+//
+// Thank you!
+//
+
+
+// CASE 1: playback rate > 0 and current time >= target effect end
+// (Also the start time is resolved and there is pending task)
+
+// Did seek = false
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+
+ // Here and in the following tests we wait until ready resolves as
+ // otherwise we don't have a resolved start time. We test the case
+ // where the start time is unresolved in a subsequent test.
+ return anim.ready.then(function() {
+ // Seek to 1ms before the target end and then wait 1ms
+ anim.currentTime = 100 * MS_PER_SEC - 1;
+ return waitForAnimationFramesWithDelay(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 100 * MS_PER_SEC,
+ 'Hold time is set to target end clamping current time');
+ });
+}, 'Updating the finished state when playing past end');
+
+// Did seek = true
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ return anim.ready.then(function() {
+ anim.currentTime = 200 * MS_PER_SEC;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 200 * MS_PER_SEC,
+ 'Hold time is set so current time should NOT change');
+ });
+}, 'Updating the finished state when seeking past end');
+
+// Test current time == target end
+//
+// We can't really write a test for current time == target end with
+// did seek = false since that would imply setting up an animation where
+// the next animation frame time happens to exactly align with the target end.
+//
+// Fortunately, we don't need to test that case since even if the implementation
+// fails to set the hold time on such a tick, it should be mostly unobservable
+// (on the subsequent tick the hold time will be set to the same value anyway).
+
+// Did seek = true
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ return anim.ready.then(function() {
+ anim.currentTime = 100 * MS_PER_SEC;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 100 * MS_PER_SEC,
+ 'Hold time is set so current time should NOT change');
+ });
+}, 'Updating the finished state when seeking exactly to end');
+
+
+// CASE 2: playback rate < 0 and current time <= 0
+// (Also the start time is resolved and there is pending task)
+
+// Did seek = false
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = -1;
+ anim.play(); // Make sure animation is not initially finished
+ return anim.ready.then(function() {
+ // Seek to 1ms before 0 and then wait 1ms
+ anim.currentTime = 1;
+ return waitForAnimationFramesWithDelay(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 0 * MS_PER_SEC,
+ 'Hold time is set to zero clamping current time');
+ });
+}, 'Updating the finished state when playing in reverse past zero');
+
+// Did seek = true
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = -1;
+ anim.play();
+ return anim.ready.then(function() {
+ anim.currentTime = -100 * MS_PER_SEC;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, -100 * MS_PER_SEC,
+ 'Hold time is set so current time should NOT change');
+ });
+}, 'Updating the finished state when seeking a reversed animation past zero');
+
+// As before, it's difficult to test current time == 0 for did seek = false but
+// it doesn't really matter.
+
+// Did seek = true
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = -1;
+ anim.play();
+ return anim.ready.then(function() {
+ anim.currentTime = 0;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 0 * MS_PER_SEC,
+ 'Hold time is set so current time should NOT change');
+ });
+}, 'Updating the finished state when seeking a reversed animation exactly'
+ + ' to zero');
+
+// CASE 3: playback rate > 0 and current time < target end OR
+// playback rate < 0 and current time > 0
+// (Also the start time is resolved and there is pending task)
+
+// Did seek = false; playback rate > 0
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+
+ // We want to test that the hold time is cleared so first we need to
+ // put the animation in a state where the hold time is set.
+ anim.finish();
+ return anim.ready.then(function() {
+ assert_equals(anim.currentTime, 100 * MS_PER_SEC,
+ 'Hold time is initially set');
+ // Then extend the duration so that the hold time is cleared and on
+ // the next tick the current time will increase.
+ anim.effect.timing.duration *= 2;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_greater_than(anim.currentTime, 100 * MS_PER_SEC,
+ 'Hold time is not set so current time should increase');
+ });
+}, 'Updating the finished state when playing before end');
+
+// Did seek = true; playback rate > 0
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.finish();
+ return anim.ready.then(function() {
+ anim.currentTime = 50 * MS_PER_SEC;
+ // When did seek = true, updating the finished state: (i) updates
+ // the animation's start time and (ii) clears the hold time.
+ // We can test both by checking that the currentTime is initially
+ // updated and then increases.
+ assert_equals(anim.currentTime, 50 * MS_PER_SEC, 'Start time is updated');
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_greater_than(anim.currentTime, 50 * MS_PER_SEC,
+ 'Hold time is not set so current time should increase');
+ });
+}, 'Updating the finished state when seeking before end');
+
+// Did seek = false; playback rate < 0
+//
+// Unfortunately it is not possible to test this case. We need to have
+// a hold time set, a resolved start time, and then perform some
+// operation that updates the finished state with did seek set to true.
+//
+// However, the only situation where this could arrive is when we
+// replace the timeline and that procedure is likely to change. For all
+// other cases we either have an unresolved start time (e.g. when
+// paused), we don't have a set hold time (e.g. regular playback), or
+// the current time is zero (and anything that gets us out of that state
+// will set did seek = true).
+
+// Did seek = true; playback rate < 0
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = -1;
+ return anim.ready.then(function() {
+ anim.currentTime = 50 * MS_PER_SEC;
+ assert_equals(anim.currentTime, 50 * MS_PER_SEC, 'Start time is updated');
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_less_than(anim.currentTime, 50 * MS_PER_SEC,
+ 'Hold time is not set so current time should decrease');
+ });
+}, 'Updating the finished state when seeking a reversed animation before end');
+
+// CASE 4: playback rate == 0
+
+// current time < 0
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = 0;
+ return anim.ready.then(function() {
+ anim.currentTime = -100 * MS_PER_SEC;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, -100 * MS_PER_SEC,
+ 'Hold time should not be cleared so current time should'
+ + ' NOT change');
+ });
+}, 'Updating the finished state when playback rate is zero and the'
+ + ' current time is less than zero');
+
+// current time < target end
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = 0;
+ return anim.ready.then(function() {
+ anim.currentTime = 50 * MS_PER_SEC;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 50 * MS_PER_SEC,
+ 'Hold time should not be cleared so current time should'
+ + ' NOT change');
+ });
+}, 'Updating the finished state when playback rate is zero and the'
+ + ' current time is less than end');
+
+// current time > target end
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.playbackRate = 0;
+ return anim.ready.then(function() {
+ anim.currentTime = 200 * MS_PER_SEC;
+ return waitForAnimationFrames(1);
+ }).then(function() {
+ assert_equals(anim.currentTime, 200 * MS_PER_SEC,
+ 'Hold time should not be cleared so current time should'
+ + ' NOT change');
+ });
+}, 'Updating the finished state when playback rate is zero and the'
+ + ' current time is greater than end');
+
+// CASE 5: current time unresolved
+
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.cancel();
+ // Trigger a change that will cause the "update the finished state"
+ // procedure to run.
+ anim.effect.timing.duration = 200 * MS_PER_SEC;
+ assert_equals(anim.currentTime, null,
+ 'The animation hold time / start time should not be updated');
+ // The "update the finished state" procedure is supposed to run after any
+ // change to timing, but just in case an implementation defers that, let's
+ // wait a frame and check that the hold time / start time has still not been
+ // updated.
+ return waitForAnimationFrames(1).then(function() {
+ assert_equals(anim.currentTime, null,
+ 'The animation hold time / start time should not be updated');
+ });
+}, 'Updating the finished state when current time is unresolved');
+
+// CASE 6: has a pending task
+
+test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.cancel();
+ anim.currentTime = 75 * MS_PER_SEC;
+ anim.play();
+ // We now have a pending task and a resolved current time.
+ //
+ // In the next step we will adjust the timing so that the current time
+ // is greater than the target end. At this point the "update the finished
+ // state" procedure should run and if we fail to check for a pending task
+ // we will set the hold time to the target end, i.e. 50ms.
+ anim.effect.timing.duration = 50 * MS_PER_SEC;
+ assert_equals(anim.currentTime, 75 * MS_PER_SEC,
+ 'Hold time should not be updated');
+}, 'Updating the finished state when there is a pending task');
+
+// CASE 7: start time unresolved
+
+// Did seek = false
+promise_test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.cancel();
+ // Make it so that only the start time is unresolved (to avoid overlapping
+ // with the test case where current time is unresolved)
+ anim.currentTime = 150 * MS_PER_SEC;
+ // Trigger a change that will cause the "update the finished state"
+ // procedure to run (did seek = false).
+ anim.effect.timing.duration = 200 * MS_PER_SEC;
+ return waitForAnimationFrames(1).then(function() {
+ assert_equals(anim.currentTime, 150 * MS_PER_SEC,
+ 'The animation hold time should not be updated');
+ assert_equals(anim.startTime, null,
+ 'The animation start time should not be updated');
+ });
+}, 'Updating the finished state when start time is unresolved and'
+ + ' did seek = false');
+
+// Did seek = true
+test(function(t) {
+ var anim = createDiv(t).animate(null, 100 * MS_PER_SEC);
+ anim.cancel();
+ anim.currentTime = 150 * MS_PER_SEC;
+ // Trigger a change that will cause the "update the finished state"
+ // procedure to run.
+ anim.currentTime = 50 * MS_PER_SEC;
+ assert_equals(anim.currentTime, 50 * MS_PER_SEC,
+ 'The animation hold time should not be updated');
+ assert_equals(anim.startTime, null,
+ 'The animation start time should not be updated');
+}, 'Updating the finished state when start time is unresolved and'
+ + ' did seek = true');
+
+</script>
+</body>