We have already resolved to make calling Finish() clear the pause state (see https://lists.w3.org/Archives/Public/public-fx/2015AprJun/0038.html, item 2). Doing that involves resolving the start time when the animation is paused. Furthermore, as a separate change, we resolved to make the finished promise not resolve when the animation is paused. That suggests making UpdateFinishedState() only resolve the finished promise when PlayState() == Finished rather than using IsFinished() which returns true even if the animation is paused. However, if we compare PlayState() == Finished in UpdateFinishedState() then we will *not* resolve the finished promise when the animation is play-pending since PlayState() == Pending in that case (pause-pending is ok since the call to SetCurrentTime will cause a transition to the Paused state). Furthermore, the existing call to cancel the pending play task will effectively leave this animation forever pending. Hence, in this patch we unconditionally fill in the start time.
245 lines
7.8 KiB
HTML
245 lines
7.8 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<script src="../testcommon.js"></script>
|
|
<style>
|
|
|
|
.animated-div {
|
|
margin-left: 10px;
|
|
}
|
|
|
|
@keyframes anim {
|
|
from { margin-left: 100px; }
|
|
to { margin-left: 200px; }
|
|
}
|
|
|
|
</style>
|
|
<body>
|
|
<script>
|
|
|
|
'use strict';
|
|
|
|
const ANIM_PROP_VAL = 'anim 100s';
|
|
const ANIM_DURATION = 100000; // ms
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.playbackRate = 0;
|
|
|
|
var threw = false;
|
|
try {
|
|
animation.finish();
|
|
} catch (e) {
|
|
threw = true;
|
|
assert_equals(e.name, 'InvalidStateError',
|
|
'Exception should be an InvalidStateError exception when ' +
|
|
'trying to finish an animation with playbackRate == 0');
|
|
}
|
|
assert_true(threw,
|
|
'Expect InvalidStateError exception trying to finish an ' +
|
|
'animation with playbackRate == 0');
|
|
}, 'Test exceptions when finishing non-running animation');
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
div.style.animationIterationCount = 'infinite';
|
|
var animation = div.getAnimations()[0];
|
|
|
|
var threw = false;
|
|
try {
|
|
animation.finish();
|
|
} catch (e) {
|
|
threw = true;
|
|
assert_equals(e.name, 'InvalidStateError',
|
|
'Exception should be an InvalidStateError exception when ' +
|
|
'trying to finish an infinite animation');
|
|
}
|
|
assert_true(threw,
|
|
'Expect InvalidStateError exception trying to finish an ' +
|
|
'infinite animation');
|
|
}, 'Test exceptions when finishing infinite animation');
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.finish();
|
|
assert_equals(animation.currentTime, ANIM_DURATION,
|
|
'After finishing, the currentTime should be set to the end ' +
|
|
'of the active duration');
|
|
}, 'Test finishing of animation');
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.currentTime = ANIM_DURATION + 1000; // 1s past effect end
|
|
|
|
animation.finish();
|
|
assert_equals(animation.currentTime, ANIM_DURATION,
|
|
'After finishing, the currentTime should be set back to the ' +
|
|
'end of the active duration');
|
|
}, 'Test finishing of animation with a current time past the effect end');
|
|
|
|
async_test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.currentTime = ANIM_DURATION;
|
|
|
|
animation.finished.then(t.step_func(function() {
|
|
animation.playbackRate = -1;
|
|
animation.finish();
|
|
assert_equals(animation.currentTime, 0,
|
|
'After finishing a reversed animation the currentTime ' +
|
|
'should be set to zero');
|
|
t.done();
|
|
}));
|
|
}, 'Test finishing of reversed animation');
|
|
|
|
async_test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.currentTime = ANIM_DURATION;
|
|
|
|
animation.finished.then(t.step_func(function() {
|
|
animation.playbackRate = -1;
|
|
|
|
animation.currentTime = -1000;
|
|
|
|
animation.finish();
|
|
assert_equals(animation.currentTime, 0,
|
|
'After finishing a reversed animation the currentTime ' +
|
|
'should be set back to zero');
|
|
t.done();
|
|
}));
|
|
}, 'Test finishing of reversed animation with a current time less than zero');
|
|
|
|
async_test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL + ' paused';
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.ready.then(t.step_func(function() {
|
|
animation.finish();
|
|
assert_equals(animation.playState, 'finished',
|
|
'The play state of a paused animation should become ' +
|
|
'"finished" after finish() is called');
|
|
assert_approx_equals(animation.startTime,
|
|
animation.timeline.currentTime - ANIM_DURATION,
|
|
0.0001,
|
|
'The start time of a paused animation should be set ' +
|
|
'after calling finish()');
|
|
t.done();
|
|
}));
|
|
}, 'Test finish() while paused');
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL + ' paused';
|
|
var animation = div.getAnimations()[0];
|
|
|
|
// Update playbackRate so we can test that the calculated startTime
|
|
// respects it
|
|
animation.playbackRate = 2;
|
|
|
|
// While animation is still pause-pending call finish()
|
|
animation.finish();
|
|
|
|
assert_equals(animation.playState, 'finished',
|
|
'The play state of a pause-pending animation should become ' +
|
|
'"finished" after finish() is called');
|
|
assert_approx_equals(animation.startTime,
|
|
animation.timeline.currentTime - ANIM_DURATION / 2,
|
|
0.0001,
|
|
'The start time of a pause-pending animation should ' +
|
|
'be set after calling finish()');
|
|
}, 'Test finish() while pause-pending with positive playbackRate');
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL + ' paused';
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.playbackRate = -2;
|
|
animation.finish();
|
|
|
|
assert_equals(animation.playState, 'finished',
|
|
'The play state of a pause-pending animation should become ' +
|
|
'"finished" after finish() is called');
|
|
assert_equals(animation.startTime, animation.timeline.currentTime,
|
|
'The start time of a pause-pending animation should be ' +
|
|
'set after calling finish()');
|
|
}, 'Test finish() while pause-pending with negative playbackRate');
|
|
|
|
test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.playbackRate = 0.5;
|
|
animation.finish();
|
|
|
|
assert_equals(animation.playState, 'finished',
|
|
'The play state of a play-pending animation should become ' +
|
|
'"finished" after finish() is called');
|
|
assert_approx_equals(animation.startTime,
|
|
animation.timeline.currentTime - ANIM_DURATION / 0.5,
|
|
0.0001,
|
|
'The start time of a play-pending animation should ' +
|
|
'be set after calling finish()');
|
|
}, 'Test finish() while play-pending');
|
|
|
|
// FIXME: Add a test for when we are play-pending without an active timeline.
|
|
// - In that case even after calling finish() we should still be pending but
|
|
// the current time should be updated
|
|
|
|
async_test(function(t) {
|
|
var div = addDiv(t);
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.ready.then(t.step_func(function() {
|
|
// Trigger pause
|
|
animation.pause();
|
|
// Then abort
|
|
animation.play();
|
|
|
|
// We are now in the unusual situation of being play-pending whilst having
|
|
// a resolved start time. Check that finish() still triggers a transition
|
|
// to the finished state immediately.
|
|
animation.finish();
|
|
assert_equals(animation.playState, 'finished',
|
|
'After aborting a pause then calling finish() the play ' +
|
|
'state of an animation should become "finished" immediately');
|
|
t.done();
|
|
}));
|
|
}, 'Test finish() during aborted pause');
|
|
|
|
async_test(function(t) {
|
|
var div = addDiv(t, {'class': 'animated-div'});
|
|
div.style.animation = ANIM_PROP_VAL;
|
|
var animation = div.getAnimations()[0];
|
|
|
|
animation.ready.then(t.step_func(function() {
|
|
animation.finish();
|
|
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
|
assert_equals(marginLeft, 10,
|
|
'The computed style should be reset when finish() is ' +
|
|
'called');
|
|
t.done();
|
|
}));
|
|
}, 'Test resetting of computed style');
|
|
|
|
done();
|
|
</script>
|
|
</body>
|