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:
Boris Zbarsky 2015-04-30 14:25:03 -04:00
Родитель ecba4cbc0c
Коммит 5844aafa2d
56 изменённых файлов: 4460 добавлений и 4061 удалений

Просмотреть файл

@ -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