зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1159743. Stop forcing the dom.animations-api.core.enabled preference on in the test harness. r=birtles
--HG-- rename : dom/animation/test/css-animations/test_animation-cancel.html => dom/animation/test/css-animations/file_animation-cancel.html rename : dom/animation/test/css-animations/test_animation-currenttime.html => dom/animation/test/css-animations/file_animation-currenttime.html rename : dom/animation/test/css-animations/test_animation-finish.html => dom/animation/test/css-animations/file_animation-finish.html rename : dom/animation/test/css-animations/test_animation-finished.html => dom/animation/test/css-animations/file_animation-finished.html rename : dom/animation/test/css-animations/test_animation-pausing.html => dom/animation/test/css-animations/file_animation-pausing.html rename : dom/animation/test/css-animations/test_animation-playstate.html => dom/animation/test/css-animations/file_animation-playstate.html rename : dom/animation/test/css-animations/test_animation-ready.html => dom/animation/test/css-animations/file_animation-ready.html rename : dom/animation/test/css-animations/test_animation-starttime.html => dom/animation/test/css-animations/file_animation-starttime.html rename : dom/animation/test/css-animations/test_animations-dynamic-changes.html => dom/animation/test/css-animations/file_animations-dynamic-changes.html rename : dom/animation/test/css-animations/test_effect-name.html => dom/animation/test/css-animations/file_effect-name.html rename : dom/animation/test/css-animations/test_effect-target.html => dom/animation/test/css-animations/file_effect-target.html rename : dom/animation/test/css-animations/test_element-get-animations.html => dom/animation/test/css-animations/file_element-get-animations.html rename : dom/animation/test/css-transitions/test_animation-cancel.html => dom/animation/test/css-transitions/file_animation-cancel.html rename : dom/animation/test/css-transitions/test_animation-currenttime.html => dom/animation/test/css-transitions/file_animation-currenttime.html rename : dom/animation/test/css-transitions/test_animation-finished.html => dom/animation/test/css-transitions/file_animation-finished.html rename : dom/animation/test/css-transitions/test_animation-pausing.html => dom/animation/test/css-transitions/file_animation-pausing.html rename : dom/animation/test/css-transitions/test_animation-ready.html => dom/animation/test/css-transitions/file_animation-ready.html rename : dom/animation/test/css-transitions/test_animation-starttime.html => dom/animation/test/css-transitions/file_animation-starttime.html rename : dom/animation/test/css-transitions/test_effect-name.html => dom/animation/test/css-transitions/file_effect-name.html rename : dom/animation/test/css-transitions/test_effect-target.html => dom/animation/test/css-transitions/file_effect-target.html rename : dom/animation/test/css-transitions/test_element-get-animations.html => dom/animation/test/css-transitions/file_element-get-animations.html rename : dom/animation/test/document-timeline/test_document-timeline.html => dom/animation/test/document-timeline/file_document-timeline.html rename : dom/animation/test/mozilla/test_deferred_start.html => dom/animation/test/mozilla/file_deferred_start.html rename : layout/style/test/test_animations_pausing.html => layout/style/test/file_animations_pausing.html
This commit is contained in:
Родитель
ecba4cbc0c
Коммит
5844aafa2d
|
@ -0,0 +1,164 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes translateAnim {
|
||||
to { transform: translate(100px) }
|
||||
}
|
||||
@keyframes marginLeftAnim {
|
||||
to { margin-left: 100px }
|
||||
}
|
||||
@keyframes marginLeftAnim100To200 {
|
||||
from { margin-left: 100px }
|
||||
to { margin-left: 200px }
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: translateAnim 100s' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(getComputedStyle(div).transform, 'none',
|
||||
'transform style is animated before cancelling');
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).transform, 'none',
|
||||
'transform style is no longer animated after cancelling');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animated style is cleared after cancelling a running CSS animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: translateAnim 100s forwards' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.finish();
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(getComputedStyle(div).transform, 'none',
|
||||
'transform style is filling before cancelling');
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).transform, 'none',
|
||||
'fill style is cleared after cancelling');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animated style is cleared after cancelling a filling CSS animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: translateAnim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_unreached('Got unexpected end event on cancelled animation');
|
||||
}));
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
// Seek to just before the end then cancel
|
||||
animation.currentTime = 99.9 * 1000;
|
||||
animation.cancel();
|
||||
|
||||
// Then wait a couple of frames and check that no event was dispatched
|
||||
return waitForAnimationFrames(2);
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Cancelled CSS animations do not dispatch events');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: marginLeftAnim 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
animation.currentTime = 50 * 1000;
|
||||
assert_equals(getComputedStyle(div).marginLeft, '50px',
|
||||
'margin-left style is updated when cancelled animation is'
|
||||
+ ' seeked');
|
||||
}, 'After cancelling an animation, it can still be seeked');
|
||||
|
||||
async_test(function(t) {
|
||||
var div =
|
||||
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
animation.play();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '100px',
|
||||
'margin-left style is animated after re-starting animation');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation succeeds in running after being re-started');
|
||||
t.done();
|
||||
}));
|
||||
}, 'After cancelling an animation, it can still be re-used');
|
||||
|
||||
test(function(t) {
|
||||
var div =
|
||||
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
// Trigger a change to some animation properties and check that this
|
||||
// doesn't cause the animation to become live again
|
||||
div.style.animationDuration = '200s';
|
||||
flushComputedStyle(div);
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is still not animated after updating'
|
||||
+ ' animation-duration');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after updating animation-duration');
|
||||
}, 'After cancelling an animation, updating animation properties doesn\'t make'
|
||||
+ ' it live again');
|
||||
|
||||
test(function(t) {
|
||||
var div =
|
||||
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
// Make some changes to animation-play-state and check that the
|
||||
// animation doesn't become live again. This is because it should be
|
||||
// possible to cancel an animation from script such that all future
|
||||
// changes to style are ignored.
|
||||
|
||||
// Redundant change
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after a redundant change to'
|
||||
+ ' animation-play-state');
|
||||
|
||||
// Pause
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after setting'
|
||||
+ ' animation-play-state: paused');
|
||||
|
||||
// Play
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after re-setting'
|
||||
+ ' animation-play-state: running');
|
||||
|
||||
}, 'After cancelling an animation, updating animation-play-state doesn\'t'
|
||||
+ ' make it live again');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,548 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS animation's
|
||||
Animation.currentTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 10px;
|
||||
/* Make it easier to calculate expected values: */
|
||||
animation-timing-function: linear ! important;
|
||||
}
|
||||
|
||||
@keyframes anim {
|
||||
from { margin-left: 100px; }
|
||||
to { margin-left: 200px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const CSS_ANIM_EVENTS =
|
||||
['animationstart', 'animationiteration', 'animationend'];
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
|
||||
|
||||
/**
|
||||
* These helpers get the value that the currentTime needs to be set to, to put
|
||||
* an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
|
||||
* the middle of various phases or points through the active duration.
|
||||
*/
|
||||
function currentTimeForBeforePhase(timeline) {
|
||||
return ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForActivePhase(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
|
||||
}
|
||||
function currentTimeForAfterPhase(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForStartOfActiveInterval(timeline) {
|
||||
return ANIM_DELAY_MS;
|
||||
}
|
||||
function currentTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function currentTimeForEndOfActiveInterval(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const UNANIMATED_POSITION = 10;
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
//
|
||||
// Note the distinction between the "animation start time" which occurs before
|
||||
// the start delay and the start of the active interval which occurs after it.
|
||||
|
||||
// Called when currentTime is set to zero (the beginning of the start delay).
|
||||
function checkStateOnSettingCurrentTimeToZero(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the start delay');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'at the beginning of the start delay');
|
||||
}
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
// the 0.0001 here is for rounding error
|
||||
assert_less_than_equal(animation.currentTime,
|
||||
animation.timeline.currentTime - animation.startTime + 0.0001,
|
||||
'Animation.currentTime should be less than the local time ' +
|
||||
'equivalent of the timeline\'s currentTime on the first paint tick ' +
|
||||
'after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" on the first paint tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, "running",
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"finished" at the end of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by the animation at the end of the active duration when the ' +
|
||||
'animation-fill-mode is none');
|
||||
}
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// Animations shouldn't start until the next paint tick, so:
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Animation.currentTime should be zero when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(animation.playState, "pending",
|
||||
'Animation.playState should be "pending" when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" when an animation is initially created');
|
||||
|
||||
// XXX Ideally we would have a test to check the ready Promise is initially
|
||||
// unresolved, but currently there is no Web API to do that. Waiting for the
|
||||
// ready Promise with a timeout doesn't work because the resolved callback
|
||||
// will be called (async) regardless of whether the Promise was resolved in
|
||||
// the past or is resolved in the future.
|
||||
|
||||
// So that animation is running instead of paused when we set currentTime:
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
|
||||
assert_approx_equals(animation.currentTime, 0, 0.0001, // rounding error
|
||||
'Check setting of currentTime actually works');
|
||||
|
||||
checkStateOnSettingCurrentTimeToZero(animation);
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'currentTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForStartOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Skipping forward through animation');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// So that animation is running instead of paused when we set currentTime:
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
|
||||
animation.currentTime = currentTimeForEndOfActiveInterval(animation.timeline);
|
||||
|
||||
var previousTimelineTime = animation.timeline.currentTime;
|
||||
|
||||
// Skipping over the active interval will dispatch an 'animationstart' then
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
'firing after normal playback through the very long ' +
|
||||
'animation duration');
|
||||
|
||||
// Now we can start the tests for skipping backwards, but first we check
|
||||
// that after the events we're still in the same end time state:
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
|
||||
// Despite going backwards from after the end of the animation (to being
|
||||
// in the active interval), we now expect an 'animationstart' event
|
||||
// because the animation should go from being inactive to active.
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.wait_for('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
animation.currentTime =
|
||||
currentTimeForStartOfActiveInterval(animation.timeline);
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.currentTime = 0;
|
||||
// Despite going backwards from just after the active interval starts to
|
||||
// the animation start time, we now expect an animationend event
|
||||
// because we went from inside to outside the active interval.
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
|
||||
// This must come after we've set up the Promise chain, since requesting
|
||||
// computed style will force events to be dispatched.
|
||||
// XXX For some reason this fails occasionally (either the animation.playState
|
||||
// check or the marginLeft check).
|
||||
//checkStateAtActiveIntervalEndTime(animation);
|
||||
}, 'Skipping backwards through animation');
|
||||
|
||||
|
||||
// Next we have multiple tests to check that redundant currentTime changes do
|
||||
// NOT dispatch events. It's impossible to distinguish between events not being
|
||||
// dispatched and events just taking an incredibly long time to dispatch
|
||||
// without waiting an infinitely long time. Obviously we don't want to do that
|
||||
// (block this test from finishing forever), so instead we just listen for
|
||||
// events until two animation frames (i.e. requestAnimationFrame callbacks)
|
||||
// have happened, then assume that no events will ever be dispatched for the
|
||||
// redundant changes if no events were detected in that time.
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> active, then back');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var exception;
|
||||
try {
|
||||
animation.currentTime = null;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
assert_equals(exception.name, 'TypeError',
|
||||
'Expect TypeError exception on trying to set ' +
|
||||
'Animation.currentTime to null');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Setting currentTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var pauseTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(animation.currentTime, null,
|
||||
'Animation.currentTime not null on ready Promise resolve');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
pauseTime = animation.currentTime;
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, pauseTime,
|
||||
'Animation.currentTime is unchanged after pausing');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.currentTime after pausing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
// just before animation ends:
|
||||
animation.currentTime = ANIM_DELAY_MS + ANIM_DUR_MS - 1;
|
||||
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
|
||||
'Animation.currentTime should not continue to increase after the ' +
|
||||
'animation has finished');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.currentTime clamping');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
// play backwards:
|
||||
animation.playbackRate = -1;
|
||||
|
||||
// just before animation ends (at the "start"):
|
||||
animation.currentTime = 1;
|
||||
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Animation.currentTime should not continue to decrease after an ' +
|
||||
'animation running in reverse has finished and currentTime is zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.currentTime clamping for reversed animation');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(animation.currentTime, null,
|
||||
'The currentTime of a cancelled animation should be null');
|
||||
}, 'Animation.currentTime after cancelling');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,160 @@
|
|||
<!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 with a current time less ' +
|
||||
'than zero');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.pause();
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.finish();
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'The play state of a paused animation should remain ' +
|
||||
'"paused" even after finish() is called');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test paused state after finishing of animation');
|
||||
|
||||
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>
|
|
@ -0,0 +1,351 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes abc {
|
||||
to { transform: translate(10px) }
|
||||
}
|
||||
@keyframes def {}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
const ANIM_PROP_VAL = 'abc 100s';
|
||||
const ANIM_DURATION = 100000; // ms
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same object when playing starts');
|
||||
animation.pause();
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise does not change when pausing');
|
||||
animation.play();
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise does not change when play() unpauses');
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
return animation.finished;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same object when playing completes');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test pausing then playing does not change the finished promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same object when playing completes');
|
||||
animation.play();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise changes when replaying animation');
|
||||
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.play();
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same after redundant play() call');
|
||||
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a finished animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.playbackRate = -1;
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should be replaced when reversing a ' +
|
||||
'finished promise');
|
||||
animation.currentTime = 0;
|
||||
return animation.finished;
|
||||
})).then(t.step_func(function() {
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.play();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is replaced after play() call on ' +
|
||||
'finished, reversed animation');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a reversed finished animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.currentTime = ANIM_DURATION + 1000;
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is unchanged jumping past end of ' +
|
||||
'finished animation');
|
||||
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test redundant finishing of 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(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Object identity of animation passed to Promise callback'
|
||||
+ ' matches the animation object owning the Promise');
|
||||
t.done();
|
||||
}));
|
||||
}, 'The finished promise is fulfilled with its Animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
// Set up listeners on finished promise
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise is fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after the original is ' +
|
||||
'rejected');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now cancel the animation and flush styles
|
||||
div.style.animation = '';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'finished promise is rejected when an animation is cancelled by resetting ' +
|
||||
'the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// As before, but this time instead of removing all animations, simply update
|
||||
// the list of animations. At least for Firefox, updating is a different
|
||||
// code path.
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
// Set up listeners on finished promise
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after the original is ' +
|
||||
'rejected');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now update the animation and flush styles
|
||||
div.style.animation = 'def 100s';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'finished promise is rejected when an animation is cancelled by changing ' +
|
||||
'the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
// Set up listeners on finished promise
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after the original is ' +
|
||||
'rejected');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.cancel();
|
||||
|
||||
}, 'finished promise is rejected when an animation is cancelled by calling ' +
|
||||
'cancel()');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'A new finished promise should be created when'
|
||||
+ ' cancelling a finished player');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'cancelling an already-finished player replaces the finished promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
|
||||
// The spec says we still create a new finished promise and reject the old
|
||||
// one even if we're already idle. That behavior might change, but for now
|
||||
// test that we do that.
|
||||
animation.finished.catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Redundant call to cancel();
|
||||
var previousFinishedPromise = animation.finished;
|
||||
animation.cancel();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'A redundant call to cancel() should still generate a new'
|
||||
+ ' finished promise');
|
||||
}, 'cancelling an idle player still replaces the finished promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
const HALF_DUR = ANIM_DURATION / 2;
|
||||
const QUARTER_DUR = ANIM_DURATION / 4;
|
||||
|
||||
animation.currentTime = HALF_DUR;
|
||||
div.style.animationDuration = QUARTER_DUR + 'ms';
|
||||
// Animation should now be finished
|
||||
|
||||
// Below we use gotNextFrame to check that shortening of the animation
|
||||
// duration causes the finished promise to resolve, rather than it just
|
||||
// getting resolved on the next animation frame. This relies on the fact
|
||||
// that the promises are resolved as a micro-task before the next frame
|
||||
// happens.
|
||||
|
||||
window.getComputedStyle(div).animationDuration; // flush style
|
||||
var gotNextFrame = false;
|
||||
waitForFrame().then(function() {
|
||||
gotNextFrame = true;
|
||||
});
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_false(gotNextFrame, 'shortening of the animation duration should ' +
|
||||
'resolve the finished promise');
|
||||
assert_equals(animation.currentTime, HALF_DUR,
|
||||
'currentTime should be unchanged when duration shortened');
|
||||
var previousFinishedPromise = animation.finished;
|
||||
div.style.animationDuration = ANIM_DURATION + 'ms'; // now active again
|
||||
window.getComputedStyle(div).animationDuration; // flush style
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after lengthening the ' +
|
||||
'duration causes the animation to become active');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise changes for animation duration changes');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = 0;
|
||||
animation.currentTime = ANIM_DURATION + 1000;
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise should not resolve when playbackRate ' +
|
||||
'is zero');
|
||||
}));
|
||||
}, 'Test finished promise changes when playbackRate == 0');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = -1;
|
||||
return animation.finished;
|
||||
}).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise resolves when playbackRate set to a negative value');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Should not replay when animation-play-state changes to ' +
|
||||
'"running" on finished animation');
|
||||
assert_equals(animation.currentTime, ANIM_DURATION,
|
||||
'currentTime should not change when animation-play-state ' +
|
||||
'changes to "running" on finished animation');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise changes when animationPlayState set to running');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,202 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim {
|
||||
0% { margin-left: 0px }
|
||||
100% { margin-left: 10000px }
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() a running animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
animation.pause();
|
||||
div.style.animationPlayState = 'running';
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Paused value of margin-left is zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
animation.play();
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > 0,
|
||||
'Playing value of margin-left is greater than zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'play() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
animation.play();
|
||||
|
||||
var previousAnimVal;
|
||||
|
||||
animation.ready.then(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
return waitForFrame();
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
div.style.animationPlayState = 'paused';
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Animated value of margin-left does not change when'
|
||||
+ ' paused by style');
|
||||
t.done();
|
||||
}));
|
||||
}, 'play() is overridden by later setting "animation-play-state: paused"');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
// Set the specified style first. If implementations fail to
|
||||
// apply the style changes first, they will ignore the redundant
|
||||
// call to play() and fail to correctly override the pause style.
|
||||
div.style.animationPlayState = 'paused';
|
||||
animation.play();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'Playing value of margin-left is increasing');
|
||||
t.done();
|
||||
}));
|
||||
}, 'play() flushes pending changes to animation-play-state first');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
// Unlike the previous test for play(), since calling pause() is sticky,
|
||||
// we'll apply it even if the underlying style also says we're paused.
|
||||
//
|
||||
// We would like to test that implementations flush styles before running
|
||||
// pause() but actually there's no style we can currently set that will
|
||||
// change the behavior of pause(). That may change in the future
|
||||
// (e.g. if we introduce animation-timeline or animation-playback-rate etc.).
|
||||
//
|
||||
// For now this just serves as a sanity check that we do the same thing
|
||||
// even if we set style before calling the API.
|
||||
div.style.animationPlayState = 'running';
|
||||
animation.pause();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Paused value of margin-left does not change');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() applies pending changes to animation-play-state first');
|
||||
// (Note that we can't actually test for this; see comment above, in test-body.)
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: anim 1000s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var readyPromiseRun = false;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_equals(animation.playState, 'pending', 'Animation is pause pending');
|
||||
|
||||
// Set current time
|
||||
animation.currentTime = 5000;
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation is running immediately after setting currentTime');
|
||||
|
||||
// The ready promise should now be resolved. If it's not then test will
|
||||
// probably time out before anything else happens that causes it to resolve.
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Setting the current time cancels a pending pause');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,89 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending');
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation returns correct playState when running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending');
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation returns correct playState when paused');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.pause();
|
||||
assert_equals(animation.playState, 'pending');
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.playState updates when paused by script');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
div.style.animationPlayState = 'running';
|
||||
// This test also checks that calling playState flushes style
|
||||
assert_equals(animation.playState, 'pending',
|
||||
'Animation.playState reports pending after updating'
|
||||
+ ' animation-play-state (got: ' + animation.playState + ')');
|
||||
}, 'Animation.playState updates when resumed by setting style');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(animation.playState, 'idle');
|
||||
}, 'Animation returns correct playState when cancelled');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
animation.currentTime = 50 * 1000;
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'After seeking an idle animation, it is effectively paused');
|
||||
}, 'After cancelling an animation, seeking it makes it paused');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,251 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes abc {
|
||||
to { transform: translate(10px) }
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
var pauseReadyPromise;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
animation.pause();
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'A new ready promise is created when pausing');
|
||||
pauseReadyPromise = animation.ready;
|
||||
// Wait for the promise to fulfill since if we abort the pause the ready
|
||||
// promise object is reused.
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
animation.play();
|
||||
assert_not_equals(animation.ready, pauseReadyPromise,
|
||||
'A new ready promise is created when playing');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created when play()/pause() is called');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s paused';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'After updating animation-play-state a new ready promise'
|
||||
+ ' object is created');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created when setting animation-play-state: running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var promiseBeforeCallingPlay = animation.ready;
|
||||
animation.play();
|
||||
assert_equals(animation.ready, promiseBeforeCallingPlay,
|
||||
'Ready promise has same object identity after redundant call'
|
||||
+ ' to play()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Redundant calls to play() do not generate new ready promise objects');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Object identity of Animation passed to Promise callback'
|
||||
+ ' matches the Animation object owning the Promise');
|
||||
t.done();
|
||||
}));
|
||||
}, 'The ready promise is fulfilled with its Animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending',
|
||||
'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise is fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now cancel the animation and flush styles
|
||||
div.style.animation = '';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'ready promise is rejected when an animation is cancelled by resetting'
|
||||
+ ' the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// As before, but this time instead of removing all animations, simply update
|
||||
// the list of animations. At least for Firefox, updating is a different
|
||||
// code path.
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending',
|
||||
'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now update the animation and flush styles
|
||||
div.style.animation = 'def 100s';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'ready promise is rejected when an animation is cancelled by updating'
|
||||
+ ' the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.cancel();
|
||||
}, 'ready promise is rejected when a play-pending animation is cancelled by'
|
||||
+ ' calling cancel()');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.pause();
|
||||
|
||||
// Set up listeners on pause-pending ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.cancel();
|
||||
}));
|
||||
}, 'ready promise is rejected when a pause-pending animation is cancelled by'
|
||||
+ ' calling cancel()');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'A new Promise object is generated when setting'
|
||||
+ ' animation-play-state: paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created when setting animation-play-state: paused');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
var firstReadyPromise = animation.ready;
|
||||
animation.pause();
|
||||
assert_equals(animation.ready, firstReadyPromise,
|
||||
'Ready promise objects are identical after redundant pause');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Pausing twice re-uses the same Promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
|
||||
// Flush style and verify we're pending at the same time
|
||||
assert_equals(animation.playState, 'pending', 'Animation is pending');
|
||||
var pauseReadyPromise = animation.ready;
|
||||
|
||||
// Now play again immediately
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_equals(animation.playState, 'pending', 'Animation is still pending');
|
||||
assert_equals(animation.ready, pauseReadyPromise,
|
||||
'The pause Promise is re-used when playing while waiting'
|
||||
+ ' to pause');
|
||||
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation is running after aborting a pause');
|
||||
t.done();
|
||||
}));
|
||||
}, 'If a pause operation is interrupted, the ready promise is reused');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Promise received when ready Promise for a pause operation'
|
||||
+ ' is completed is the animation on which the pause was'
|
||||
+ ' performed');
|
||||
t.done();
|
||||
}));
|
||||
}, 'When a pause is complete the Promise callback gets the correct animation');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,557 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS animation's
|
||||
Animation.startTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 10px;
|
||||
/* Make it easier to calculate expected values: */
|
||||
animation-timing-function: linear ! important;
|
||||
}
|
||||
|
||||
@keyframes anim {
|
||||
from { margin-left: 100px; }
|
||||
to { margin-left: 200px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const CSS_ANIM_EVENTS =
|
||||
['animationstart', 'animationiteration', 'animationend'];
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
|
||||
|
||||
/**
|
||||
* These helpers get the value that the startTime needs to be set to, to put an
|
||||
* animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
|
||||
* middle of various phases or points through the active duration.
|
||||
*/
|
||||
function startTimeForBeforePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForActivePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
|
||||
}
|
||||
function startTimeForAfterPhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForStartOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS;
|
||||
}
|
||||
function startTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function startTimeForEndOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const UNANIMATED_POSITION = 10;
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
//
|
||||
// Note the distinction between the "animation start time" which occurs before
|
||||
// the start delay and the start of the active interval which occurs after it.
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
|
||||
'Animation.startTime should be less than the timeline\'s ' +
|
||||
'currentTime on the first paint tick after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" on the first paint tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, "running",
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"finished" at the end of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by the animation at the end of the active duration when the ' +
|
||||
'animation-fill-mode is none');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created (play-pending) animation is unresolved');
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created (pause-pending) animation is unresolved');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_true(animation.startTime > 0,
|
||||
'startTime is resolved when running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime is resolved when running');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'startTime is unresolved when paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime is unresolved when paused');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'startTime is resolved when pause-pending');
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'startTime is preserved when a pause is aborted');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime while pause-pending and play-pending');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
// Seek to end to put us in the finished state
|
||||
// FIXME: Once we implement finish(), use that here.
|
||||
animation.currentTime = 100 * 1000;
|
||||
animation.ready.then(t.step_func(function() {
|
||||
// Call play() which puts us back in the running state
|
||||
animation.play();
|
||||
// FIXME: Enable this once we implement finishing behavior (bug 1074630)
|
||||
/*
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
*/
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime while play-pending from finished state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: anim 1000s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(animation.startTime, null, 'The initial startTime is null');
|
||||
var initialTimelineTime = document.timeline.currentTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_true(animation.startTime > initialTimelineTime,
|
||||
'After the animation has started, startTime is greater than ' +
|
||||
'the time when it was started');
|
||||
var startTimeBeforePausing = animation.startTime;
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
// Flush styles just in case querying animation.startTime doesn't flush
|
||||
// styles (which would be a bug in of itself and could mask a further bug
|
||||
// by causing startTime to appear to not change).
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(animation.startTime, startTimeBeforePausing,
|
||||
'The startTime does not change when pausing-pending');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'After actually pausing, the startTime of an animation ' +
|
||||
'is null');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Pausing should make the startTime become null');
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var currentTime = animation.timeline.currentTime;
|
||||
animation.startTime = currentTime;
|
||||
assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error
|
||||
'Check setting of startTime actually works');
|
||||
}, 'Sanity test to check round-tripping assigning to a new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Skipping forward through animation');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
|
||||
var previousTimelineTime = animation.timeline.currentTime;
|
||||
|
||||
// Skipping over the active interval will dispatch an 'animationstart' then
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
'firing after normal playback through the very long ' +
|
||||
'animation duration');
|
||||
|
||||
// Now we can start the tests for skipping backwards, but first we check
|
||||
// that after the events we're still in the same end time state:
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
|
||||
// Despite going backwards from after the end of the animation (to being
|
||||
// in the active interval), we now expect an 'animationstart' event
|
||||
// because the animation should go from being inactive to active.
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.wait_for('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
// Despite going backwards from just after the active interval starts to
|
||||
// the animation start time, we now expect an animationend event
|
||||
// because we went from inside to outside the active interval.
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
|
||||
// This must come after we've set up the Promise chain, since requesting
|
||||
// computed style will force events to be dispatched.
|
||||
// XXX For some reason this fails occasionally (either the animation.playState
|
||||
// check or the marginLeft check).
|
||||
//checkStateAtActiveIntervalEndTime(animation);
|
||||
}, 'Skipping backwards through animation');
|
||||
|
||||
|
||||
// Next we have multiple tests to check that redundant startTime changes do NOT
|
||||
// dispatch events. It's impossible to distinguish between events not being
|
||||
// dispatched and events just taking an incredibly long time to dispatch
|
||||
// without waiting an infinitely long time. Obviously we don't want to do that
|
||||
// (block this test from finishing forever), so instead we just listen for
|
||||
// events until two animation frames (i.e. requestAnimationFrame callbacks)
|
||||
// have happened, then assume that no events will ever be dispatched for the
|
||||
// redundant changes if no events were detected in that time.
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> active, then back');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var storedCurrentTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
storedCurrentTime = animation.currentTime;
|
||||
animation.startTime = null;
|
||||
return animation.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Setting startTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var savedStartTime = animation.startTime;
|
||||
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'Animation.startTime not null on ready Promise resolve');
|
||||
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'Animation.startTime is null after paused');
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'Animation.playState is "paused" after pause() call');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.startTime after pausing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_equals(animation.startTime, null,
|
||||
'The startTime of a cancelled animation should be null');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.startTime after cancelling');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,159 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
to { left: 100px }
|
||||
}
|
||||
@keyframes anim2 { }
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
|
||||
var originalAnimation = div.getAnimations()[0];
|
||||
var originalStartTime;
|
||||
var originalCurrentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and doesn't
|
||||
// simply reflect the current time).
|
||||
originalAnimation.ready.then(function() {
|
||||
originalStartTime = originalAnimation.startTime;
|
||||
originalCurrentTime = originalAnimation.currentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and
|
||||
// doesn't simply reflect the current time).
|
||||
return waitForFrame();
|
||||
}).then(t.step_func(function() {
|
||||
div.style.animationDuration = '200s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation, originalAnimation,
|
||||
'The same Animation is returned after updating'
|
||||
+ ' animation duration');
|
||||
assert_equals(animation.startTime, originalStartTime,
|
||||
'Animations returned by getAnimations preserve'
|
||||
+ ' their startTime even when they are updated');
|
||||
// Sanity check
|
||||
assert_not_equals(animation.currentTime, originalCurrentTime,
|
||||
'Animation.currentTime has updated in next'
|
||||
+ ' requestAnimationFrame callback');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animations preserve their startTime when changed');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
var animations = div.getAnimations();
|
||||
var animation1 = animations[0];
|
||||
var animation2 = animations[1];
|
||||
|
||||
// Update first in list
|
||||
div.style.animationDuration = '200s, 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations[0], animation1,
|
||||
'First Animation is in same position after update');
|
||||
assert_equals(animations[1], animation2,
|
||||
'Second Animation is in same position after update');
|
||||
}, 'Updated Animations maintain their order in the list');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 200s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
var animations = div.getAnimations();
|
||||
var animation1 = animations[0];
|
||||
var animation2 = animations[1];
|
||||
|
||||
// Wait before continuing so we can compare start times (otherwise the
|
||||
// new Animation objects and existing Animation objects will all have the same
|
||||
// start time).
|
||||
waitForAllAnimations(animations).then(waitForFrame).then(t.step_func(function() {
|
||||
// Swap duration of first and second in list and prepend animation at the
|
||||
// same time
|
||||
div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
|
||||
animations = div.getAnimations();
|
||||
assert_true(animations[0] !== animation1 && animations[0] !== animation2,
|
||||
'New Animation is prepended to start of list');
|
||||
assert_equals(animations[1], animation1,
|
||||
'First Animation is in second position after update');
|
||||
assert_equals(animations[2], animation2,
|
||||
'Second Animation is in third position after update');
|
||||
assert_equals(animations[1].startTime, animations[2].startTime,
|
||||
'Old Animations have the same start time');
|
||||
// TODO: Check that animations[0].startTime === null
|
||||
return animations[0].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime > animations[1].startTime,
|
||||
'New Animation has later start time');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Only the startTimes of existing animations are preserved');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
var secondAnimation = div.getAnimations()[1];
|
||||
|
||||
// Wait before continuing so we can compare start times
|
||||
secondAnimation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
// Trim list of animations
|
||||
div.style.animationName = 'anim1';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1, 'List of Animations was trimmed');
|
||||
assert_equals(animations[0], secondAnimation,
|
||||
'Remaining Animation is the second one in the list');
|
||||
assert_equals(typeof(animations[0].startTime), 'number',
|
||||
'Remaining Animation has resolved startTime');
|
||||
assert_true(animations[0].startTime < animations[0].timeline.currentTime,
|
||||
'Remaining Animation preserves startTime');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animations are removed from the start of the list while preserving'
|
||||
+ ' the state of existing Animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var firstAddedAnimation = div.getAnimations()[0],
|
||||
secondAddedAnimation,
|
||||
animations;
|
||||
|
||||
// Wait and add second Animation
|
||||
firstAddedAnimation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
secondAddedAnimation = div.getAnimations()[0];
|
||||
|
||||
// Wait again and add another Animation
|
||||
return secondAddedAnimation.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_not_equals(firstAddedAnimation, secondAddedAnimation,
|
||||
'New Animations are added to start of the list');
|
||||
assert_equals(animations[0], secondAddedAnimation,
|
||||
'Second Animation remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_equals(animations[2], firstAddedAnimation,
|
||||
'First Animation remains in same position after'
|
||||
+ ' interleaving');
|
||||
return animations[1].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[1].startTime > animations[0].startTime,
|
||||
'Interleaved animation starts later than existing animations');
|
||||
assert_true(animations[0].startTime > animations[2].startTime,
|
||||
'Original animations retain their start time');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation state is preserved when interleaving animations in list');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,37 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes xyz {
|
||||
to { left: 100px }
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'xyz 100s';
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'xyz',
|
||||
'Animation effect name matches keyframes rule name');
|
||||
}, 'Effect name makes keyframe rule');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\yz 100s';
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'xyz',
|
||||
'Escaped animation effect name matches keyframes rule name');
|
||||
}, 'Escaped animation name');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\79 z 100s';
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'xyz',
|
||||
'Hex-escaped animation name matches keyframes rule'
|
||||
+ ' name');
|
||||
}, 'Animation name with hex-escape');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.effect.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
}, 'Returned CSS animations have the correct Animation.target');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,272 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
to { left: 100px }
|
||||
}
|
||||
@keyframes anim2 {
|
||||
to { top: 100px }
|
||||
}
|
||||
@keyframes multiPropAnim {
|
||||
to { background: green, opacity: 0.5, left: 100px, top: 100px }
|
||||
}
|
||||
@keyframes empty { }
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for an element'
|
||||
+ ' with no animations');
|
||||
}, 'getAnimations for non-animated content');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.animation = 'anim1 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns an Animation running CSS Animations');
|
||||
animations[0].ready.then(t.step_func(function() {
|
||||
var startTime = animations[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS animation has a sensible start time');
|
||||
|
||||
// Wait a moment then add a second animation.
|
||||
//
|
||||
// We wait for the next frame so that we can test that the start times of
|
||||
// the animations differ.
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations returns one Animation for each value of'
|
||||
+ ' animation-name');
|
||||
// Wait until both Animations are ready
|
||||
// (We don't make any assumptions about the order of the Animations since
|
||||
// that is the purpose of the following test.)
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime < animations[1].startTime,
|
||||
'Additional Animations for CSS animations start after the original'
|
||||
+ ' animation and appear later in the list');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS Animations');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation that targets multiple properties
|
||||
div.style.animation = 'multiPropAnim 100s';
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns only one Animation for a CSS Animation'
|
||||
+ ' that targets multiple properties');
|
||||
}, 'getAnimations for multi-property animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.backgroundColor = 'red';
|
||||
div.style.animation = 'anim1 100s';
|
||||
window.getComputedStyle(div).backgroundColor;
|
||||
|
||||
// Wait until a frame after the animation starts, then add a transition
|
||||
var animations = div.getAnimations();
|
||||
animations[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.backgroundColor = 'green';
|
||||
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations returns Animations for both animations and'
|
||||
+ ' transitions that run simultaneously');
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime > animations[1].startTime,
|
||||
'Animations for transitions appear before animations even if they'
|
||||
+ ' start later');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for both CSS Animations and Transitions at once');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations does not return Animations for finished '
|
||||
+ ' (and non-forwards-filling) CSS Animations');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Add a very short animation
|
||||
div.style.animation = 'anim1 0.01s';
|
||||
}, 'getAnimations for CSS Animations that have finished');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns Animations for CSS Animations that have'
|
||||
+ ' finished but are filling forwards');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Add a very short animation
|
||||
div.style.animation = 'anim1 0.01s forwards';
|
||||
}, 'getAnimations for CSS Animations that have finished but are'
|
||||
+ ' forwards filling');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'none 100s';
|
||||
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 0,
|
||||
'getAnimations returns an empty sequence for an element'
|
||||
+ ' with animation-name: none');
|
||||
|
||||
div.style.animation = 'none 100s, anim1 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns Animations only for those CSS Animations whose'
|
||||
+ ' animation-name is not none');
|
||||
}, 'getAnimations for CSS Animations with animation-name: none');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'missing 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 0,
|
||||
'getAnimations returns an empty sequence for an element'
|
||||
+ ' with animation-name: missing');
|
||||
|
||||
div.style.animation = 'anim1 100s, missing 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns Animations only for those CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
}, 'getAnimations for CSS Animations with animation-name: missing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, notyet 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations initally only returns Animations for CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
|
||||
animations[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
var keyframes = '@keyframes notyet { to { left: 100px; } }';
|
||||
document.styleSheets[0].insertRule(keyframes, 0);
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations includes Animation when @keyframes rule is added'
|
||||
+ ' later');
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime < animations[1].startTime,
|
||||
'Newly added animation has a later start time');
|
||||
document.styleSheets[0].deleteRule(0);
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS Animations where the @keyframes rule is added'
|
||||
+ ' later');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
assert_equals(div.getAnimations().length, 2,
|
||||
'getAnimations returns one Animation for each CSS animation-name'
|
||||
+ ' even if the names are duplicated');
|
||||
}, 'getAnimations for CSS Animations with duplicated animation-name');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'empty 100s';
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns Animations for CSS animations with an'
|
||||
+ ' empty keyframes rule');
|
||||
}, 'getAnimations for CSS Animations with empty keyframes rule');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns animations for CSS animations whose'
|
||||
+ ' delay makes them start later');
|
||||
animations[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime <= document.timeline.currentTime,
|
||||
'For CSS Animations in delay phase, the start time of the Animation is'
|
||||
+ ' not in the future');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS animations in delay phase');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 0s 100s';
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns animations for CSS animations whose'
|
||||
+ ' duration is zero');
|
||||
div.remove();
|
||||
}, 'getAnimations for zero-duration CSS Animations');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var originalAnimation = div.getAnimations()[0];
|
||||
|
||||
// Update pause state (an Animation change)
|
||||
div.style.animationPlayState = 'paused';
|
||||
var pendingAnimation = div.getAnimations()[0];
|
||||
assert_equals(pendingAnimation.playState, 'pending',
|
||||
'animation\'s play state is updated');
|
||||
assert_equals(originalAnimation, pendingAnimation,
|
||||
'getAnimations returns the same objects even when their'
|
||||
+ ' play state changes');
|
||||
|
||||
// Update duration (an Animation change)
|
||||
div.style.animationDuration = '200s';
|
||||
var extendedAnimation = div.getAnimations()[0];
|
||||
// FIXME: Check extendedAnimation.effect.timing.duration has changed once the
|
||||
// API is available
|
||||
assert_equals(originalAnimation, extendedAnimation,
|
||||
'getAnimations returns the same objects even when their'
|
||||
+ ' duration changes');
|
||||
}, 'getAnimations returns objects with the same identity');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns an animation before cancelling');
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.cancel();
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations does not return cancelled animations');
|
||||
|
||||
animation.play();
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns cancelled animations that have been re-started');
|
||||
|
||||
}, 'getAnimations for CSS Animations that are cancelled');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -2,163 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes translateAnim {
|
||||
to { transform: translate(100px) }
|
||||
}
|
||||
@keyframes marginLeftAnim {
|
||||
to { margin-left: 100px }
|
||||
}
|
||||
@keyframes marginLeftAnim100To200 {
|
||||
from { margin-left: 100px }
|
||||
to { margin-left: 200px }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: translateAnim 100s' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(getComputedStyle(div).transform, 'none',
|
||||
'transform style is animated before cancelling');
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).transform, 'none',
|
||||
'transform style is no longer animated after cancelling');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animated style is cleared after cancelling a running CSS animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: translateAnim 100s forwards' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.finish();
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(getComputedStyle(div).transform, 'none',
|
||||
'transform style is filling before cancelling');
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).transform, 'none',
|
||||
'fill style is cleared after cancelling');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animated style is cleared after cancelling a filling CSS animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: translateAnim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_unreached('Got unexpected end event on cancelled animation');
|
||||
}));
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
// Seek to just before the end then cancel
|
||||
animation.currentTime = 99.9 * 1000;
|
||||
animation.cancel();
|
||||
|
||||
// Then wait a couple of frames and check that no event was dispatched
|
||||
return waitForAnimationFrames(2);
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Cancelled CSS animations do not dispatch events');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: marginLeftAnim 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
animation.currentTime = 50 * 1000;
|
||||
assert_equals(getComputedStyle(div).marginLeft, '50px',
|
||||
'margin-left style is updated when cancelled animation is'
|
||||
+ ' seeked');
|
||||
}, 'After cancelling an animation, it can still be seeked');
|
||||
|
||||
async_test(function(t) {
|
||||
var div =
|
||||
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
animation.play();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '100px',
|
||||
'margin-left style is animated after re-starting animation');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation succeeds in running after being re-started');
|
||||
t.done();
|
||||
}));
|
||||
}, 'After cancelling an animation, it can still be re-used');
|
||||
|
||||
test(function(t) {
|
||||
var div =
|
||||
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
// Trigger a change to some animation properties and check that this
|
||||
// doesn't cause the animation to become live again
|
||||
div.style.animationDuration = '200s';
|
||||
flushComputedStyle(div);
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is still not animated after updating'
|
||||
+ ' animation-duration');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after updating animation-duration');
|
||||
}, 'After cancelling an animation, updating animation properties doesn\'t make'
|
||||
+ ' it live again');
|
||||
|
||||
test(function(t) {
|
||||
var div =
|
||||
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
// Make some changes to animation-play-state and check that the
|
||||
// animation doesn't become live again. This is because it should be
|
||||
// possible to cancel an animation from script such that all future
|
||||
// changes to style are ignored.
|
||||
|
||||
// Redundant change
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after a redundant change to'
|
||||
+ ' animation-play-state');
|
||||
|
||||
// Pause
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after setting'
|
||||
+ ' animation-play-state: paused');
|
||||
|
||||
// Play
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is still idle after re-setting'
|
||||
+ ' animation-play-state: running');
|
||||
|
||||
}, 'After cancelling an animation, updating animation-play-state doesn\'t'
|
||||
+ ' make it live again');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-cancel.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -1,550 +1,15 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS animation's
|
||||
Animation.currentTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 10px;
|
||||
/* Make it easier to calculate expected values: */
|
||||
animation-timing-function: linear ! important;
|
||||
}
|
||||
|
||||
@keyframes anim {
|
||||
from { margin-left: 100px; }
|
||||
to { margin-left: 200px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const CSS_ANIM_EVENTS =
|
||||
['animationstart', 'animationiteration', 'animationend'];
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
|
||||
|
||||
/**
|
||||
* These helpers get the value that the currentTime needs to be set to, to put
|
||||
* an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
|
||||
* the middle of various phases or points through the active duration.
|
||||
*/
|
||||
function currentTimeForBeforePhase(timeline) {
|
||||
return ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForActivePhase(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
|
||||
}
|
||||
function currentTimeForAfterPhase(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForStartOfActiveInterval(timeline) {
|
||||
return ANIM_DELAY_MS;
|
||||
}
|
||||
function currentTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function currentTimeForEndOfActiveInterval(timeline) {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const UNANIMATED_POSITION = 10;
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
//
|
||||
// Note the distinction between the "animation start time" which occurs before
|
||||
// the start delay and the start of the active interval which occurs after it.
|
||||
|
||||
// Called when currentTime is set to zero (the beginning of the start delay).
|
||||
function checkStateOnSettingCurrentTimeToZero(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the start delay');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'at the beginning of the start delay');
|
||||
}
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
// the 0.0001 here is for rounding error
|
||||
assert_less_than_equal(animation.currentTime,
|
||||
animation.timeline.currentTime - animation.startTime + 0.0001,
|
||||
'Animation.currentTime should be less than the local time ' +
|
||||
'equivalent of the timeline\'s currentTime on the first paint tick ' +
|
||||
'after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" on the first paint tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, "running",
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"finished" at the end of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by the animation at the end of the active duration when the ' +
|
||||
'animation-fill-mode is none');
|
||||
}
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// Animations shouldn't start until the next paint tick, so:
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Animation.currentTime should be zero when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(animation.playState, "pending",
|
||||
'Animation.playState should be "pending" when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" when an animation is initially created');
|
||||
|
||||
// XXX Ideally we would have a test to check the ready Promise is initially
|
||||
// unresolved, but currently there is no Web API to do that. Waiting for the
|
||||
// ready Promise with a timeout doesn't work because the resolved callback
|
||||
// will be called (async) regardless of whether the Promise was resolved in
|
||||
// the past or is resolved in the future.
|
||||
|
||||
// So that animation is running instead of paused when we set currentTime:
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
|
||||
assert_approx_equals(animation.currentTime, 0, 0.0001, // rounding error
|
||||
'Check setting of currentTime actually works');
|
||||
|
||||
checkStateOnSettingCurrentTimeToZero(animation);
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'currentTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForStartOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-currenttime.html");
|
||||
});
|
||||
}, 'Skipping forward through animation');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// So that animation is running instead of paused when we set currentTime:
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
|
||||
animation.currentTime = currentTimeForEndOfActiveInterval(animation.timeline);
|
||||
|
||||
var previousTimelineTime = animation.timeline.currentTime;
|
||||
|
||||
// Skipping over the active interval will dispatch an 'animationstart' then
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
'firing after normal playback through the very long ' +
|
||||
'animation duration');
|
||||
|
||||
// Now we can start the tests for skipping backwards, but first we check
|
||||
// that after the events we're still in the same end time state:
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
|
||||
// Despite going backwards from after the end of the animation (to being
|
||||
// in the active interval), we now expect an 'animationstart' event
|
||||
// because the animation should go from being inactive to active.
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.wait_for('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
animation.currentTime =
|
||||
currentTimeForStartOfActiveInterval(animation.timeline);
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.currentTime = 0;
|
||||
// Despite going backwards from just after the active interval starts to
|
||||
// the animation start time, we now expect an animationend event
|
||||
// because we went from inside to outside the active interval.
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
|
||||
// This must come after we've set up the Promise chain, since requesting
|
||||
// computed style will force events to be dispatched.
|
||||
// XXX For some reason this fails occasionally (either the animation.playState
|
||||
// check or the marginLeft check).
|
||||
//checkStateAtActiveIntervalEndTime(animation);
|
||||
}, 'Skipping backwards through animation');
|
||||
|
||||
|
||||
// Next we have multiple tests to check that redundant currentTime changes do
|
||||
// NOT dispatch events. It's impossible to distinguish between events not being
|
||||
// dispatched and events just taking an incredibly long time to dispatch
|
||||
// without waiting an infinitely long time. Obviously we don't want to do that
|
||||
// (block this test from finishing forever), so instead we just listen for
|
||||
// events until two animation frames (i.e. requestAnimationFrame callbacks)
|
||||
// have happened, then assume that no events will ever be dispatched for the
|
||||
// redundant changes if no events were detected in that time.
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> active, then back');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var exception;
|
||||
try {
|
||||
animation.currentTime = null;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
assert_equals(exception.name, 'TypeError',
|
||||
'Expect TypeError exception on trying to set ' +
|
||||
'Animation.currentTime to null');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Setting currentTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var pauseTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(animation.currentTime, null,
|
||||
'Animation.currentTime not null on ready Promise resolve');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
pauseTime = animation.currentTime;
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, pauseTime,
|
||||
'Animation.currentTime is unchanged after pausing');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.currentTime after pausing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
// just before animation ends:
|
||||
animation.currentTime = ANIM_DELAY_MS + ANIM_DUR_MS - 1;
|
||||
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
|
||||
'Animation.currentTime should not continue to increase after the ' +
|
||||
'animation has finished');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.currentTime clamping');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
// play backwards:
|
||||
animation.playbackRate = -1;
|
||||
|
||||
// just before animation ends (at the "start"):
|
||||
animation.currentTime = 1;
|
||||
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Animation.currentTime should not continue to decrease after an ' +
|
||||
'animation running in reverse has finished and currentTime is zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.currentTime clamping for reversed animation');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(animation.currentTime, null,
|
||||
'The currentTime of a cancelled animation should be null');
|
||||
}, 'Animation.currentTime after cancelling');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,159 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@keyframes anim {
|
||||
from { margin-left: 100px; }
|
||||
to { margin-left: 200px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
<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 with a current time less ' +
|
||||
'than zero');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.pause();
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.finish();
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'The play state of a paused animation should remain ' +
|
||||
'"paused" even after finish() is called');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test paused state after finishing of animation');
|
||||
|
||||
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');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-finish.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,350 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes abc {
|
||||
to { transform: translate(10px) }
|
||||
}
|
||||
@keyframes def {}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
const ANIM_PROP_VAL = 'abc 100s';
|
||||
const ANIM_DURATION = 100000; // ms
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same object when playing starts');
|
||||
animation.pause();
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise does not change when pausing');
|
||||
animation.play();
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise does not change when play() unpauses');
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
return animation.finished;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same object when playing completes');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test pausing then playing does not change the finished promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same object when playing completes');
|
||||
animation.play();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise changes when replaying animation');
|
||||
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.play();
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is the same after redundant play() call');
|
||||
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a finished animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.playbackRate = -1;
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should be replaced when reversing a ' +
|
||||
'finished promise');
|
||||
animation.currentTime = 0;
|
||||
return animation.finished;
|
||||
})).then(t.step_func(function() {
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.play();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is replaced after play() call on ' +
|
||||
'finished, reversed animation');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a reversed finished animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.currentTime = ANIM_DURATION + 1000;
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise is unchanged jumping past end of ' +
|
||||
'finished animation');
|
||||
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test redundant finishing of 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(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Object identity of animation passed to Promise callback'
|
||||
+ ' matches the animation object owning the Promise');
|
||||
t.done();
|
||||
}));
|
||||
}, 'The finished promise is fulfilled with its Animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
// Set up listeners on finished promise
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise is fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after the original is ' +
|
||||
'rejected');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now cancel the animation and flush styles
|
||||
div.style.animation = '';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'finished promise is rejected when an animation is cancelled by resetting ' +
|
||||
'the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// As before, but this time instead of removing all animations, simply update
|
||||
// the list of animations. At least for Firefox, updating is a different
|
||||
// code path.
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
// Set up listeners on finished promise
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after the original is ' +
|
||||
'rejected');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now update the animation and flush styles
|
||||
div.style.animation = 'def 100s';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'finished promise is rejected when an animation is cancelled by changing ' +
|
||||
'the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
// Set up listeners on finished promise
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after the original is ' +
|
||||
'rejected');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.cancel();
|
||||
|
||||
}, 'finished promise is rejected when an animation is cancelled by calling ' +
|
||||
'cancel()');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'A new finished promise should be created when'
|
||||
+ ' cancelling a finished player');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'cancelling an already-finished player replaces the finished promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
|
||||
// The spec says we still create a new finished promise and reject the old
|
||||
// one even if we're already idle. That behavior might change, but for now
|
||||
// test that we do that.
|
||||
animation.finished.catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'finished promise is rejected with AbortError');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Redundant call to cancel();
|
||||
var previousFinishedPromise = animation.finished;
|
||||
animation.cancel();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'A redundant call to cancel() should still generate a new'
|
||||
+ ' finished promise');
|
||||
}, 'cancelling an idle player still replaces the finished promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
const HALF_DUR = ANIM_DURATION / 2;
|
||||
const QUARTER_DUR = ANIM_DURATION / 4;
|
||||
|
||||
animation.currentTime = HALF_DUR;
|
||||
div.style.animationDuration = QUARTER_DUR + 'ms';
|
||||
// Animation should now be finished
|
||||
|
||||
// Below we use gotNextFrame to check that shortening of the animation
|
||||
// duration causes the finished promise to resolve, rather than it just
|
||||
// getting resolved on the next animation frame. This relies on the fact
|
||||
// that the promises are resolved as a micro-task before the next frame
|
||||
// happens.
|
||||
|
||||
window.getComputedStyle(div).animationDuration; // flush style
|
||||
var gotNextFrame = false;
|
||||
waitForFrame().then(function() {
|
||||
gotNextFrame = true;
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-finished.html");
|
||||
});
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_false(gotNextFrame, 'shortening of the animation duration should ' +
|
||||
'resolve the finished promise');
|
||||
assert_equals(animation.currentTime, HALF_DUR,
|
||||
'currentTime should be unchanged when duration shortened');
|
||||
var previousFinishedPromise = animation.finished;
|
||||
div.style.animationDuration = ANIM_DURATION + 'ms'; // now active again
|
||||
window.getComputedStyle(div).animationDuration; // flush style
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
'Finished promise should change after lengthening the ' +
|
||||
'duration causes the animation to become active');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise changes for animation duration changes');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = 0;
|
||||
animation.currentTime = ANIM_DURATION + 1000;
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
assert_unreached('finished promise should not resolve when playbackRate ' +
|
||||
'is zero');
|
||||
}));
|
||||
}, 'Test finished promise changes when playbackRate == 0');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = -1;
|
||||
return animation.finished;
|
||||
}).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise resolves when playbackRate set to a negative value');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var previousFinishedPromise = animation.finished;
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
return waitForAnimationFrames(2);
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(animation.finished, previousFinishedPromise,
|
||||
'Should not replay when animation-play-state changes to ' +
|
||||
'"running" on finished animation');
|
||||
assert_equals(animation.currentTime, ANIM_DURATION,
|
||||
'currentTime should not change when animation-play-state ' +
|
||||
'changes to "running" on finished animation');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finished promise changes when animationPlayState set to running');
|
||||
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,201 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim {
|
||||
0% { margin-left: 0px }
|
||||
100% { margin-left: 10000px }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() a running animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
animation.pause();
|
||||
div.style.animationPlayState = 'running';
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Paused value of margin-left is zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
animation.play();
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > 0,
|
||||
'Playing value of margin-left is greater than zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'play() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
animation.play();
|
||||
|
||||
var previousAnimVal;
|
||||
|
||||
animation.ready.then(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
return waitForFrame();
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
div.style.animationPlayState = 'paused';
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Animated value of margin-left does not change when'
|
||||
+ ' paused by style');
|
||||
t.done();
|
||||
}));
|
||||
}, 'play() is overridden by later setting "animation-play-state: paused"');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
// Set the specified style first. If implementations fail to
|
||||
// apply the style changes first, they will ignore the redundant
|
||||
// call to play() and fail to correctly override the pause style.
|
||||
div.style.animationPlayState = 'paused';
|
||||
animation.play();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'Playing value of margin-left is increasing');
|
||||
t.done();
|
||||
}));
|
||||
}, 'play() flushes pending changes to animation-play-state first');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
// Unlike the previous test for play(), since calling pause() is sticky,
|
||||
// we'll apply it even if the underlying style also says we're paused.
|
||||
//
|
||||
// We would like to test that implementations flush styles before running
|
||||
// pause() but actually there's no style we can currently set that will
|
||||
// change the behavior of pause(). That may change in the future
|
||||
// (e.g. if we introduce animation-timeline or animation-playback-rate etc.).
|
||||
//
|
||||
// For now this just serves as a sanity check that we do the same thing
|
||||
// even if we set style before calling the API.
|
||||
div.style.animationPlayState = 'running';
|
||||
animation.pause();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Paused value of margin-left does not change');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() applies pending changes to animation-play-state first');
|
||||
// (Note that we can't actually test for this; see comment above, in test-body.)
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: anim 1000s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var readyPromiseRun = false;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_equals(animation.playState, 'pending', 'Animation is pause pending');
|
||||
|
||||
// Set current time
|
||||
animation.currentTime = 5000;
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation is running immediately after setting currentTime');
|
||||
|
||||
// The ready promise should now be resolved. If it's not then test will
|
||||
// probably time out before anything else happens that causes it to resolve.
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Setting the current time cancels a pending pause');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-pausing.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,88 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending');
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation returns correct playState when running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending');
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation returns correct playState when paused');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.pause();
|
||||
assert_equals(animation.playState, 'pending');
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.playState updates when paused by script');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
div.style.animationPlayState = 'running';
|
||||
// This test also checks that calling playState flushes style
|
||||
assert_equals(animation.playState, 'pending',
|
||||
'Animation.playState reports pending after updating'
|
||||
+ ' animation-play-state (got: ' + animation.playState + ')');
|
||||
}, 'Animation.playState updates when resumed by setting style');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(animation.playState, 'idle');
|
||||
}, 'Animation returns correct playState when cancelled');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
animation.currentTime = 50 * 1000;
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'After seeking an idle animation, it is effectively paused');
|
||||
}, 'After cancelling an animation, seeking it makes it paused');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-playstate.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,250 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes abc {
|
||||
to { transform: translate(10px) }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
var pauseReadyPromise;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
animation.pause();
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'A new ready promise is created when pausing');
|
||||
pauseReadyPromise = animation.ready;
|
||||
// Wait for the promise to fulfill since if we abort the pause the ready
|
||||
// promise object is reused.
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
animation.play();
|
||||
assert_not_equals(animation.ready, pauseReadyPromise,
|
||||
'A new ready promise is created when playing');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created when play()/pause() is called');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s paused';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'After updating animation-play-state a new ready promise'
|
||||
+ ' object is created');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created when setting animation-play-state: running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var promiseBeforeCallingPlay = animation.ready;
|
||||
animation.play();
|
||||
assert_equals(animation.ready, promiseBeforeCallingPlay,
|
||||
'Ready promise has same object identity after redundant call'
|
||||
+ ' to play()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Redundant calls to play() do not generate new ready promise objects');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Object identity of Animation passed to Promise callback'
|
||||
+ ' matches the Animation object owning the Promise');
|
||||
t.done();
|
||||
}));
|
||||
}, 'The ready promise is fulfilled with its Animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending',
|
||||
'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise is fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now cancel the animation and flush styles
|
||||
div.style.animation = '';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'ready promise is rejected when an animation is cancelled by resetting'
|
||||
+ ' the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// As before, but this time instead of removing all animations, simply update
|
||||
// the list of animations. At least for Firefox, updating is a different
|
||||
// code path.
|
||||
|
||||
// Set up pending animation
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending',
|
||||
'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now update the animation and flush styles
|
||||
div.style.animation = 'def 100s';
|
||||
window.getComputedStyle(div).animation;
|
||||
|
||||
}, 'ready promise is rejected when an animation is cancelled by updating'
|
||||
+ ' the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.cancel();
|
||||
}, 'ready promise is rejected when a play-pending animation is cancelled by'
|
||||
+ ' calling cancel()');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.pause();
|
||||
|
||||
// Set up listeners on pause-pending ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
animation.cancel();
|
||||
}));
|
||||
}, 'ready promise is rejected when a pause-pending animation is cancelled by'
|
||||
+ ' calling cancel()');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'A new Promise object is generated when setting'
|
||||
+ ' animation-play-state: paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created when setting animation-play-state: paused');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
var firstReadyPromise = animation.ready;
|
||||
animation.pause();
|
||||
assert_equals(animation.ready, firstReadyPromise,
|
||||
'Ready promise objects are identical after redundant pause');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Pausing twice re-uses the same Promise');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
|
||||
// Flush style and verify we're pending at the same time
|
||||
assert_equals(animation.playState, 'pending', 'Animation is pending');
|
||||
var pauseReadyPromise = animation.ready;
|
||||
|
||||
// Now play again immediately
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_equals(animation.playState, 'pending', 'Animation is still pending');
|
||||
assert_equals(animation.ready, pauseReadyPromise,
|
||||
'The pause Promise is re-used when playing while waiting'
|
||||
+ ' to pause');
|
||||
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation is running after aborting a pause');
|
||||
t.done();
|
||||
}));
|
||||
}, 'If a pause operation is interrupted, the ready promise is reused');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: abc 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Promise received when ready Promise for a pause operation'
|
||||
+ ' is completed is the animation on which the pause was'
|
||||
+ ' performed');
|
||||
t.done();
|
||||
}));
|
||||
}, 'When a pause is complete the Promise callback gets the correct animation');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-ready.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -1,559 +1,15 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS animation's
|
||||
Animation.startTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 10px;
|
||||
/* Make it easier to calculate expected values: */
|
||||
animation-timing-function: linear ! important;
|
||||
}
|
||||
|
||||
@keyframes anim {
|
||||
from { margin-left: 100px; }
|
||||
to { margin-left: 200px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const CSS_ANIM_EVENTS =
|
||||
['animationstart', 'animationiteration', 'animationend'];
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
const ANIM_PROPERTY_VAL = 'anim ' + ANIM_DUR_MS + 'ms ' + ANIM_DELAY_MS + 'ms';
|
||||
|
||||
/**
|
||||
* These helpers get the value that the startTime needs to be set to, to put an
|
||||
* animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
|
||||
* middle of various phases or points through the active duration.
|
||||
*/
|
||||
function startTimeForBeforePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForActivePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
|
||||
}
|
||||
function startTimeForAfterPhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForStartOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS;
|
||||
}
|
||||
function startTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function startTimeForEndOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const UNANIMATED_POSITION = 10;
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
//
|
||||
// Note the distinction between the "animation start time" which occurs before
|
||||
// the start delay and the start of the active interval which occurs after it.
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
|
||||
'Animation.startTime should be less than the timeline\'s ' +
|
||||
'currentTime on the first paint tick after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" on the first paint tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, "running",
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"finished" at the end of the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by the animation at the end of the active duration when the ' +
|
||||
'animation-fill-mode is none');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created (play-pending) animation is unresolved');
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created (pause-pending) animation is unresolved');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_true(animation.startTime > 0,
|
||||
'startTime is resolved when running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime is resolved when running');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s paused' });
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'startTime is unresolved when paused');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime is unresolved when paused');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'paused';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'startTime is resolved when pause-pending');
|
||||
|
||||
div.style.animationPlayState = 'running';
|
||||
getComputedStyle(div).animationPlayState;
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'startTime is preserved when a pause is aborted');
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime while pause-pending and play-pending');
|
||||
|
||||
async_test(function(t)
|
||||
{
|
||||
var div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
// Seek to end to put us in the finished state
|
||||
// FIXME: Once we implement finish(), use that here.
|
||||
animation.currentTime = 100 * 1000;
|
||||
animation.ready.then(t.step_func(function() {
|
||||
// Call play() which puts us back in the running state
|
||||
animation.play();
|
||||
// FIXME: Enable this once we implement finishing behavior (bug 1074630)
|
||||
/*
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
*/
|
||||
t.done();
|
||||
}));
|
||||
}, 'startTime while play-pending from finished state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: anim 1000s' });
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(animation.startTime, null, 'The initial startTime is null');
|
||||
var initialTimelineTime = document.timeline.currentTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_true(animation.startTime > initialTimelineTime,
|
||||
'After the animation has started, startTime is greater than ' +
|
||||
'the time when it was started');
|
||||
var startTimeBeforePausing = animation.startTime;
|
||||
|
||||
div.style.animationPlayState = 'paused';
|
||||
// Flush styles just in case querying animation.startTime doesn't flush
|
||||
// styles (which would be a bug in of itself and could mask a further bug
|
||||
// by causing startTime to appear to not change).
|
||||
getComputedStyle(div).animationPlayState;
|
||||
|
||||
assert_equals(animation.startTime, startTimeBeforePausing,
|
||||
'The startTime does not change when pausing-pending');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'After actually pausing, the startTime of an animation ' +
|
||||
'is null');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Pausing should make the startTime become null');
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var currentTime = animation.timeline.currentTime;
|
||||
animation.startTime = currentTime;
|
||||
assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error
|
||||
'Check setting of startTime actually works');
|
||||
}, 'Sanity test to check round-tripping assigning to a new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-starttime.html");
|
||||
});
|
||||
}, 'Skipping forward through animation');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
|
||||
var previousTimelineTime = animation.timeline.currentTime;
|
||||
|
||||
// Skipping over the active interval will dispatch an 'animationstart' then
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
'firing after normal playback through the very long ' +
|
||||
'animation duration');
|
||||
|
||||
// Now we can start the tests for skipping backwards, but first we check
|
||||
// that after the events we're still in the same end time state:
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
|
||||
// Despite going backwards from after the end of the animation (to being
|
||||
// in the active interval), we now expect an 'animationstart' event
|
||||
// because the animation should go from being inactive to active.
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.wait_for('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
// Despite going backwards from just after the active interval starts to
|
||||
// the animation start time, we now expect an animationend event
|
||||
// because we went from inside to outside the active interval.
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
|
||||
// This must come after we've set up the Promise chain, since requesting
|
||||
// computed style will force events to be dispatched.
|
||||
// XXX For some reason this fails occasionally (either the animation.playState
|
||||
// check or the marginLeft check).
|
||||
//checkStateAtActiveIntervalEndTime(animation);
|
||||
}, 'Skipping backwards through animation');
|
||||
|
||||
|
||||
// Next we have multiple tests to check that redundant startTime changes do NOT
|
||||
// dispatch events. It's impossible to distinguish between events not being
|
||||
// dispatched and events just taking an incredibly long time to dispatch
|
||||
// without waiting an infinitely long time. Obviously we don't want to do that
|
||||
// (block this test from finishing forever), so instead we just listen for
|
||||
// events until two animation frames (i.e. requestAnimationFrame callbacks)
|
||||
// have happened, then assume that no events will ever be dispatched for the
|
||||
// redundant changes if no events were detected in that time.
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
}, 'Redundant change, active -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
}, 'Redundant change, after -> active, then back');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var storedCurrentTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
storedCurrentTime = animation.currentTime;
|
||||
animation.startTime = null;
|
||||
return animation.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Setting startTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var savedStartTime = animation.startTime;
|
||||
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'Animation.startTime not null on ready Promise resolve');
|
||||
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'Animation.startTime is null after paused');
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'Animation.playState is "paused" after pause() call');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.startTime after pausing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = 'anim 100s';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_equals(animation.startTime, null,
|
||||
'The startTime of a cancelled animation should be null');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.startTime after cancelling');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,158 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
to { left: 100px }
|
||||
}
|
||||
@keyframes anim2 { }
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
|
||||
var originalAnimation = div.getAnimations()[0];
|
||||
var originalStartTime;
|
||||
var originalCurrentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and doesn't
|
||||
// simply reflect the current time).
|
||||
originalAnimation.ready.then(function() {
|
||||
originalStartTime = originalAnimation.startTime;
|
||||
originalCurrentTime = originalAnimation.currentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and
|
||||
// doesn't simply reflect the current time).
|
||||
return waitForFrame();
|
||||
}).then(t.step_func(function() {
|
||||
div.style.animationDuration = '200s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation, originalAnimation,
|
||||
'The same Animation is returned after updating'
|
||||
+ ' animation duration');
|
||||
assert_equals(animation.startTime, originalStartTime,
|
||||
'Animations returned by getAnimations preserve'
|
||||
+ ' their startTime even when they are updated');
|
||||
// Sanity check
|
||||
assert_not_equals(animation.currentTime, originalCurrentTime,
|
||||
'Animation.currentTime has updated in next'
|
||||
+ ' requestAnimationFrame callback');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animations preserve their startTime when changed');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
var animations = div.getAnimations();
|
||||
var animation1 = animations[0];
|
||||
var animation2 = animations[1];
|
||||
|
||||
// Update first in list
|
||||
div.style.animationDuration = '200s, 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations[0], animation1,
|
||||
'First Animation is in same position after update');
|
||||
assert_equals(animations[1], animation2,
|
||||
'Second Animation is in same position after update');
|
||||
}, 'Updated Animations maintain their order in the list');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 200s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
var animations = div.getAnimations();
|
||||
var animation1 = animations[0];
|
||||
var animation2 = animations[1];
|
||||
|
||||
// Wait before continuing so we can compare start times (otherwise the
|
||||
// new Animation objects and existing Animation objects will all have the same
|
||||
// start time).
|
||||
waitForAllAnimations(animations).then(waitForFrame).then(t.step_func(function() {
|
||||
// Swap duration of first and second in list and prepend animation at the
|
||||
// same time
|
||||
div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
|
||||
animations = div.getAnimations();
|
||||
assert_true(animations[0] !== animation1 && animations[0] !== animation2,
|
||||
'New Animation is prepended to start of list');
|
||||
assert_equals(animations[1], animation1,
|
||||
'First Animation is in second position after update');
|
||||
assert_equals(animations[2], animation2,
|
||||
'Second Animation is in third position after update');
|
||||
assert_equals(animations[1].startTime, animations[2].startTime,
|
||||
'Old Animations have the same start time');
|
||||
// TODO: Check that animations[0].startTime === null
|
||||
return animations[0].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime > animations[1].startTime,
|
||||
'New Animation has later start time');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Only the startTimes of existing animations are preserved');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
var secondAnimation = div.getAnimations()[1];
|
||||
|
||||
// Wait before continuing so we can compare start times
|
||||
secondAnimation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
// Trim list of animations
|
||||
div.style.animationName = 'anim1';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1, 'List of Animations was trimmed');
|
||||
assert_equals(animations[0], secondAnimation,
|
||||
'Remaining Animation is the second one in the list');
|
||||
assert_equals(typeof(animations[0].startTime), 'number',
|
||||
'Remaining Animation has resolved startTime');
|
||||
assert_true(animations[0].startTime < animations[0].timeline.currentTime,
|
||||
'Remaining Animation preserves startTime');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animations are removed from the start of the list while preserving'
|
||||
+ ' the state of existing Animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var firstAddedAnimation = div.getAnimations()[0],
|
||||
secondAddedAnimation,
|
||||
animations;
|
||||
|
||||
// Wait and add second Animation
|
||||
firstAddedAnimation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
secondAddedAnimation = div.getAnimations()[0];
|
||||
|
||||
// Wait again and add another Animation
|
||||
return secondAddedAnimation.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_not_equals(firstAddedAnimation, secondAddedAnimation,
|
||||
'New Animations are added to start of the list');
|
||||
assert_equals(animations[0], secondAddedAnimation,
|
||||
'Second Animation remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_equals(animations[2], firstAddedAnimation,
|
||||
'First Animation remains in same position after'
|
||||
+ ' interleaving');
|
||||
return animations[1].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[1].startTime > animations[0].startTime,
|
||||
'Interleaved animation starts later than existing animations');
|
||||
assert_true(animations[0].startTime > animations[2].startTime,
|
||||
'Original animations retain their start time');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation state is preserved when interleaving animations in list');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animations-dynamic-changes.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,36 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes xyz {
|
||||
to { left: 100px }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'xyz 100s';
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'xyz',
|
||||
'Animation effect name matches keyframes rule name');
|
||||
}, 'Effect name makes keyframe rule');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\yz 100s';
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'xyz',
|
||||
'Escaped animation effect name matches keyframes rule name');
|
||||
}, 'Escaped animation name');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\79 z 100s';
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'xyz',
|
||||
'Hex-escaped animation name matches keyframes rule'
|
||||
+ ' name');
|
||||
}, 'Animation name with hex-escape');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_effect-name.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,20 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.effect.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
}, 'Returned CSS animations have the correct Animation.target');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_effect-target.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -2,271 +2,14 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
to { left: 100px }
|
||||
}
|
||||
@keyframes anim2 {
|
||||
to { top: 100px }
|
||||
}
|
||||
@keyframes multiPropAnim {
|
||||
to { background: green, opacity: 0.5, left: 100px, top: 100px }
|
||||
}
|
||||
@keyframes empty { }
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for an element'
|
||||
+ ' with no animations');
|
||||
}, 'getAnimations for non-animated content');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.animation = 'anim1 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns an Animation running CSS Animations');
|
||||
animations[0].ready.then(t.step_func(function() {
|
||||
var startTime = animations[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS animation has a sensible start time');
|
||||
|
||||
// Wait a moment then add a second animation.
|
||||
//
|
||||
// We wait for the next frame so that we can test that the start times of
|
||||
// the animations differ.
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations returns one Animation for each value of'
|
||||
+ ' animation-name');
|
||||
// Wait until both Animations are ready
|
||||
// (We don't make any assumptions about the order of the Animations since
|
||||
// that is the purpose of the following test.)
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime < animations[1].startTime,
|
||||
'Additional Animations for CSS animations start after the original'
|
||||
+ ' animation and appear later in the list');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS Animations');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation that targets multiple properties
|
||||
div.style.animation = 'multiPropAnim 100s';
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns only one Animation for a CSS Animation'
|
||||
+ ' that targets multiple properties');
|
||||
}, 'getAnimations for multi-property animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.backgroundColor = 'red';
|
||||
div.style.animation = 'anim1 100s';
|
||||
window.getComputedStyle(div).backgroundColor;
|
||||
|
||||
// Wait until a frame after the animation starts, then add a transition
|
||||
var animations = div.getAnimations();
|
||||
animations[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.backgroundColor = 'green';
|
||||
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations returns Animations for both animations and'
|
||||
+ ' transitions that run simultaneously');
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime > animations[1].startTime,
|
||||
'Animations for transitions appear before animations even if they'
|
||||
+ ' start later');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for both CSS Animations and Transitions at once');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations does not return Animations for finished '
|
||||
+ ' (and non-forwards-filling) CSS Animations');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Add a very short animation
|
||||
div.style.animation = 'anim1 0.01s';
|
||||
}, 'getAnimations for CSS Animations that have finished');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns Animations for CSS Animations that have'
|
||||
+ ' finished but are filling forwards');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Add a very short animation
|
||||
div.style.animation = 'anim1 0.01s forwards';
|
||||
}, 'getAnimations for CSS Animations that have finished but are'
|
||||
+ ' forwards filling');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'none 100s';
|
||||
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 0,
|
||||
'getAnimations returns an empty sequence for an element'
|
||||
+ ' with animation-name: none');
|
||||
|
||||
div.style.animation = 'none 100s, anim1 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns Animations only for those CSS Animations whose'
|
||||
+ ' animation-name is not none');
|
||||
}, 'getAnimations for CSS Animations with animation-name: none');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'missing 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 0,
|
||||
'getAnimations returns an empty sequence for an element'
|
||||
+ ' with animation-name: missing');
|
||||
|
||||
div.style.animation = 'anim1 100s, missing 100s';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns Animations only for those CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
}, 'getAnimations for CSS Animations with animation-name: missing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, notyet 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations initally only returns Animations for CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
|
||||
animations[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
var keyframes = '@keyframes notyet { to { left: 100px; } }';
|
||||
document.styleSheets[0].insertRule(keyframes, 0);
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations includes Animation when @keyframes rule is added'
|
||||
+ ' later');
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime < animations[1].startTime,
|
||||
'Newly added animation has a later start time');
|
||||
document.styleSheets[0].deleteRule(0);
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS Animations where the @keyframes rule is added'
|
||||
+ ' later');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
assert_equals(div.getAnimations().length, 2,
|
||||
'getAnimations returns one Animation for each CSS animation-name'
|
||||
+ ' even if the names are duplicated');
|
||||
}, 'getAnimations for CSS Animations with duplicated animation-name');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'empty 100s';
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns Animations for CSS animations with an'
|
||||
+ ' empty keyframes rule');
|
||||
}, 'getAnimations for CSS Animations with empty keyframes rule');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s 100s';
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1,
|
||||
'getAnimations returns animations for CSS animations whose'
|
||||
+ ' delay makes them start later');
|
||||
animations[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(animations[0].startTime <= document.timeline.currentTime,
|
||||
'For CSS Animations in delay phase, the start time of the Animation is'
|
||||
+ ' not in the future');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS animations in delay phase');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 0s 100s';
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns animations for CSS animations whose'
|
||||
+ ' duration is zero');
|
||||
div.remove();
|
||||
}, 'getAnimations for zero-duration CSS Animations');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var originalAnimation = div.getAnimations()[0];
|
||||
|
||||
// Update pause state (an Animation change)
|
||||
div.style.animationPlayState = 'paused';
|
||||
var pendingAnimation = div.getAnimations()[0];
|
||||
assert_equals(pendingAnimation.playState, 'pending',
|
||||
'animation\'s play state is updated');
|
||||
assert_equals(originalAnimation, pendingAnimation,
|
||||
'getAnimations returns the same objects even when their'
|
||||
+ ' play state changes');
|
||||
|
||||
// Update duration (an Animation change)
|
||||
div.style.animationDuration = '200s';
|
||||
var extendedAnimation = div.getAnimations()[0];
|
||||
// FIXME: Check extendedAnimation.effect.timing.duration has changed once the
|
||||
// API is available
|
||||
assert_equals(originalAnimation, extendedAnimation,
|
||||
'getAnimations returns the same objects even when their'
|
||||
+ ' duration changes');
|
||||
}, 'getAnimations returns objects with the same identity');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns an animation before cancelling');
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.cancel();
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations does not return cancelled animations');
|
||||
|
||||
animation.play();
|
||||
assert_equals(div.getAnimations().length, 1,
|
||||
'getAnimations returns cancelled animations that have been re-started');
|
||||
|
||||
}, 'getAnimations for CSS Animations that are cancelled');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_element-get-animations.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'transform style is animated before cancelling');
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
|
||||
'transform style is no longer animated after cancelling');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animated style is cleared after cancelling a running CSS transition');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.addEventListener('transitionend', t.step_func(function() {
|
||||
assert_unreached('Got unexpected end event on cancelled transition');
|
||||
}));
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
// Seek to just before the end then cancel
|
||||
animation.currentTime = 99.9 * 1000;
|
||||
animation.cancel();
|
||||
|
||||
// Then wait a couple of frames and check that no event was dispatched
|
||||
return waitForAnimationFrames(2);
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Cancelled CSS transitions do not dispatch events');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
animation.play();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is animated after re-starting transition');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Transition succeeds in running after being re-started');
|
||||
t.done();
|
||||
}));
|
||||
}, 'After cancelling a transition, it can still be re-used');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.finish();
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
animation.play();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is animated after re-starting transition');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Transition succeeds in running after being re-started');
|
||||
t.done();
|
||||
}));
|
||||
}, 'After cancelling a finished transition, it can still be re-used');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
// Trigger a change to a transition property and check that this
|
||||
// doesn't cause the animation to become live again
|
||||
div.style.transitionDuration = '200s';
|
||||
flushComputedStyle(div);
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is still not animated after updating'
|
||||
+ ' transition-duration');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Transition is still idle after updating transition-duration');
|
||||
}, 'After cancelling a transition, updating transition properties doesn\'t make'
|
||||
+ ' it live again');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,313 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS transition's
|
||||
Animation.currentTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
/**
|
||||
* These helpers get the value that the currentTime needs to be set to, to put
|
||||
* an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
|
||||
* the middle of various phases or points through the active duration.
|
||||
*/
|
||||
function currentTimeForBeforePhase() {
|
||||
return ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForActivePhase() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
|
||||
}
|
||||
function currentTimeForAfterPhase() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForStartOfActiveInterval() {
|
||||
return ANIM_DELAY_MS;
|
||||
}
|
||||
function currentTimeForFiftyPercentThroughActiveInterval() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function currentTimeForEndOfActiveInterval() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// http://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
|
||||
// Called when currentTime is set to zero (the beginning of the start delay).
|
||||
function checkStateOnSettingCurrentTimeToZero(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the start delay');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'at the beginning of the start delay');
|
||||
}
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
// the 0.0001 here is for rounding error
|
||||
assert_less_than_equal(animation.currentTime,
|
||||
animation.timeline.currentTime - animation.startTime + 0.0001,
|
||||
'Animation.currentTime should be less than the local time ' +
|
||||
'equivalent of the timeline\'s currentTime on the first paint tick ' +
|
||||
'after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, INITIAL_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, END_POSITION,
|
||||
'the computed value of margin-left should be the final transitioned-to ' +
|
||||
'value at the end of the active duration');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.currentTime, 0, 'currentTime should be zero');
|
||||
}, 'currentTime of a newly created transition is zero');
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// So that animation is running instead of paused when we set currentTime:
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
|
||||
animation.currentTime = 10;
|
||||
assert_equals(animation.currentTime, 10,
|
||||
'Check setting of currentTime actually works');
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'currentTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.currentTime = currentTimeForStartOfActiveInterval();
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime = currentTimeForEndOfActiveInterval();
|
||||
return eventWatcher.wait_for('transitionend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Skipping forward through transition');
|
||||
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// Unlike in the case of CSS animations, we cannot skip to the end and skip
|
||||
// backwards since when we reach the end the transition effect is removed and
|
||||
// changes to the Animation object no longer affect the element. For
|
||||
// this reason we only skip forwards as far as the 50% through point.
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime = currentTimeForStartOfActiveInterval();
|
||||
|
||||
// Despite going backwards from being in the active interval to being
|
||||
// before it, we now expect a 'transitionend' event because the transition
|
||||
// should go from being active to inactive.
|
||||
//
|
||||
// Calling checkStateAtActiveIntervalStartTime will check computed style,
|
||||
// causing computed style to be updated and the 'transitionend' event to
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
eventWatcher.wait_for('transitionend').then(function() {
|
||||
t.done();
|
||||
});
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
}));
|
||||
}, 'Skipping backwards through transition');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var exception;
|
||||
try {
|
||||
animation.currentTime = null;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
assert_equals(exception.name, 'TypeError',
|
||||
'Expect TypeError exception on trying to set ' +
|
||||
'Animation.currentTime to null');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Setting currentTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var pauseTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(animation.currentTime, null,
|
||||
'Animation.currentTime not null on ready Promise resolve');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
pauseTime = animation.currentTime;
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, pauseTime,
|
||||
'Animation.currentTime is unchanged after pausing');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.currentTime after pausing');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,61 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
'use strict';
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.finish();
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.play();
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Replaying a finished transition should reset its ' +
|
||||
'currentTime');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a finished transition');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = -1;
|
||||
return animation.finished;
|
||||
}).then(t.step_func(function() {
|
||||
animation.play();
|
||||
// FIXME: once animation.effect.computedTiming.endTime is available (bug
|
||||
// 1108055) we should use that here.
|
||||
assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
|
||||
'Replaying a finished reversed transition should reset ' +
|
||||
'its currentTime to the end of the effect');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a reversed finished transition');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,50 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
div.style.marginLeft = '0px';
|
||||
cs.marginLeft; // Flush style to set up transition start point
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '10000px';
|
||||
cs.marginLeft;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
animation.play();
|
||||
return animation.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left increases after calling play()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() and play() a transition');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,96 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.transform = 'translate(0px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
div.style.transition = 'transform 100s';
|
||||
div.style.transform = 'translate(10px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var originalReadyPromise = animation.ready;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
animation.pause();
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'Ready promise object identity differs when pausing');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created each time play() is called'
|
||||
+ ' the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending transition
|
||||
div.style.transform = 'translate(0px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
div.style.transition = 'transform 100s';
|
||||
div.style.transform = 'translate(10px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending', 'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is idle after transition was cancelled');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now remove transform from transition-property and flush styles
|
||||
div.style.transitionProperty = 'none';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
|
||||
}, 'ready promise is rejected when a transition is cancelled by updating'
|
||||
+ ' transition-property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending transition
|
||||
div.style.marginLeft = '0px';
|
||||
window.getComputedStyle(div).marginLeft;
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '100px';
|
||||
window.getComputedStyle(div).marginLeft;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending', 'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is idle after transition was cancelled');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now update the transition to animate to something not-interpolable
|
||||
div.style.marginLeft = 'auto';
|
||||
window.getComputedStyle(div).marginLeft;
|
||||
|
||||
}, 'ready promise is rejected when a transition is cancelled by changing'
|
||||
+ ' the transition property to something not interpolable');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,290 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS transition's
|
||||
Animation.startTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
/**
|
||||
* These helpers get the value that the startTime needs to be set to, to put an
|
||||
* animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
|
||||
* middle of various phases or points through the active duration.
|
||||
*/
|
||||
function startTimeForBeforePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForActivePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
|
||||
}
|
||||
function startTimeForAfterPhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForStartOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS;
|
||||
}
|
||||
function startTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function startTimeForEndOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
//
|
||||
// Note the distinction between the "animation start time" which occurs before
|
||||
// the start delay and the start of the active interval which occurs after it.
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
|
||||
'Animation.startTime should be less than the timeline\'s ' +
|
||||
'currentTime on the first paint tick after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, INITIAL_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, END_POSITION,
|
||||
'the computed value of margin-left should be the final transitioned-to ' +
|
||||
'value at the end of the active duration');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created transition is unresolved');
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var currentTime = animation.timeline.currentTime;
|
||||
animation.startTime = currentTime;
|
||||
assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error
|
||||
'Check setting of startTime actually works');
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('transitionend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Skipping forward through animation');
|
||||
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// Unlike in the case of CSS animations, we cannot skip to the end and skip
|
||||
// backwards since when we reach the end the transition effect is removed and
|
||||
// changes to the Animation object no longer affect the element. For
|
||||
// this reason we only skip forwards as far as the 90% through point.
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
|
||||
// Despite going backwards from being in the active interval to being before
|
||||
// it, we now expect an 'animationend' event because the animation should go
|
||||
// from being active to inactive.
|
||||
//
|
||||
// Calling checkStateAtActiveIntervalStartTime will check computed style,
|
||||
// causing computed style to be updated and the 'transitionend' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
eventWatcher.wait_for('transitionend').then(function() {
|
||||
t.done();
|
||||
});
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
}, 'Skipping backwards through transition');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var storedCurrentTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
storedCurrentTime = animation.currentTime;
|
||||
animation.startTime = null;
|
||||
return animation.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Setting startTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var savedStartTime = animation.startTime;
|
||||
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'Animation.startTime not null on ready Promise resolve');
|
||||
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'Animation.startTime is null after paused');
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'Animation.playState is "paused" after pause() call');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.startTime after paused');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,24 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a transition
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.left = '100px';
|
||||
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'left',
|
||||
'The name for the transitions corresponds to the property ' +
|
||||
'being transitioned');
|
||||
}, 'Effect name for transitions');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
div.style.transition = 'left 100s';
|
||||
div.style.left = '100px';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.effect.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
}, 'Returned CSS transitions have the correct Animation.target');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,94 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a couple of transitions
|
||||
div.style.left = '0px';
|
||||
div.style.top = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.left = '100px';
|
||||
div.style.top = '100px';
|
||||
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations() returns one Animation per transitioning property');
|
||||
waitForAllAnimations(animations).then(t.step_func(function() {
|
||||
var startTime = animations[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS transitions have sensible start times');
|
||||
assert_equals(animations[0].startTime, animations[1].startTime,
|
||||
'CSS transitions started together have the same start time');
|
||||
// Wait a moment then add a third transition
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.backgroundColor = 'green';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 3,
|
||||
'getAnimations returns Animations for all running CSS Transitions');
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[1].startTime < animations[2].startTime,
|
||||
'Animation for additional CSS transition starts after the original'
|
||||
+ ' transitions and appears later in the list');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS Transitions');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('transitionend', t.step_func(function() {
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations does not return finished CSS Transitions');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Add a very short transition
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).left;
|
||||
|
||||
div.style.transition = 'all 0.01s';
|
||||
div.style.left = '100px';
|
||||
window.getComputedStyle(div).left;
|
||||
}, 'getAnimations for CSS Transitions that have finished');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Try to transition non-animatable property animation-duration
|
||||
div.style.animationDuration = '10s';
|
||||
window.getComputedStyle(div).animationDuration;
|
||||
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.animationDuration = '100s';
|
||||
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for a transition'
|
||||
+ ' of a non-animatable property');
|
||||
}, 'getAnimations for transition on non-animatable property');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.setProperty('-vendor-unsupported', '0px', '');
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.setProperty('-vendor-unsupported', '100px', '');
|
||||
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for a transition'
|
||||
+ ' of an unsupported property');
|
||||
}, 'getAnimations for transition on unsupported property');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -2,128 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'transform style is animated before cancelling');
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
|
||||
'transform style is no longer animated after cancelling');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animated style is cleared after cancelling a running CSS transition');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.addEventListener('transitionend', t.step_func(function() {
|
||||
assert_unreached('Got unexpected end event on cancelled transition');
|
||||
}));
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
// Seek to just before the end then cancel
|
||||
animation.currentTime = 99.9 * 1000;
|
||||
animation.cancel();
|
||||
|
||||
// Then wait a couple of frames and check that no event was dispatched
|
||||
return waitForAnimationFrames(2);
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
}, 'Cancelled CSS transitions do not dispatch events');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
animation.play();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is animated after re-starting transition');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Transition succeeds in running after being re-started');
|
||||
t.done();
|
||||
}));
|
||||
}, 'After cancelling a transition, it can still be re-used');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.finish();
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
animation.play();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||
'margin-left style is animated after re-starting transition');
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Transition succeeds in running after being re-started');
|
||||
t.done();
|
||||
}));
|
||||
}, 'After cancelling a finished transition, it can still be re-used');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'margin-left: 0px' });
|
||||
flushComputedStyle(div);
|
||||
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '1000px';
|
||||
flushComputedStyle(div);
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.cancel();
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is not animated after cancelling');
|
||||
|
||||
// Trigger a change to a transition property and check that this
|
||||
// doesn't cause the animation to become live again
|
||||
div.style.transitionDuration = '200s';
|
||||
flushComputedStyle(div);
|
||||
assert_equals(getComputedStyle(div).marginLeft, '1000px',
|
||||
'margin-left style is still not animated after updating'
|
||||
+ ' transition-duration');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Transition is still idle after updating transition-duration');
|
||||
}, 'After cancelling a transition, updating transition properties doesn\'t make'
|
||||
+ ' it live again');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-cancel.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -1,315 +1,14 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS transition's
|
||||
Animation.currentTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
/**
|
||||
* These helpers get the value that the currentTime needs to be set to, to put
|
||||
* an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
|
||||
* the middle of various phases or points through the active duration.
|
||||
*/
|
||||
function currentTimeForBeforePhase() {
|
||||
return ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForActivePhase() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
|
||||
}
|
||||
function currentTimeForAfterPhase() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function currentTimeForStartOfActiveInterval() {
|
||||
return ANIM_DELAY_MS;
|
||||
}
|
||||
function currentTimeForFiftyPercentThroughActiveInterval() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function currentTimeForEndOfActiveInterval() {
|
||||
return ANIM_DELAY_MS + ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// http://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
|
||||
// Called when currentTime is set to zero (the beginning of the start delay).
|
||||
function checkStateOnSettingCurrentTimeToZero(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
assert_equals(animation.effect.target.style.animationPlayState, 'running',
|
||||
'Animation.effect.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the start delay');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, UNANIMATED_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'at the beginning of the start delay');
|
||||
}
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
// the 0.0001 here is for rounding error
|
||||
assert_less_than_equal(animation.currentTime,
|
||||
animation.timeline.currentTime - animation.startTime + 0.0001,
|
||||
'Animation.currentTime should be less than the local time ' +
|
||||
'equivalent of the timeline\'s currentTime on the first paint tick ' +
|
||||
'after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, INITIAL_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when currentTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, END_POSITION,
|
||||
'the computed value of margin-left should be the final transitioned-to ' +
|
||||
'value at the end of the active duration');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.currentTime, 0, 'currentTime should be zero');
|
||||
}, 'currentTime of a newly created transition is zero');
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// So that animation is running instead of paused when we set currentTime:
|
||||
animation.startTime = animation.timeline.currentTime;
|
||||
|
||||
animation.currentTime = 10;
|
||||
assert_equals(animation.currentTime, 10,
|
||||
'Check setting of currentTime actually works');
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'currentTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.currentTime = currentTimeForStartOfActiveInterval();
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime = currentTimeForEndOfActiveInterval();
|
||||
return eventWatcher.wait_for('transitionend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-currenttime.html");
|
||||
});
|
||||
}, 'Skipping forward through transition');
|
||||
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// Unlike in the case of CSS animations, we cannot skip to the end and skip
|
||||
// backwards since when we reach the end the transition effect is removed and
|
||||
// changes to the Animation object no longer affect the element. For
|
||||
// this reason we only skip forwards as far as the 50% through point.
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime = currentTimeForStartOfActiveInterval();
|
||||
|
||||
// Despite going backwards from being in the active interval to being
|
||||
// before it, we now expect a 'transitionend' event because the transition
|
||||
// should go from being active to inactive.
|
||||
//
|
||||
// Calling checkStateAtActiveIntervalStartTime will check computed style,
|
||||
// causing computed style to be updated and the 'transitionend' event to
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
eventWatcher.wait_for('transitionend').then(function() {
|
||||
t.done();
|
||||
});
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
}));
|
||||
}, 'Skipping backwards through transition');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var exception;
|
||||
try {
|
||||
animation.currentTime = null;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
assert_equals(exception.name, 'TypeError',
|
||||
'Expect TypeError exception on trying to set ' +
|
||||
'Animation.currentTime to null');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Setting currentTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var pauseTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_not_equals(animation.currentTime, null,
|
||||
'Animation.currentTime not null on ready Promise resolve');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
pauseTime = animation.currentTime;
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, pauseTime,
|
||||
'Animation.currentTime is unchanged after pausing');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.currentTime after pausing');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</script>
|
||||
|
|
|
@ -2,60 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
'use strict';
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.finish();
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.play();
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Replaying a finished transition should reset its ' +
|
||||
'currentTime');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a finished transition');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = -1;
|
||||
return animation.finished;
|
||||
}).then(t.step_func(function() {
|
||||
animation.play();
|
||||
// FIXME: once animation.effect.computedTiming.endTime is available (bug
|
||||
// 1108055) we should use that here.
|
||||
assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
|
||||
'Replaying a finished reversed transition should reset ' +
|
||||
'its currentTime to the end of the effect');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a reversed finished transition');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-finished.html");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -2,49 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
div.style.marginLeft = '0px';
|
||||
cs.marginLeft; // Flush style to set up transition start point
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '10000px';
|
||||
cs.marginLeft;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
animation.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
animation.play();
|
||||
return animation.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left increases after calling play()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'pause() and play() a transition');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-pausing.html");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -2,95 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.transform = 'translate(0px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
div.style.transition = 'transform 100s';
|
||||
div.style.transform = 'translate(10px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var originalReadyPromise = animation.ready;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_equals(animation.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
animation.pause();
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'Ready promise object identity differs when pausing');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created each time play() is called'
|
||||
+ ' the animation property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending transition
|
||||
div.style.transform = 'translate(0px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
div.style.transition = 'transform 100s';
|
||||
div.style.transform = 'translate(10px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending', 'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is idle after transition was cancelled');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now remove transform from transition-property and flush styles
|
||||
div.style.transitionProperty = 'none';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
|
||||
}, 'ready promise is rejected when a transition is cancelled by updating'
|
||||
+ ' transition-property');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up pending transition
|
||||
div.style.marginLeft = '0px';
|
||||
window.getComputedStyle(div).marginLeft;
|
||||
div.style.transition = 'margin-left 100s';
|
||||
div.style.marginLeft = '100px';
|
||||
window.getComputedStyle(div).marginLeft;
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.playState, 'pending', 'Animation is initially pending');
|
||||
|
||||
// Set up listeners on ready promise
|
||||
animation.ready.then(t.step_func(function() {
|
||||
assert_unreached('ready promise was fulfilled');
|
||||
})).catch(t.step_func(function(err) {
|
||||
assert_equals(err.name, 'AbortError',
|
||||
'ready promise is rejected with AbortError');
|
||||
assert_equals(animation.playState, 'idle',
|
||||
'Animation is idle after transition was cancelled');
|
||||
})).then(t.step_func(function() {
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Now update the transition to animate to something not-interpolable
|
||||
div.style.marginLeft = 'auto';
|
||||
window.getComputedStyle(div).marginLeft;
|
||||
|
||||
}, 'ready promise is rejected when a transition is cancelled by changing'
|
||||
+ ' the transition property to something not interpolable');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-ready.html");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -1,292 +1,14 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS transition's
|
||||
Animation.startTime</title>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// TODO: add equivalent tests without an animation-delay, but first we need to
|
||||
// change the timing of animationstart dispatch. (Right now the animationstart
|
||||
// event will fire before the ready Promise is resolved if there is no
|
||||
// animation-delay.)
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1134163
|
||||
|
||||
// TODO: Once the computedTiming property is implemented, add checks to the
|
||||
// checker helpers to ensure that computedTiming's properties are updated as
|
||||
// expected.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
|
||||
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
/**
|
||||
* These helpers get the value that the startTime needs to be set to, to put an
|
||||
* animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
|
||||
* middle of various phases or points through the active duration.
|
||||
*/
|
||||
function startTimeForBeforePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForActivePhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
|
||||
}
|
||||
function startTimeForAfterPhase(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
|
||||
}
|
||||
function startTimeForStartOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS;
|
||||
}
|
||||
function startTimeForFiftyPercentThroughActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
|
||||
}
|
||||
function startTimeForEndOfActiveInterval(timeline) {
|
||||
return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
|
||||
}
|
||||
|
||||
|
||||
// Expected computed 'margin-left' values at points during the active interval:
|
||||
// When we assert_between_inclusive using these values we could in theory cause
|
||||
// intermittent failure due to very long delays between paints, but since the
|
||||
// active duration is 1000s long, a delay would need to be around 100s to cause
|
||||
// that. If that's happening then there are likely other issues that should be
|
||||
// fixed, so a failure to make us look into that seems like a good thing.
|
||||
const INITIAL_POSITION = 100;
|
||||
const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
//
|
||||
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
|
||||
//
|
||||
// Note the distinction between the "animation start time" which occurs before
|
||||
// the start delay and the start of the active interval which occurs after it.
|
||||
|
||||
// Called when the ready Promise's callbacks should happen
|
||||
function checkStateOnReadyPromiseResolved(animation)
|
||||
{
|
||||
assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
|
||||
'Animation.startTime should be less than the timeline\'s ' +
|
||||
'currentTime on the first paint tick after animation creation');
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, INITIAL_POSITION,
|
||||
'the computed value of margin-left should be unaffected ' +
|
||||
'by an animation with a delay on ready Promise resolve');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval starts.
|
||||
function checkStateAtActiveIntervalStartTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'running',
|
||||
'Animation.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
|
||||
'the computed value of margin-left should be close to the value at the ' +
|
||||
'beginning of the animation');
|
||||
}
|
||||
|
||||
function checkStateAtFiftyPctOfActiveInterval(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, FIFTY_PCT_POSITION,
|
||||
'the computed value of margin-left should be half way through the ' +
|
||||
'animation at the midpoint of the active interval');
|
||||
}
|
||||
|
||||
// Called when startTime is set to the time the active interval ends.
|
||||
function checkStateAtActiveIntervalEndTime(animation)
|
||||
{
|
||||
// We don't test animation.startTime since our caller just set it.
|
||||
|
||||
assert_equals(animation.playState, 'finished',
|
||||
'Animation.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
var div = animation.effect.target;
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, END_POSITION,
|
||||
'the computed value of margin-left should be the final transitioned-to ' +
|
||||
'value at the end of the active duration');
|
||||
}
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.startTime, null, 'startTime is unresolved');
|
||||
}, 'startTime of a newly created transition is unresolved');
|
||||
|
||||
|
||||
test(function(t)
|
||||
{
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
var currentTime = animation.timeline.currentTime;
|
||||
animation.startTime = currentTime;
|
||||
assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error
|
||||
'Check setting of startTime actually works');
|
||||
}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
|
||||
'startTime');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.wait_for('transitionend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_animation-starttime.html");
|
||||
});
|
||||
}, 'Skipping forward through animation');
|
||||
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
// Unlike in the case of CSS animations, we cannot skip to the end and skip
|
||||
// backwards since when we reach the end the transition effect is removed and
|
||||
// changes to the Animation object no longer affect the element. For
|
||||
// this reason we only skip forwards as far as the 90% through point.
|
||||
|
||||
animation.startTime =
|
||||
startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
|
||||
// Despite going backwards from being in the active interval to being before
|
||||
// it, we now expect an 'animationend' event because the animation should go
|
||||
// from being active to inactive.
|
||||
//
|
||||
// Calling checkStateAtActiveIntervalStartTime will check computed style,
|
||||
// causing computed style to be updated and the 'transitionend' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
eventWatcher.wait_for('transitionend').then(function() {
|
||||
t.done();
|
||||
});
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
}, 'Skipping backwards through transition');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var storedCurrentTime;
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
storedCurrentTime = animation.currentTime;
|
||||
animation.startTime = null;
|
||||
return animation.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Setting startTime to null');
|
||||
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var savedStartTime = animation.startTime;
|
||||
|
||||
assert_not_equals(animation.startTime, null,
|
||||
'Animation.startTime not null on ready Promise resolve');
|
||||
|
||||
animation.pause();
|
||||
return animation.ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.startTime, null,
|
||||
'Animation.startTime is null after paused');
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'Animation.playState is "paused" after pause() call');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'Animation.startTime after paused');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</script>
|
||||
|
|
|
@ -2,23 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a transition
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.left = '100px';
|
||||
|
||||
assert_equals(div.getAnimations()[0].effect.name, 'left',
|
||||
'The name for the transitions corresponds to the property ' +
|
||||
'being transitioned');
|
||||
}, 'Effect name for transitions');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_effect-name.html");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -2,22 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
div.style.transition = 'left 100s';
|
||||
div.style.left = '100px';
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
assert_equals(animation.effect.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
}, 'Returned CSS transitions have the correct Animation.target');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_effect-target.html");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -2,93 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a couple of transitions
|
||||
div.style.left = '0px';
|
||||
div.style.top = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.left = '100px';
|
||||
div.style.top = '100px';
|
||||
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations() returns one Animation per transitioning property');
|
||||
waitForAllAnimations(animations).then(t.step_func(function() {
|
||||
var startTime = animations[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS transitions have sensible start times');
|
||||
assert_equals(animations[0].startTime, animations[1].startTime,
|
||||
'CSS transitions started together have the same start time');
|
||||
// Wait a moment then add a third transition
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.backgroundColor = 'green';
|
||||
animations = div.getAnimations();
|
||||
assert_equals(animations.length, 3,
|
||||
'getAnimations returns Animations for all running CSS Transitions');
|
||||
return waitForAllAnimations(animations);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(animations[1].startTime < animations[2].startTime,
|
||||
'Animation for additional CSS transition starts after the original'
|
||||
+ ' transitions and appears later in the list');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimations for CSS Transitions');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('transitionend', t.step_func(function() {
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations does not return finished CSS Transitions');
|
||||
t.done();
|
||||
}));
|
||||
|
||||
// Add a very short transition
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).left;
|
||||
|
||||
div.style.transition = 'all 0.01s';
|
||||
div.style.left = '100px';
|
||||
window.getComputedStyle(div).left;
|
||||
}, 'getAnimations for CSS Transitions that have finished');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Try to transition non-animatable property animation-duration
|
||||
div.style.animationDuration = '10s';
|
||||
window.getComputedStyle(div).animationDuration;
|
||||
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.animationDuration = '100s';
|
||||
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for a transition'
|
||||
+ ' of a non-animatable property');
|
||||
}, 'getAnimations for transition on non-animatable property');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.setProperty('-vendor-unsupported', '0px', '');
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.setProperty('-vendor-unsupported', '100px', '');
|
||||
|
||||
assert_equals(div.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for a transition'
|
||||
+ ' of an unsupported property');
|
||||
}, 'getAnimations for transition on unsupported property');
|
||||
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_element-get-animations.html");
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Web Animations API: DocumentTimeline tests</title>
|
||||
<script src="../testcommon.js"></script>
|
||||
<iframe src="data:text/html;charset=utf-8," width="10" height="10" id="iframe"></iframe>
|
||||
<iframe src="data:text/html;charset=utf-8,%3Chtml%20style%3D%22display%3Anone%22%3E%3C%2Fhtml%3E" width="10" height="10" id="hidden-iframe"></iframe>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function() {
|
||||
assert_equals(document.timeline, document.timeline,
|
||||
'document.timeline returns the same object every time');
|
||||
var iframe = document.getElementById('iframe');
|
||||
assert_not_equals(document.timeline, iframe.contentDocument.timeline,
|
||||
'document.timeline returns a different object for each document');
|
||||
assert_not_equals(iframe.contentDocument.timeline, null,
|
||||
'document.timeline on an iframe is not null');
|
||||
},
|
||||
'document.timeline identity tests',
|
||||
{
|
||||
help: 'http://dev.w3.org/fxtf/web-animations/#the-document-timeline',
|
||||
assert: [ 'Each document has a timeline called the document timeline' ],
|
||||
author: 'Brian Birtles'
|
||||
});
|
||||
|
||||
async_test(function(t) {
|
||||
assert_true(document.timeline.currentTime > 0,
|
||||
'document.timeline.currentTime is positive');
|
||||
// document.timeline.currentTime should be set even before document
|
||||
// load fires. We expect this code to be run before document load and hence
|
||||
// the above assertion is sufficient.
|
||||
// If the following assertion fails, this test needs to be redesigned.
|
||||
assert_true(document.readyState !== 'complete',
|
||||
'Test is running prior to document load');
|
||||
|
||||
// Test that the document timeline's current time is measured from
|
||||
// navigationStart.
|
||||
//
|
||||
// We can't just compare document.timeline.currentTime to
|
||||
// window.performance.now() because currentTime is only updated on a sample
|
||||
// so we use requestAnimationFrame instead.
|
||||
window.requestAnimationFrame(t.step_func(function(rafTime) {
|
||||
assert_equals(document.timeline.currentTime, rafTime,
|
||||
'document.timeline.currentTime matches' +
|
||||
' requestAnimationFrame time');
|
||||
t.done();
|
||||
}));
|
||||
},
|
||||
'document.timeline.currentTime value tests',
|
||||
{
|
||||
help: [
|
||||
'http://dev.w3.org/fxtf/web-animations/#the-global-clock',
|
||||
'http://dev.w3.org/fxtf/web-animations/#the-document-timeline'
|
||||
],
|
||||
assert: [
|
||||
'The global clock is a source of monotonically increasing time values',
|
||||
'The time values of the document timeline are calculated as a fixed' +
|
||||
' offset from the global clock',
|
||||
'the zero time corresponds to the navigationStart moment',
|
||||
'the time value of each document timeline must be equal to the time ' +
|
||||
'passed to animation frame request callbacks for that browsing context'
|
||||
],
|
||||
author: 'Brian Birtles'
|
||||
});
|
||||
|
||||
async_test(function(t) {
|
||||
var valueAtStart = document.timeline.currentTime;
|
||||
var timeAtStart = window.performance.now();
|
||||
while (window.performance.now() - timeAtStart < 100) {
|
||||
// Wait 100ms
|
||||
}
|
||||
assert_equals(document.timeline.currentTime, valueAtStart,
|
||||
'document.timeline.currentTime does not change within a script block');
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime > valueAtStart,
|
||||
'document.timeline.currentTime increases between script blocks');
|
||||
t.done();
|
||||
}));
|
||||
},
|
||||
'document.timeline.currentTime liveness tests',
|
||||
{
|
||||
help: 'http://dev.w3.org/fxtf/web-animations/#script-execution-and-live-updates-to-the-model',
|
||||
assert: [ 'The value returned by the currentTime attribute of a' +
|
||||
' document timeline will not change within a script block' ],
|
||||
author: 'Brian Birtles'
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var hiddenIFrame = document.getElementById('hidden-iframe');
|
||||
assert_equals(typeof hiddenIFrame.contentDocument.timeline.currentTime,
|
||||
'number',
|
||||
'currentTime of an initially hidden subframe\'s timeline is a number');
|
||||
assert_true(hiddenIFrame.contentDocument.timeline.currentTime >= 0,
|
||||
'currentTime of an initially hidden subframe\'s timeline is >= 0');
|
||||
}, 'document.timeline.currentTime hidden subframe test');
|
||||
|
||||
async_test(function(t) {
|
||||
var hiddenIFrame = document.getElementById('hidden-iframe');
|
||||
|
||||
// Don't run the test until after the iframe has completed loading or else the
|
||||
// contentDocument may change.
|
||||
var testToRunOnLoad = t.step_func(function() {
|
||||
// Remove display:none
|
||||
hiddenIFrame.style.display = 'block';
|
||||
window.getComputedStyle(hiddenIFrame).display;
|
||||
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
assert_true(hiddenIFrame.contentDocument.timeline.currentTime > 0,
|
||||
'document.timeline.currentTime is positive after removing'
|
||||
+ ' display:none');
|
||||
var previousValue = hiddenIFrame.contentDocument.timeline.currentTime;
|
||||
|
||||
// Re-introduce display:none
|
||||
hiddenIFrame.style.display = 'none';
|
||||
window.getComputedStyle(hiddenIFrame).display;
|
||||
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
assert_true(
|
||||
hiddenIFrame.contentDocument.timeline.currentTime >= previousValue,
|
||||
'document.timeline.currentTime does not go backwards after'
|
||||
+ ' re-setting display:none');
|
||||
t.done();
|
||||
}));
|
||||
}));
|
||||
});
|
||||
|
||||
if (hiddenIFrame.contentDocument.readyState === 'complete') {
|
||||
testToRunOnLoad();
|
||||
} else {
|
||||
hiddenIFrame.addEventListener("load", testToRunOnLoad);
|
||||
}
|
||||
}, 'document.timeline.currentTime hidden subframe dynamic test');
|
||||
|
||||
done();
|
||||
</script>
|
|
@ -1,136 +1,14 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>Web Animations API: DocumentTimeline tests</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<iframe src="data:text/html;charset=utf-8," width="10" height="10" id="iframe"></iframe>
|
||||
<iframe src="data:text/html;charset=utf-8,%3Chtml%20style%3D%22display%3Anone%22%3E%3C%2Fhtml%3E" width="10" height="10" id="hidden-iframe"></iframe>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function() {
|
||||
assert_equals(document.timeline, document.timeline,
|
||||
'document.timeline returns the same object every time');
|
||||
var iframe = document.getElementById('iframe');
|
||||
assert_not_equals(document.timeline, iframe.contentDocument.timeline,
|
||||
'document.timeline returns a different object for each document');
|
||||
assert_not_equals(iframe.contentDocument.timeline, null,
|
||||
'document.timeline on an iframe is not null');
|
||||
},
|
||||
'document.timeline identity tests',
|
||||
{
|
||||
help: 'http://dev.w3.org/fxtf/web-animations/#the-document-timeline',
|
||||
assert: [ 'Each document has a timeline called the document timeline' ],
|
||||
author: 'Brian Birtles'
|
||||
});
|
||||
|
||||
async_test(function(t) {
|
||||
assert_true(document.timeline.currentTime > 0,
|
||||
'document.timeline.currentTime is positive');
|
||||
// document.timeline.currentTime should be set even before document
|
||||
// load fires. We expect this code to be run before document load and hence
|
||||
// the above assertion is sufficient.
|
||||
// If the following assertion fails, this test needs to be redesigned.
|
||||
assert_true(document.readyState !== 'complete',
|
||||
'Test is running prior to document load');
|
||||
|
||||
// Test that the document timeline's current time is measured from
|
||||
// navigationStart.
|
||||
//
|
||||
// We can't just compare document.timeline.currentTime to
|
||||
// window.performance.now() because currentTime is only updated on a sample
|
||||
// so we use requestAnimationFrame instead.
|
||||
window.requestAnimationFrame(t.step_func(function(rafTime) {
|
||||
assert_equals(document.timeline.currentTime, rafTime,
|
||||
'document.timeline.currentTime matches' +
|
||||
' requestAnimationFrame time');
|
||||
t.done();
|
||||
}));
|
||||
},
|
||||
'document.timeline.currentTime value tests',
|
||||
{
|
||||
help: [
|
||||
'http://dev.w3.org/fxtf/web-animations/#the-global-clock',
|
||||
'http://dev.w3.org/fxtf/web-animations/#the-document-timeline'
|
||||
],
|
||||
assert: [
|
||||
'The global clock is a source of monotonically increasing time values',
|
||||
'The time values of the document timeline are calculated as a fixed' +
|
||||
' offset from the global clock',
|
||||
'the zero time corresponds to the navigationStart moment',
|
||||
'the time value of each document timeline must be equal to the time ' +
|
||||
'passed to animation frame request callbacks for that browsing context'
|
||||
],
|
||||
author: 'Brian Birtles'
|
||||
});
|
||||
|
||||
async_test(function(t) {
|
||||
var valueAtStart = document.timeline.currentTime;
|
||||
var timeAtStart = window.performance.now();
|
||||
while (window.performance.now() - timeAtStart < 100) {
|
||||
// Wait 100ms
|
||||
}
|
||||
assert_equals(document.timeline.currentTime, valueAtStart,
|
||||
'document.timeline.currentTime does not change within a script block');
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime > valueAtStart,
|
||||
'document.timeline.currentTime increases between script blocks');
|
||||
t.done();
|
||||
}));
|
||||
},
|
||||
'document.timeline.currentTime liveness tests',
|
||||
{
|
||||
help: 'http://dev.w3.org/fxtf/web-animations/#script-execution-and-live-updates-to-the-model',
|
||||
assert: [ 'The value returned by the currentTime attribute of a' +
|
||||
' document timeline will not change within a script block' ],
|
||||
author: 'Brian Birtles'
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var hiddenIFrame = document.getElementById('hidden-iframe');
|
||||
assert_equals(typeof hiddenIFrame.contentDocument.timeline.currentTime,
|
||||
'number',
|
||||
'currentTime of an initially hidden subframe\'s timeline is a number');
|
||||
assert_true(hiddenIFrame.contentDocument.timeline.currentTime >= 0,
|
||||
'currentTime of an initially hidden subframe\'s timeline is >= 0');
|
||||
}, 'document.timeline.currentTime hidden subframe test');
|
||||
|
||||
async_test(function(t) {
|
||||
var hiddenIFrame = document.getElementById('hidden-iframe');
|
||||
|
||||
// Don't run the test until after the iframe has completed loading or else the
|
||||
// contentDocument may change.
|
||||
var testToRunOnLoad = t.step_func(function() {
|
||||
// Remove display:none
|
||||
hiddenIFrame.style.display = 'block';
|
||||
window.getComputedStyle(hiddenIFrame).display;
|
||||
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
assert_true(hiddenIFrame.contentDocument.timeline.currentTime > 0,
|
||||
'document.timeline.currentTime is positive after removing'
|
||||
+ ' display:none');
|
||||
var previousValue = hiddenIFrame.contentDocument.timeline.currentTime;
|
||||
|
||||
// Re-introduce display:none
|
||||
hiddenIFrame.style.display = 'none';
|
||||
window.getComputedStyle(hiddenIFrame).display;
|
||||
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
assert_true(
|
||||
hiddenIFrame.contentDocument.timeline.currentTime >= previousValue,
|
||||
'document.timeline.currentTime does not go backwards after'
|
||||
+ ' re-setting display:none');
|
||||
t.done();
|
||||
}));
|
||||
}));
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_document-timeline.html");
|
||||
});
|
||||
|
||||
if (hiddenIFrame.contentDocument.readyState === 'complete') {
|
||||
testToRunOnLoad();
|
||||
} else {
|
||||
hiddenIFrame.addEventListener("load", testToRunOnLoad);
|
||||
}
|
||||
}, 'document.timeline.currentTime hidden subframe dynamic test');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -3,29 +3,52 @@ support-files =
|
|||
testcommon.js
|
||||
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
support-files = css-animations/file_animations-dynamic-changes.html
|
||||
[css-animations/test_animation-cancel.html]
|
||||
support-files = css-animations/file_animation-cancel.html
|
||||
[css-animations/test_animation-currenttime.html]
|
||||
support-files = css-animations/file_animation-currenttime.html
|
||||
[css-animations/test_animation-finish.html]
|
||||
support-files = css-animations/file_animation-finish.html
|
||||
[css-animations/test_animation-finished.html]
|
||||
support-files = css-animations/file_animation-finished.html
|
||||
[css-animations/test_animation-pausing.html]
|
||||
support-files = css-animations/file_animation-pausing.html
|
||||
[css-animations/test_animation-playstate.html]
|
||||
support-files = css-animations/file_animation-playstate.html
|
||||
[css-animations/test_animation-ready.html]
|
||||
support-files = css-animations/file_animation-ready.html
|
||||
[css-animations/test_animation-starttime.html]
|
||||
support-files = css-animations/file_animation-starttime.html
|
||||
[css-animations/test_effect-name.html]
|
||||
support-files = css-animations/file_effect-name.html
|
||||
[css-animations/test_effect-target.html]
|
||||
support-files = css-animations/file_effect-target.html
|
||||
[css-animations/test_element-get-animations.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
support-files = css-animations/file_element-get-animations.html
|
||||
[css-transitions/test_animation-cancel.html]
|
||||
support-files = css-transitions/file_animation-cancel.html
|
||||
[css-transitions/test_animation-currenttime.html]
|
||||
support-files = css-transitions/file_animation-currenttime.html
|
||||
[css-transitions/test_animation-finished.html]
|
||||
support-files = css-transitions/file_animation-finished.html
|
||||
[css-transitions/test_animation-pausing.html]
|
||||
support-files = css-transitions/file_animation-pausing.html
|
||||
[css-transitions/test_animation-ready.html]
|
||||
support-files = css-transitions/file_animation-ready.html
|
||||
[css-transitions/test_animation-starttime.html]
|
||||
support-files = css-transitions/file_animation-starttime.html
|
||||
[css-transitions/test_effect-name.html]
|
||||
support-files = css-transitions/file_effect-name.html
|
||||
[css-transitions/test_effect-target.html]
|
||||
support-files = css-transitions/file_effect-target.html
|
||||
[css-transitions/test_element-get-animations.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
support-files = css-transitions/file_element-get-animations.html
|
||||
[document-timeline/test_document-timeline.html]
|
||||
support-files = document-timeline/file_document-timeline.html
|
||||
[document-timeline/test_request_animation_frame.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[mozilla/test_deferred_start.html]
|
||||
support-files = mozilla/file_deferred_start.html
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes empty { }
|
||||
@keyframes animTransform {
|
||||
from { transform: translate(0px); }
|
||||
to { transform: translate(100px); }
|
||||
}
|
||||
.target {
|
||||
/* Element needs geometry to be eligible for layerization */
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function waitForDocLoad() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (document.readyState === 'complete') {
|
||||
resolve();
|
||||
} else {
|
||||
window.addEventListener('load', resolve);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
// Test that empty animations actually start.
|
||||
//
|
||||
// Normally we tie the start of animations to when their first frame of
|
||||
// the animation is rendered. However, for animations that don't actually
|
||||
// trigger a paint (e.g. because they are empty, or are animating something
|
||||
// that doesn't render or is offscreen) we want to make sure they still
|
||||
// start.
|
||||
//
|
||||
// Before we start, wait for the document to finish loading. This is because
|
||||
// during loading we will have other paint events taking place which might,
|
||||
// by luck, happen to trigger animations that otherwise would not have been
|
||||
// triggered, leading to false positives.
|
||||
//
|
||||
// As a result, it's better to wait until we have a more stable state before
|
||||
// continuing.
|
||||
var promiseCallbackDone = false;
|
||||
waitForDocLoad().then(function() {
|
||||
div.style.animation = 'empty 1000s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
promiseCallbackDone = true;
|
||||
}).catch(function() {
|
||||
assert_unreached('ready promise was rejected');
|
||||
});
|
||||
|
||||
// We need to wait for up to three frames. This is because in some
|
||||
// cases it can take up to two frames for the initial layout
|
||||
// to take place. Even after that happens we don't actually resolve the
|
||||
// ready promise until the following tick.
|
||||
})
|
||||
.then(waitForFrame)
|
||||
.then(waitForFrame)
|
||||
.then(waitForFrame)
|
||||
.then(t.step_func(function() {
|
||||
assert_true(promiseCallbackDone,
|
||||
'ready promise for an empty animation was resolved'
|
||||
+ ' within three animation frames');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.ready is resolved for an empty animation');
|
||||
|
||||
// Test that compositor animations with delays get synced correctly
|
||||
//
|
||||
// NOTE: It is important that we DON'T use
|
||||
// SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
|
||||
// us through a different code path.
|
||||
async_test(function(t) {
|
||||
// This test only applies to compositor animations
|
||||
const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations';
|
||||
var omtaEnabled = SpecialPowers.DOMWindowUtils.layerManagerRemote &&
|
||||
SpecialPowers.getBoolPref(OMTAPrefKey);
|
||||
if (!omtaEnabled) {
|
||||
t.done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup animation
|
||||
var div = addDiv(t);
|
||||
div.classList.add('target');
|
||||
div.style.animation = 'animTransform 100s -50s forwards';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var transformStr =
|
||||
SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
|
||||
|
||||
var matrixComponents =
|
||||
transformStr.startsWith('matrix(')
|
||||
? transformStr.substring('matrix('.length, transformStr.length-1)
|
||||
.split(',')
|
||||
.map(component => Number(component))
|
||||
: [];
|
||||
assert_equals(matrixComponents.length, 6,
|
||||
'Got a valid transform matrix on the compositor'
|
||||
+ ' (got: "' + transformStr + '")');
|
||||
|
||||
// If the delay has been applied correctly we should be at least
|
||||
// half-way through the animation
|
||||
assert_true(matrixComponents[4] >= 50,
|
||||
'Animation is at least half-way through on the compositor'
|
||||
+ ' (got translation of ' + matrixComponents[4] + ')');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Starting an animation with a delay starts from the correct point');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
|
@ -2,122 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes empty { }
|
||||
@keyframes animTransform {
|
||||
from { transform: translate(0px); }
|
||||
to { transform: translate(100px); }
|
||||
}
|
||||
.target {
|
||||
/* Element needs geometry to be eligible for layerization */
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function waitForDocLoad() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (document.readyState === 'complete') {
|
||||
resolve();
|
||||
} else {
|
||||
window.addEventListener('load', resolve);
|
||||
}
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_deferred_start.html");
|
||||
});
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
// Test that empty animations actually start.
|
||||
//
|
||||
// Normally we tie the start of animations to when their first frame of
|
||||
// the animation is rendered. However, for animations that don't actually
|
||||
// trigger a paint (e.g. because they are empty, or are animating something
|
||||
// that doesn't render or is offscreen) we want to make sure they still
|
||||
// start.
|
||||
//
|
||||
// Before we start, wait for the document to finish loading. This is because
|
||||
// during loading we will have other paint events taking place which might,
|
||||
// by luck, happen to trigger animations that otherwise would not have been
|
||||
// triggered, leading to false positives.
|
||||
//
|
||||
// As a result, it's better to wait until we have a more stable state before
|
||||
// continuing.
|
||||
var promiseCallbackDone = false;
|
||||
waitForDocLoad().then(function() {
|
||||
div.style.animation = 'empty 1000s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
promiseCallbackDone = true;
|
||||
}).catch(function() {
|
||||
assert_unreached('ready promise was rejected');
|
||||
});
|
||||
|
||||
// We need to wait for up to three frames. This is because in some
|
||||
// cases it can take up to two frames for the initial layout
|
||||
// to take place. Even after that happens we don't actually resolve the
|
||||
// ready promise until the following tick.
|
||||
})
|
||||
.then(waitForFrame)
|
||||
.then(waitForFrame)
|
||||
.then(waitForFrame)
|
||||
.then(t.step_func(function() {
|
||||
assert_true(promiseCallbackDone,
|
||||
'ready promise for an empty animation was resolved'
|
||||
+ ' within three animation frames');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animation.ready is resolved for an empty animation');
|
||||
|
||||
// Test that compositor animations with delays get synced correctly
|
||||
//
|
||||
// NOTE: It is important that we DON'T use
|
||||
// SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh here since that takes
|
||||
// us through a different code path.
|
||||
async_test(function(t) {
|
||||
// This test only applies to compositor animations
|
||||
const OMTAPrefKey = 'layers.offmainthreadcomposition.async-animations';
|
||||
var omtaEnabled = SpecialPowers.DOMWindowUtils.layerManagerRemote &&
|
||||
SpecialPowers.getBoolPref(OMTAPrefKey);
|
||||
if (!omtaEnabled) {
|
||||
t.done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup animation
|
||||
var div = addDiv(t);
|
||||
div.classList.add('target');
|
||||
div.style.animation = 'animTransform 100s -50s forwards';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var transformStr =
|
||||
SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
|
||||
|
||||
var matrixComponents =
|
||||
transformStr.startsWith('matrix(')
|
||||
? transformStr.substring('matrix('.length, transformStr.length-1)
|
||||
.split(',')
|
||||
.map(component => Number(component))
|
||||
: [];
|
||||
assert_equals(matrixComponents.length, 6,
|
||||
'Got a valid transform matrix on the compositor'
|
||||
+ ' (got: "' + transformStr + '")');
|
||||
|
||||
// If the delay has been applied correctly we should be at least
|
||||
// half-way through the animation
|
||||
assert_true(matrixComponents[4] >= 50,
|
||||
'Animation is at least half-way through on the compositor'
|
||||
+ ' (got translation of ' + matrixComponents[4] + ')');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Starting an animation with a delay starts from the correct point');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -77,3 +77,18 @@ function flushComputedStyle(elem) {
|
|||
cs.marginLeft;
|
||||
}
|
||||
|
||||
for (var funcName of ["async_test", "assert_not_equals", "assert_equals",
|
||||
"assert_approx_equals", "assert_less_than_equal",
|
||||
"assert_between_inclusive", "assert_true", "assert_false",
|
||||
"test"]) {
|
||||
window[funcName] = opener[funcName].bind(opener);
|
||||
}
|
||||
|
||||
window.EventWatcher = opener.EventWatcher;
|
||||
|
||||
function done() {
|
||||
opener.add_completion_callback(function() {
|
||||
self.close();
|
||||
});
|
||||
opener.done();
|
||||
}
|
||||
|
|
|
@ -130,13 +130,13 @@ var interfaceNamesInGlobalScope =
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"AnalyserNode",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "Animation", pref: "dom.animations-api.core.enabled"},
|
||||
{name: "Animation", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "AnimationEffectReadOnly", pref: "dom.animations-api.core.enabled"},
|
||||
{name: "AnimationEffectReadOnly", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"AnimationEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "AnimationTimeline", pref: "dom.animations-api.core.enabled"},
|
||||
{name: "AnimationTimeline", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Attr",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -351,7 +351,7 @@ var interfaceNamesInGlobalScope =
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DocumentFragment",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "DocumentTimeline", pref: "dom.animations-api.core.enabled"},
|
||||
{name: "DocumentTimeline", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DocumentType",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -651,7 +651,7 @@ var interfaceNamesInGlobalScope =
|
|||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"KeyboardEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "KeyframeEffectReadOnly", pref: "dom.animations-api.core.enabled"},
|
||||
{name: "KeyframeEffectReadOnly", release: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"LocalMediaStream",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -26,15 +26,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1135764
|
|||
SimpleTest.finish();
|
||||
}
|
||||
addLoadEvent(function() {
|
||||
startTimelineValue = frames[0].document.timeline.currentTime;
|
||||
frames[0].requestAnimationFrame(waitATick);
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [[ "dom.animations-api.core.enabled", true]] },
|
||||
function() {
|
||||
var ifr = document.querySelector("iframe");
|
||||
ifr.onload = function() {
|
||||
startTimelineValue = frames[0].document.timeline.currentTime;
|
||||
frames[0].requestAnimationFrame(waitATick);
|
||||
}
|
||||
ifr.src = "file_bug1135764.xml";
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1135764">Mozilla Bug 1135764</a>
|
||||
<p id="display">
|
||||
<iframe src="file_bug1135764.xml"></iframe>
|
||||
<iframe></iframe>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript"
|
||||
src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="animation_utils.js"></script>
|
||||
<style type="text/css">
|
||||
@keyframes anim {
|
||||
0% { transform: translate(0px) }
|
||||
100% { transform: translate(100px) }
|
||||
}
|
||||
.target {
|
||||
/* The animation target needs geometry in order to qualify for OMTA */
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var ok = opener.ok.bind(opener);
|
||||
var is = opener.is.bind(opener);
|
||||
var okOrTodo = opener.okOrTodo.bind(opener);
|
||||
function finish() {
|
||||
var o = opener;
|
||||
self.close();
|
||||
o.SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="display"></div>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
runOMTATest(function() {
|
||||
runAllAsyncAnimTests().then(function() {
|
||||
finish();
|
||||
});
|
||||
}, finish);
|
||||
|
||||
addAsyncAnimTest(function *() {
|
||||
var [ div, cs ] = new_div("animation: anim 10s 2 linear alternate");
|
||||
|
||||
// Animation is initially running on compositor
|
||||
yield waitForPaintsFlushed();
|
||||
advance_clock(1000);
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.Compositor,
|
||||
"Animation is initally animating on compositor");
|
||||
|
||||
// pause() means it is no longer on the compositor
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.pause();
|
||||
// pause() should set up the changes to animations for the next layer
|
||||
// transaction but it won't schedule a paint immediately so we need to tick
|
||||
// the refresh driver before we can wait on the next paint.
|
||||
advance_clock(0);
|
||||
yield waitForPaints();
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.MainThread,
|
||||
"After pausing, animation is removed from compositor");
|
||||
|
||||
// Animation remains paused
|
||||
advance_clock(1000);
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.MainThread,
|
||||
"Animation remains paused");
|
||||
|
||||
// play() puts the animation back on the compositor
|
||||
animation.play();
|
||||
// As with pause(), play() will set up pending animations for the next layer
|
||||
// transaction but won't schedule a paint so we need to tick the refresh
|
||||
// driver before waiting on the next paint.
|
||||
advance_clock(0);
|
||||
yield waitForPaints();
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.Compositor,
|
||||
"After playing, animation is sent to compositor");
|
||||
|
||||
// Where it continues to run
|
||||
advance_clock(1000);
|
||||
omta_is(div, "transform", { tx: 20 }, RunningOn.Compositor,
|
||||
"Animation continues playing on compositor");
|
||||
|
||||
done_div();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -267,7 +267,7 @@ support-files = bug732209-css.sjs
|
|||
[test_counter_descriptor_storage.html]
|
||||
[test_position_float_display.html]
|
||||
[test_animations_async_tests.html]
|
||||
support-files = ../../reftests/fonts/Ahem.ttf
|
||||
support-files = ../../reftests/fonts/Ahem.ttf file_animations_pausing.html
|
||||
[test_setPropertyWithNull.html]
|
||||
[test_attribute_selector_eof_behavior.html]
|
||||
[test_css_loader_crossorigin_data_url.html]
|
||||
|
|
|
@ -6,22 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1070745
|
|||
<head>
|
||||
<title>Test for play() and pause() on animations (Bug 1070745)</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="animation_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style type="text/css">
|
||||
@keyframes anim {
|
||||
0% { transform: translate(0px) }
|
||||
100% { transform: translate(100px) }
|
||||
}
|
||||
.target {
|
||||
/* The animation target needs geometry in order to qualify for OMTA */
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1070745">Mozilla Bug 1070745</a>
|
||||
|
@ -32,55 +17,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1070745
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
runOMTATest(function() {
|
||||
runAllAsyncAnimTests().then(function() {
|
||||
SimpleTest.finish();
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [[ "dom.animations-api.core.enabled", true]] },
|
||||
function() {
|
||||
window.open("file_animations_pausing.html");
|
||||
});
|
||||
}, SimpleTest.finish);
|
||||
|
||||
addAsyncAnimTest(function *() {
|
||||
var [ div, cs ] = new_div("animation: anim 10s 2 linear alternate");
|
||||
|
||||
// Animation is initially running on compositor
|
||||
yield waitForPaintsFlushed();
|
||||
advance_clock(1000);
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.Compositor,
|
||||
"Animation is initally animating on compositor");
|
||||
|
||||
// pause() means it is no longer on the compositor
|
||||
var animation = div.getAnimations()[0];
|
||||
animation.pause();
|
||||
// pause() should set up the changes to animations for the next layer
|
||||
// transaction but it won't schedule a paint immediately so we need to tick
|
||||
// the refresh driver before we can wait on the next paint.
|
||||
advance_clock(0);
|
||||
yield waitForPaints();
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.MainThread,
|
||||
"After pausing, animation is removed from compositor");
|
||||
|
||||
// Animation remains paused
|
||||
advance_clock(1000);
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.MainThread,
|
||||
"Animation remains paused");
|
||||
|
||||
// play() puts the animation back on the compositor
|
||||
animation.play();
|
||||
// As with pause(), play() will set up pending animations for the next layer
|
||||
// transaction but won't schedule a paint so we need to tick the refresh
|
||||
// driver before waiting on the next paint.
|
||||
advance_clock(0);
|
||||
yield waitForPaints();
|
||||
omta_is(div, "transform", { tx: 10 }, RunningOn.Compositor,
|
||||
"After playing, animation is sent to compositor");
|
||||
|
||||
// Where it continues to run
|
||||
advance_clock(1000);
|
||||
omta_is(div, "transform", { tx: 20 }, RunningOn.Compositor,
|
||||
"Animation continues playing on compositor");
|
||||
|
||||
done_div();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -51,7 +51,6 @@ user_pref("dom.w3c_touch_events.enabled", 1);
|
|||
user_pref("dom.undo_manager.enabled", true);
|
||||
user_pref("dom.webcomponents.enabled", true);
|
||||
user_pref("dom.htmlimports.enabled", true);
|
||||
user_pref("dom.animations-api.core.enabled", true);
|
||||
// Set a future policy version to avoid the telemetry prompt.
|
||||
user_pref("toolkit.telemetry.prompted", 999);
|
||||
user_pref("toolkit.telemetry.notifiedOptOut", 999);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[animation-timeline.html]
|
||||
type: testharness
|
||||
prefs: [dom.animations-api.core.enabled:true]
|
|
@ -1,5 +1,6 @@
|
|||
[idlharness.html]
|
||||
type: testharness
|
||||
prefs: [dom.animations-api.core.enabled:true]
|
||||
[AnimationTimeline must be primary interface of document.timeline]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче