mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1070744, part 2 - Tests for the effect of setting CSS animation's AnimationPlayer.currentTime. r=birtles
This commit is contained in:
parent
96700a424d
commit
85c49511db
@ -0,0 +1,616 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for the effect of setting a CSS animation's
|
||||
AnimationPlayer.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">
|
||||
|
||||
'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;
|
||||
|
||||
/**
|
||||
* CSS animation events fire asynchronously after we set 'startTime'. This
|
||||
* helper class allows us to handle such events using Promises.
|
||||
*
|
||||
* To use this class:
|
||||
*
|
||||
* var eventWatcher = new EventWatcher(watchedNode, eventTypes);
|
||||
* eventWatcher.waitForEvent(eventType).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkStuff();
|
||||
* makeSomeChanges();
|
||||
* return eventWatcher.waitForEvent(nextEventType);
|
||||
* }).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkMoreStuff();
|
||||
* eventWatcher.stopWatching(); // all done - stop listening for events
|
||||
* });
|
||||
*
|
||||
* This class will assert_unreached() if an event occurs when there is no
|
||||
* Promise created by a waitForEvent() call waiting to be fulfilled, or if the
|
||||
* event is of a different type to the type passed to waitForEvent. This helps
|
||||
* provide test coverage to ensure that only events that are expected occur, in
|
||||
* the correct order and with the correct timing. It also helps vastly simplify
|
||||
* the already complex code below by avoiding lots of gnarly error handling
|
||||
* code.
|
||||
*/
|
||||
function EventWatcher(watchedNode, eventTypes)
|
||||
{
|
||||
if (typeof eventTypes == 'string') {
|
||||
eventTypes = [eventTypes];
|
||||
}
|
||||
|
||||
var waitingFor = null;
|
||||
|
||||
function eventHandler(evt) {
|
||||
if (!waitingFor) {
|
||||
assert_unreached('Not expecting event, but got: ' + evt.type +
|
||||
' targeting element #' + evt.target.getAttribute('id'));
|
||||
return;
|
||||
}
|
||||
if (evt.type != waitingFor.types[0]) {
|
||||
assert_unreached('Expected ' + waitingFor.types[0] + ' event but got ' +
|
||||
evt.type + ' event');
|
||||
return;
|
||||
}
|
||||
if (waitingFor.types.length > 1) {
|
||||
// Pop first event from array
|
||||
waitingFor.types.shift();
|
||||
return;
|
||||
}
|
||||
// We need to null out waitingFor before calling the resolve function since
|
||||
// the Promise's resolve handlers may call waitForEvent() which will need
|
||||
// to set waitingFor.
|
||||
var resolveFunc = waitingFor.resolve;
|
||||
waitingFor = null;
|
||||
resolveFunc(evt);
|
||||
}
|
||||
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.addEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
|
||||
this.waitForEvent = function(type) {
|
||||
if (typeof type != 'string') {
|
||||
return Promise.reject('Event type not a string');
|
||||
}
|
||||
return this.waitForEvents([type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is useful when two events are expected to fire one immediately after
|
||||
* the other. This happens when we skip over the entire active interval for
|
||||
* instance. In this case an 'animationstart' and an 'animationend' are fired
|
||||
* and due to the asynchronous nature of Promise callbacks this won't work:
|
||||
*
|
||||
* eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
* return waitForEvent('animationend');
|
||||
* }).then(...);
|
||||
*
|
||||
* It doesn't work because the 'animationend' listener is added too late,
|
||||
* because the resolve handler for the first Promise is called asynchronously
|
||||
* some time after the 'animationstart' event is called, rather than at the
|
||||
* time the event reaches the watched element.
|
||||
*/
|
||||
this.waitForEvents = function(types) {
|
||||
if (waitingFor) {
|
||||
return Promise.reject('Already waiting for an event');
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitingFor = {
|
||||
types: types,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.stopWatching = function() {
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.removeEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// 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-node-phases-and-states
|
||||
//
|
||||
// Note the distinction between "player start time" and "animation start time".
|
||||
// The former is the start of the start delay. The latter is the start of the
|
||||
// active interval. (If there is no delay, they are the same.)
|
||||
|
||||
// Called when currentTime is set to zero (the beginning of the start delay).
|
||||
function checkStateOnSettingCurrentTimeToZero(player)
|
||||
{
|
||||
// We don't test player.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(player.playState, 'running',
|
||||
'AnimationPlayer.playState should be "running" at the start of ' +
|
||||
'the start delay');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, 'running',
|
||||
'AnimationPlayer.source.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the start delay');
|
||||
|
||||
var div = player.source.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(player)
|
||||
{
|
||||
// the 0.0001 here is for rounding error
|
||||
assert_less_than_equal(player.currentTime,
|
||||
player.timeline.currentTime - player.startTime + 0.0001,
|
||||
'AnimationPlayer.currentTime should be less than the local time ' +
|
||||
'equivalent of the timeline\'s currentTime on the first paint tick ' +
|
||||
'after animation creation');
|
||||
|
||||
assert_equals(player.playState, 'running',
|
||||
'AnimationPlayer.playState should be "running" on the first paint ' +
|
||||
'tick after animation creation');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, 'running',
|
||||
'AnimationPlayer.source.target.style.animationPlayState should be ' +
|
||||
'"running" on the first paint tick after animation creation');
|
||||
|
||||
var div = player.source.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(player)
|
||||
{
|
||||
// We don't test player.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(player.playState, 'running',
|
||||
'AnimationPlayer.playState should be "running" at the start of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, 'running',
|
||||
'AnimationPlayer.source.target.style.animationPlayState should be ' +
|
||||
'"running" at the start of the active interval');
|
||||
|
||||
var div = player.source.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(player)
|
||||
{
|
||||
// We don't test player.currentTime since our caller just set it.
|
||||
|
||||
var div = player.source.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(player)
|
||||
{
|
||||
// We don't test player.currentTime since our caller just set it.
|
||||
|
||||
assert_equals(player.playState, 'finished',
|
||||
'AnimationPlayer.playState should be "finished" at the end of ' +
|
||||
'the active interval');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, "running",
|
||||
'AnimationPlayer.source.target.style.animationPlayState should be ' +
|
||||
'"finished" at the end of the active interval');
|
||||
|
||||
var div = player.source.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 player = div.getAnimationPlayers()[0];
|
||||
|
||||
// Animations shouldn't start until the next paint tick, so:
|
||||
assert_equals(player.currentTime, 0,
|
||||
'AnimationPlayer.currentTime should be zero when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(player.playState, "pending",
|
||||
'AnimationPlayer.playState should be "pending" when an animation ' +
|
||||
'is initially created');
|
||||
|
||||
assert_equals(player.source.target.style.animationPlayState, 'running',
|
||||
'AnimationPlayer.source.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.
|
||||
|
||||
// Just so that player is running instead of paused when we set currentTime:
|
||||
player.startTime = player.timeline.currentTime;
|
||||
|
||||
assert_approx_equals(player.currentTime, 0, 0.0001, // rounding error
|
||||
'Check setting of currentTime actually works');
|
||||
|
||||
checkStateOnSettingCurrentTimeToZero(player);
|
||||
}, '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(div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(player);
|
||||
|
||||
player.currentTime = currentTimeForStartOfActiveInterval(player.timeline);
|
||||
return eventWatcher.waitForEvent('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(player);
|
||||
|
||||
player.currentTime =
|
||||
currentTimeForFiftyPercentThroughActiveInterval(player.timeline);
|
||||
checkStateAtFiftyPctOfActiveInterval(player);
|
||||
|
||||
player.currentTime = currentTimeForEndOfActiveInterval(player.timeline);
|
||||
return eventWatcher.waitForEvent('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(player);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).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(div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
// Just so that player is running instead of paused when we set currentTime:
|
||||
player.startTime = player.timeline.currentTime;
|
||||
|
||||
player.currentTime = currentTimeForEndOfActiveInterval(player.timeline);
|
||||
|
||||
var previousTimelineTime = player.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.waitForEvents(['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(player);
|
||||
|
||||
player.currentTime =
|
||||
currentTimeForFiftyPercentThroughActiveInterval(player.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 waitForEvent first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.waitForEvent('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(player);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
player.currentTime = currentTimeForStartOfActiveInterval(player.timeline);
|
||||
checkStateAtActiveIntervalStartTime(player);
|
||||
|
||||
player.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.waitForEvent('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(player);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).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 player.playState
|
||||
// check or the marginLeft check).
|
||||
//checkStateAtActiveIntervalEndTime(player);
|
||||
}, '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(div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.currentTime = currentTimeForActivePhase(player.timeline);
|
||||
player.currentTime = currentTimeForBeforePhase(player.timeline);
|
||||
|
||||
waitForTwoAnimationFrames().then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.currentTime = currentTimeForAfterPhase(player.timeline);
|
||||
player.currentTime = currentTimeForBeforePhase(player.timeline);
|
||||
|
||||
waitForTwoAnimationFrames().then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
player.currentTime = currentTimeForBeforePhase(player.timeline);
|
||||
player.currentTime = currentTimeForActivePhase(player.timeline);
|
||||
|
||||
waitForTwoAnimationFrames().then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
player.currentTime = currentTimeForActivePhase(player.timeline);
|
||||
}, 'Redundant change, active -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
player.currentTime = currentTimeForAfterPhase(player.timeline);
|
||||
player.currentTime = currentTimeForActivePhase(player.timeline);
|
||||
|
||||
waitForTwoAnimationFrames().then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
player.currentTime = currentTimeForActivePhase(player.timeline);
|
||||
}, 'Redundant change, active -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
player.currentTime = currentTimeForBeforePhase(player.timeline);
|
||||
player.currentTime = currentTimeForAfterPhase(player.timeline);
|
||||
|
||||
waitForTwoAnimationFrames().then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
player.currentTime = currentTimeForAfterPhase(player.timeline);
|
||||
}, 'Redundant change, after -> before, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
player.currentTime = currentTimeForActivePhase(player.timeline);
|
||||
player.currentTime = currentTimeForAfterPhase(player.timeline);
|
||||
|
||||
waitForTwoAnimationFrames().then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
// get us into the initial state:
|
||||
player.currentTime = currentTimeForAfterPhase(player.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 player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
var exception;
|
||||
try {
|
||||
player.currentTime = null;
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
assert_equals(exception.name, 'TypeError',
|
||||
'Expect TypeError exception on trying to set ' +
|
||||
'AnimationPlayer.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 player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
var savedCurrentTime = player.currentTime;
|
||||
|
||||
assert_not_equals(player.currentTime, null,
|
||||
'AnimationPlayer.currentTime not null on ready Promise resolve');
|
||||
|
||||
var prePauseCurrentTime = player.currentTime;
|
||||
|
||||
player.pause();
|
||||
// After bug 1109390 we will need to wait here for the ready promise again
|
||||
|
||||
assert_equals(player.currentTime, prePauseCurrentTime,
|
||||
'AnimationPlayer.currentTime is unchanged after AnimationPlayer.pause()');
|
||||
assert_equals(player.playState, 'paused',
|
||||
'AnimationPlayer.playState is "paused" after pause() call');
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
t.done();
|
||||
});
|
||||
}, 'AnimationPlayer.currentTime after paused');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -7,6 +7,7 @@ skip-if = buildapp == 'mulet'
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
[css-animations/test_animation-effect-name.html]
|
||||
[css-animations/test_animation-pausing.html]
|
||||
[css-animations/test_animation-player-currenttime.html]
|
||||
[css-animations/test_animation-player-playstate.html]
|
||||
[css-animations/test_animation-player-ready.html]
|
||||
[css-animations/test_animation-player-starttime.html]
|
||||
|
Loading…
Reference in New Issue
Block a user