mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-10-22 02:25:05 +00:00
1462 lines
53 KiB
HTML
1462 lines
53 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset=utf-8>
|
|
<title>Test chrome-only MutationObserver animation notifications</title>
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
|
<style>
|
|
@keyframes anim {
|
|
to { transform: translate(100px); }
|
|
}
|
|
#target {
|
|
width: 100px;
|
|
height: 100px;
|
|
background-color: yellow;
|
|
line-height: 16px;
|
|
}
|
|
</style>
|
|
<div id=container><div id=target></div></div>
|
|
<script>
|
|
var div = document.getElementById("target");
|
|
var gRecords = [];
|
|
var gRecordPromiseResolvers = [];
|
|
var gObserver = new MutationObserver(function(newRecords) {
|
|
gRecords.push(...newRecords);
|
|
|
|
var resolvers = gRecordPromiseResolvers;
|
|
gRecordPromiseResolvers = [];
|
|
resolvers.forEach(fn => fn());
|
|
});
|
|
|
|
// Asynchronous testing framework based on layout/style/test/animation_utils.js.
|
|
|
|
var gTests = [];
|
|
var gCurrentTestName;
|
|
|
|
function addAsyncAnimTest(aName, aOptions, aTestGenerator) {
|
|
aTestGenerator.testName = aName;
|
|
aTestGenerator.options = aOptions || {};
|
|
gTests.push(aTestGenerator);
|
|
}
|
|
|
|
function runAsyncTest(aTestGenerator) {
|
|
return await_frame().then(function() {
|
|
var generator;
|
|
|
|
function step(arg) {
|
|
var next;
|
|
try {
|
|
next = generator.next(arg);
|
|
} catch (e) {
|
|
return Promise.reject(e);
|
|
}
|
|
if (next.done) {
|
|
return Promise.resolve(next.value);
|
|
} else {
|
|
return Promise.resolve(next.value).then(step);
|
|
}
|
|
}
|
|
|
|
var subtree = aTestGenerator.options.subtree;
|
|
|
|
gCurrentTestName = aTestGenerator.testName;
|
|
if (subtree) {
|
|
gCurrentTestName += ":subtree";
|
|
}
|
|
|
|
gRecords = [];
|
|
gObserver.disconnect();
|
|
gObserver.observe(aTestGenerator.options.observe,
|
|
{ animations: true, subtree: subtree});
|
|
|
|
generator = aTestGenerator();
|
|
return step();
|
|
});
|
|
};
|
|
|
|
function runAllAsyncTests() {
|
|
return gTests.reduce(function(sequence, test) {
|
|
return sequence.then(() => runAsyncTest(test));
|
|
}, Promise.resolve());
|
|
}
|
|
|
|
// Wrap is and ok with versions that prepend the current sub-test name
|
|
// to the assertion description.
|
|
var old_is = is, old_ok = ok;
|
|
is = function(a, b, message) {
|
|
if (gCurrentTestName && message) {
|
|
message = `[${gCurrentTestName}] ${message}`;
|
|
}
|
|
old_is(a, b, message);
|
|
}
|
|
ok = function(a, message) {
|
|
if (gCurrentTestName && message) {
|
|
message = `[${gCurrentTestName}] ${message}`;
|
|
}
|
|
old_ok(a, message);
|
|
}
|
|
|
|
// Returns a Promise that is resolved by a requestAnimationFrame callback.
|
|
function await_frame() {
|
|
return new Promise(function(aResolve) {
|
|
requestAnimationFrame(function() {
|
|
aResolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
// Adds an event listener and returns a Promise that is resolved when the
|
|
// event listener is called.
|
|
function await_event(aElement, aEventName) {
|
|
return new Promise(function(aResolve) {
|
|
function listener(aEvent) {
|
|
aElement.removeEventListener(aEventName, listener);
|
|
aResolve();
|
|
}
|
|
aElement.addEventListener(aEventName, listener, false);
|
|
});
|
|
}
|
|
|
|
// Returns a Promise that is resolved after a given timeout duration.
|
|
function await_timeout(aTimeout) {
|
|
return new Promise(function(aResolve) {
|
|
setTimeout(function() {
|
|
aResolve();
|
|
}, aTimeout);
|
|
});
|
|
}
|
|
|
|
// Returns a Promise that is resolved when the MutationObserver is next
|
|
// invoked.
|
|
function await_records() {
|
|
return new Promise(function(aResolve) {
|
|
gRecordPromiseResolvers.push(aResolve);
|
|
});
|
|
}
|
|
|
|
function assert_record_list(actual, expected, desc, index, listName) {
|
|
is(actual.length, expected.length, `${desc} - record[${index}].${listName} length`);
|
|
if (actual.length != expected.length) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < actual.length; i++) {
|
|
ok(actual.indexOf(expected[i]) != -1,
|
|
`${desc} - record[${index}].${listName} contains expected Animation`);
|
|
}
|
|
}
|
|
|
|
function assert_records(expected, desc) {
|
|
var records = gRecords;
|
|
gRecords = [];
|
|
is(records.length, expected.length, `${desc} - number of records`);
|
|
if (records.length != expected.length) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < records.length; i++) {
|
|
assert_record_list(records[i].addedAnimations, expected[i].added, desc, i, "addedAnimations");
|
|
assert_record_list(records[i].changedAnimations, expected[i].changed, desc, i, "changedAnimations");
|
|
assert_record_list(records[i].removedAnimations, expected[i].removed, desc, i, "removedAnimations");
|
|
}
|
|
}
|
|
|
|
// -- Tests ------------------------------------------------------------------
|
|
|
|
// We run all tests first targetting the div and observing the div, then again
|
|
// targetting the div and observing its parent while using the subtree:true
|
|
// MutationObserver option.
|
|
|
|
[
|
|
{ observe: div, target: div, subtree: false },
|
|
{ observe: div.parentNode, target: div, subtree: true },
|
|
].forEach(function(aOptions) {
|
|
|
|
var e = aOptions.target;
|
|
|
|
// Test that starting a single transition that completes normally
|
|
// dispatches an added notification and then a removed notification.
|
|
addAsyncAnimTest("single_transition", aOptions, function*() {
|
|
// Start a transition.
|
|
e.style = "transition: background-color 100s; background-color: lime;";
|
|
|
|
// Register for the end of the transition.
|
|
var transitionEnd = await_event(e, "transitionend");
|
|
|
|
// The transition should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after transition start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after transition start");
|
|
|
|
// Advance until near the end of the transition, then wait for it to finish.
|
|
animations[0].currentTime = 99900;
|
|
yield transitionEnd;
|
|
|
|
// After the transition has finished, the Animation should disappear.
|
|
is(e.getAnimations().length, 0,
|
|
"getAnimations().length after transition end");
|
|
|
|
// Wait for the change MutationRecord for seeking the Animation to be
|
|
// delivered, followed by the the removal MutationRecord.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: [], removed: animations }],
|
|
"records after transition end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single transition that is cancelled by resetting
|
|
// the transition-property property dispatches an added notification and
|
|
// then a removed notification.
|
|
addAsyncAnimTest("single_transition_cancelled_property", aOptions, function*() {
|
|
// Start a long transition.
|
|
e.style = "transition: background-color 100s; background-color: lime;";
|
|
|
|
// The transition should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after transition start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after transition start");
|
|
|
|
// Cancel the transition by setting transition-property.
|
|
e.style.transitionProperty = "none";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after transition end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single transition that is cancelled by setting
|
|
// style to the currently animated value dispatches an added
|
|
// notification and then a removed notification.
|
|
addAsyncAnimTest("single_transition_cancelled_value", aOptions, function*() {
|
|
// Start a long transition with a predictable value.
|
|
e.style = "transition: background-color 100s steps(2, end) -51s; background-color: lime;";
|
|
|
|
// The transition should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after transition start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after transition start");
|
|
|
|
// Cancel the transition by setting the current animation value.
|
|
var value = "rgb(128, 255, 0)";
|
|
is(getComputedStyle(e).backgroundColor, value, "half-way transition value");
|
|
e.style.backgroundColor = value;
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after transition end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single transition that is cancelled by setting
|
|
// style to a non-interpolable value dispatches an added notification
|
|
// and then a removed notification.
|
|
addAsyncAnimTest("single_transition_cancelled_noninterpolable", aOptions, function*() {
|
|
// Start a long transition.
|
|
e.style = "transition: line-height 100s; line-height: 100px;";
|
|
|
|
// The transition should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after transition start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after transition start");
|
|
|
|
// Cancel the transition by setting line-height to a non-interpolable value.
|
|
e.style.lineHeight = "normal";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after transition end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single transition and then reversing it
|
|
// dispatches an added notification, then a simultaneous removed and
|
|
// added notification, then a removed notification once finished.
|
|
addAsyncAnimTest("single_transition_reversed", aOptions, function*() {
|
|
// Start a long transition.
|
|
e.style = "transition: background-color 100s step-start; background-color: lime;";
|
|
|
|
// The transition should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after transition start");
|
|
|
|
var firstAnimation = animations[0];
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [firstAnimation], changed: [], removed: [] }],
|
|
"records after transition start");
|
|
|
|
// Wait for the Animation to be playing, then seek well into
|
|
// the transition.
|
|
yield firstAnimation.ready;
|
|
firstAnimation.currentTime = 50000;
|
|
|
|
// Reverse the transition by setting the background-color back to its
|
|
// original value.
|
|
e.style.backgroundColor = "yellow";
|
|
|
|
// The reversal should cause the creation of a new Animation.
|
|
animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after transition reversal");
|
|
|
|
var secondAnimation = animations[0];
|
|
|
|
ok(firstAnimation != secondAnimation,
|
|
"second Animation should be different from the first");
|
|
|
|
// Wait for the change Mutation record from seeking the first animation
|
|
// to be delivered, followed by a subsequent MutationRecord for the removal
|
|
// of the original Animation and the addition of the new Animation.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [firstAnimation], removed: [] },
|
|
{ added: [secondAnimation], changed: [],
|
|
removed: [firstAnimation] }],
|
|
"records after transition reversal");
|
|
|
|
// Cancel the transition.
|
|
e.style.transitionProperty = "none";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: [secondAnimation] }],
|
|
"records after transition end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that multiple transitions starting and ending on an element
|
|
// at the same time get batched up into a single MutationRecord.
|
|
addAsyncAnimTest("multiple_transitions", aOptions, function*() {
|
|
// Start three long transitions.
|
|
e.style = "transition-duration: 100s; " +
|
|
"transition-property: color, background-color, line-height; " +
|
|
"color: blue; background-color: lime; line-height: 24px;";
|
|
|
|
// The transitions should cause the creation of three Animations.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 3, "getAnimations().length after transition starts");
|
|
|
|
// Wait for the single MutationRecord for the Animation additions to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after transition starts");
|
|
|
|
// Wait for the Animations to get going.
|
|
yield animations[0].ready;
|
|
is(animations.filter(p => p.playState == "running").length, 3,
|
|
"number of running Animations");
|
|
|
|
// Seek well into each animation.
|
|
animations.forEach(p => p.currentTime = 50000);
|
|
|
|
// Prepare the set of expected change MutationRecords, one for each
|
|
// animation that was seeked.
|
|
var seekRecords = animations.map(
|
|
p => ({ added: [], changed: [p], removed: [] })
|
|
);
|
|
|
|
// Cancel one of the transitions by setting transition-property.
|
|
e.style.transitionProperty = "background-color, line-height";
|
|
|
|
var colorAnimation = animations.filter(p => p.playState != "running");
|
|
var otherAnimations = animations.filter(p => p.playState == "running");
|
|
|
|
is(colorAnimation.length, 1,
|
|
"number of non-running Animations after cancelling one");
|
|
is(otherAnimations.length, 2,
|
|
"number of running Animations after cancelling one");
|
|
|
|
// Wait for the MutationRecords to be delivered: one for each animation
|
|
// that was seeked, followed by one for the removal of the color animation.
|
|
yield await_frame();
|
|
assert_records(seekRecords.concat(
|
|
{ added: [], changed: [], removed: colorAnimation }),
|
|
"records after color transition end");
|
|
|
|
// Cancel the remaining transitions.
|
|
e.style.transitionProperty = "none";
|
|
|
|
// Wait for the MutationRecord for the other two Animation
|
|
// removals to be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: otherAnimations }],
|
|
"records after other transition ends");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that completes normally
|
|
// dispatches an added notification and then a removed notification.
|
|
addAsyncAnimTest("single_animation", aOptions, function*() {
|
|
// Start an animation.
|
|
e.style = "animation: anim 100s;";
|
|
|
|
// Register for the end of the animation.
|
|
var animationEnd = await_event(e, "animationend");
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Advance until near the end of the animation, then wait for it to finish.
|
|
animations[0].currentTime = 99900;
|
|
yield animationEnd;
|
|
|
|
// After the animation has finished, the Animation should disappear.
|
|
is(e.getAnimations().length, 0,
|
|
"getAnimations().length after animation end");
|
|
|
|
// Wait for the change MutationRecord from seeking the Animation to
|
|
// be delivered, followed by a further MutationRecord for the Animation
|
|
// removal.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that is cancelled by resetting
|
|
// the animation-name property dispatches an added notification and
|
|
// then a removed notification.
|
|
addAsyncAnimTest("single_animation_cancelled_name", aOptions, function*() {
|
|
// Start a long animation.
|
|
e.style = "animation: anim 100s;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Cancel the animation by setting animation-name.
|
|
e.style.animationName = "none";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that is cancelled by updating
|
|
// the animation-duration property dispatches an added notification and
|
|
// then a removed notification.
|
|
addAsyncAnimTest("single_animation_cancelled_duration", aOptions, function*() {
|
|
// Start a long animation.
|
|
e.style = "animation: anim 100s;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Advance the animation by a second.
|
|
animations[0].currentTime += 1000;
|
|
|
|
// Cancel the animation by setting animation-duration to a value less
|
|
// than a second.
|
|
e.style.animationDuration = "0.1s";
|
|
|
|
// Wait for the change MutationRecord from seeking the Animation to
|
|
// be delivered, followed by a further MutationRecord for the Animation
|
|
// removal.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that is cancelled by updating
|
|
// the animation-delay property dispatches an added notification and
|
|
// then a removed notification.
|
|
addAsyncAnimTest("single_animation_cancelled_delay", aOptions, function*() {
|
|
// Start a long animation.
|
|
e.style = "animation: anim 100s;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Cancel the animation by setting animation-delay.
|
|
e.style.animationDelay = "-200s";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that is cancelled by updating
|
|
// the animation-fill-mode property dispatches an added notification and
|
|
// then a removed notification.
|
|
addAsyncAnimTest("single_animation_cancelled_fill", aOptions, function*() {
|
|
// Start a short, filled animation.
|
|
e.style = "animation: anim 100s forwards;";
|
|
|
|
// Register for the end of the animation.
|
|
var animationEnd = await_event(e, "animationend");
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Advance until near the end of the animation, then wait for it to finish.
|
|
animations[0].currentTime = 99900;
|
|
yield animationEnd;
|
|
|
|
// The only MutationRecord at this point should be the change from
|
|
// seeking the Animation.
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after animation starts filling");
|
|
|
|
// Cancel the animation by setting animation-fill-mode.
|
|
e.style.animationFillMode = "none";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that is cancelled by updating
|
|
// the animation-iteration-count property dispatches an added notification
|
|
// and then a removed notification.
|
|
addAsyncAnimTest("single_animation_cancelled_iteration_count",
|
|
aOptions, function*() {
|
|
// Start a short, repeated animation.
|
|
e.style = "animation: anim 0.5s infinite;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Advance the animation until we are past the first iteration.
|
|
animations[0].currentTime += 1000;
|
|
|
|
// The only MutationRecord at this point should be the change from
|
|
// seeking the Animation.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after seeking animations");
|
|
|
|
// Cancel the animation by setting animation-iteration-count.
|
|
e.style.animationIterationCount = "1";
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that starting a single animation that is cancelled by calling
|
|
// cancel() dispatches an added notification and then a removed
|
|
// notification.
|
|
addAsyncAnimTest("single_animation_cancelled_api", aOptions, function*() {
|
|
// Start a short, filled animation.
|
|
e.style = "animation: anim 100s forwards;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Cancel the animation
|
|
animations[0].cancel();
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
// Re-trigger the animation
|
|
animations[0].play();
|
|
yield await_frame();
|
|
|
|
// Wait for the single MutationRecord for the Animation (re-)addition to
|
|
// be delivered.
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that updating an animation property dispatches a changed notification.
|
|
[
|
|
{ name: "duration", prop: "animationDuration", val: "200s" },
|
|
{ name: "timing", prop: "animationTimingFunction", val: "linear" },
|
|
{ name: "iteration", prop: "animationIterationCount", val: "2" },
|
|
{ name: "direction", prop: "animationDirection", val: "reverse" },
|
|
{ name: "state", prop: "animationPlayState", val: "paused" },
|
|
{ name: "delay", prop: "animationDelay", val: "-1s" },
|
|
{ name: "fill", prop: "animationFillMode", val: "both" },
|
|
].forEach(function(aChangeTest) {
|
|
addAsyncAnimTest(`single_animation_change_${aChangeTest.name}`, aOptions, function*() {
|
|
// Start a long animation.
|
|
e.style = "animation: anim 100s;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Change a property of the animation such that it keeps running.
|
|
e.style[aChangeTest.prop] = aChangeTest.val;
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after animation change");
|
|
|
|
// Cancel the animation.
|
|
e.style.animationName = "none";
|
|
|
|
// Wait for the addition, change and removal MutationRecords to be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
});
|
|
|
|
// Test that updating a property on the Animation object dispatches a changed
|
|
// notification.
|
|
[
|
|
{ prop: "playbackRate", val: 0.5 },
|
|
{ prop: "startTime", val: 50000 },
|
|
{ prop: "currentTime", val: 50000 },
|
|
].forEach(function(aChangeTest) {
|
|
addAsyncAnimTest(`single_animation_api_change_${aChangeTest.prop}`,
|
|
aOptions, function*() {
|
|
// Start a long animation
|
|
//
|
|
// We use a forwards fill mode so that even if the change we make causes
|
|
// the animation to become finished, it will still be "relevant" so we
|
|
// won't mark it as removed.
|
|
e.style = "animation: anim 100s forwards";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is playing
|
|
yield animations[0].ready;
|
|
|
|
// Update the property
|
|
animations[0][aChangeTest.prop] = aChangeTest.val;
|
|
|
|
// Make a redundant change
|
|
animations[0][aChangeTest.prop] = animations[0][aChangeTest.prop];
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after animation property change");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
});
|
|
|
|
// Test that making a redundant change to currentTime while an Animation
|
|
// is pause-pending still generates a change MutationRecord since setting
|
|
// the currentTime to any value in this state aborts the pending pause.
|
|
addAsyncAnimTest("change_currentTime_while_pause_pending", aOptions,
|
|
function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is playing
|
|
yield animations[0].ready;
|
|
|
|
// Pause the animation
|
|
animations[0].pause();
|
|
|
|
// We are now pause-pending. Even if we make a redundant change to the
|
|
// currentTime, we should still get a change record because setting the
|
|
// currentTime while pause-pending has the effect of cancelling a pause.
|
|
animations[0].currentTime = animations[0].currentTime;
|
|
|
|
// Wait for the two MutationRecords for the Animation changes to
|
|
// be delivered: one for pausing, one for aborting the pause.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after pausing then seeking");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling reverse() dispatches a changed notification.
|
|
addAsyncAnimTest("reverse", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s both";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Reverse
|
|
animations[0].reverse();
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after calling reverse()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling reverse() does *not* dispatch a changed notification
|
|
// when playbackRate == 0.
|
|
addAsyncAnimTest("reverse_with_zero_playbackRate", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s both";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Seek to the middle and set playbackRate to zero.
|
|
animations[0].currentTime = 50000;
|
|
animations[0].playbackRate = 0;
|
|
|
|
// Wait for the MutationRecords, one for each change, to be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after seeking and setting playbackRate");
|
|
|
|
// Reverse
|
|
animations[0].reverse();
|
|
|
|
// We should get no notifications.
|
|
assert_records([], "records after calling reverse()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling finish() on a forwards-filling Animation dispatches
|
|
// a changed notification.
|
|
addAsyncAnimTest("finish_with_forwards_fill", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s forwards";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is playing
|
|
yield animations[0].ready;
|
|
|
|
// Finish
|
|
animations[0].finish();
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after finish()");
|
|
|
|
// Redundant finish
|
|
animations[0].finish();
|
|
|
|
// Wait to ensure no change is dispatched
|
|
yield await_frame();
|
|
assert_records([], "records after redundant finish()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling finish() on an Animation that does not fill forwards,
|
|
// dispatches a removal notification.
|
|
addAsyncAnimTest("finish_without_fill", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is playing
|
|
yield animations[0].ready;
|
|
|
|
// Finish
|
|
animations[0].finish();
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after finishing");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that calling finish() on a paused (but otherwise finished) animation
|
|
// dispatches a changed notification.
|
|
addAsyncAnimTest("finish_from_pause", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s forwards";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is playing.
|
|
yield animations[0].ready;
|
|
|
|
// Finish and pause.
|
|
animations[0].finish();
|
|
animations[0].pause();
|
|
|
|
// Wait for the pause to complete.
|
|
yield animations[0].ready;
|
|
is(animations[0].playState, "paused",
|
|
"playState after finishing and pausing");
|
|
|
|
// We should have two MutationRecords for the Animation changes:
|
|
// one for the finish, one for the pause.
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after finish() and pause()");
|
|
|
|
// Call finish() again.
|
|
animations[0].finish();
|
|
is(animations[0].playState, "finished",
|
|
"playState after finishing from paused state");
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered. Even though the currentTime does not change, the
|
|
// playState will change.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after finish() and pause()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling finish() on a pause-pending (but otherwise finished)
|
|
// animation dispatches a changed notification.
|
|
addAsyncAnimTest("finish_from_pause_pending", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s forwards";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is playing.
|
|
yield animations[0].ready;
|
|
|
|
// Finish and pause.
|
|
animations[0].finish();
|
|
animations[0].pause();
|
|
is(animations[0].playState, "pending",
|
|
"playState after finishing and calling pause()");
|
|
|
|
// Call finish() again to abort the pause
|
|
animations[0].finish();
|
|
is(animations[0].playState, "finished",
|
|
"playState after finishing and calling pause()");
|
|
|
|
// Wait for three MutationRecords for the Animation changes to
|
|
// be delivered: one for each finish(), pause(), finish() operation.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after finish(), pause(), finish()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling play() on a paused Animation dispatches a changed
|
|
// notification.
|
|
addAsyncAnimTest("play", aOptions, function*() {
|
|
// Start a long, paused animation
|
|
e.style = "animation: anim 100s paused";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is ready
|
|
yield animations[0].ready;
|
|
|
|
// Play
|
|
animations[0].play();
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield animations[0].ready;
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after play()");
|
|
|
|
// Redundant play
|
|
animations[0].play();
|
|
|
|
// Wait to ensure no change is dispatched
|
|
yield await_frame();
|
|
assert_records([], "records after redundant play()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling play() on a finished Animation that fills forwards
|
|
// dispatches a changed notification.
|
|
addAsyncAnimTest("play_filling_forwards", aOptions, function*() {
|
|
// Start a long animation with a forwards fill
|
|
e.style = "animation: anim 100s forwards";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is ready
|
|
yield animations[0].ready;
|
|
|
|
// Seek to the end
|
|
animations[0].finish();
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after finish()");
|
|
|
|
// Since we are filling forwards, calling play() should produce a
|
|
// change record since the animation remains relevant.
|
|
animations[0].play();
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after play()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling play() on a finished Animation that does *not* fill
|
|
// forwards dispatches an addition notification.
|
|
addAsyncAnimTest("play_after_finish", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is ready
|
|
yield animations[0].ready;
|
|
|
|
// Seek to the end
|
|
animations[0].finish();
|
|
|
|
// Wait for the single MutationRecord for the Animation removal to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after finish()");
|
|
|
|
// Since we are *not* filling forwards, calling play() is equivalent
|
|
// to creating a new animation since it becomes relevant again.
|
|
animations[0].play();
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after play()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling pause() on an Animation dispatches a changed
|
|
// notification.
|
|
addAsyncAnimTest("pause", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is ready
|
|
yield animations[0].ready;
|
|
|
|
// Pause
|
|
animations[0].pause();
|
|
|
|
// Wait for the single MutationRecord for the Animation change to
|
|
// be delivered.
|
|
yield animations[0].ready;
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after pause()");
|
|
|
|
// Redundant pause
|
|
animations[0].pause();
|
|
|
|
// Wait to ensure no change is dispatched
|
|
yield animations[0].ready;
|
|
assert_records([], "records after redundant pause()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling pause() on an Animation that is pause-pending
|
|
// does not dispatch an additional changed notification.
|
|
addAsyncAnimTest("pause_while_pause_pending", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is ready
|
|
yield animations[0].ready;
|
|
|
|
// Pause
|
|
animations[0].pause();
|
|
|
|
// We are now pause-pending, but pause again
|
|
animations[0].pause();
|
|
|
|
// We should only get a single change record
|
|
yield animations[0].ready;
|
|
assert_records([{ added: [], changed: animations, removed: [] }],
|
|
"records after pause()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that calling play() on an Animation that is pause-pending
|
|
// dispatches a changed notification.
|
|
addAsyncAnimTest("aborted_pause", aOptions, function*() {
|
|
// Start a long animation
|
|
e.style = "animation: anim 100s";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Wait until the animation is ready
|
|
yield animations[0].ready;
|
|
|
|
// Pause
|
|
animations[0].pause();
|
|
|
|
// We are now pause-pending. If we play() now, we will abort the pause
|
|
animations[0].play();
|
|
|
|
// We should get two change records
|
|
yield animations[0].ready;
|
|
assert_records([{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after aborting a pause()");
|
|
|
|
// Cancel the animation.
|
|
e.style = "";
|
|
|
|
// Wait for the single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
});
|
|
|
|
// Test that a non-cancelling change to an animation followed immediately by a
|
|
// cancelling change will only send an animation removal notification.
|
|
addAsyncAnimTest("coalesce_change_cancel", aOptions, function*() {
|
|
// Start a long animation.
|
|
e.style = "animation: anim 100s;";
|
|
|
|
// The animation should cause the creation of a single Animation.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 1, "getAnimations().length after animation start");
|
|
|
|
// Wait for the single MutationRecord for the Animation addition to
|
|
// be delivered.
|
|
yield await_frame();
|
|
assert_records([{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Update the animation's delay such that it is still running.
|
|
e.style.animationDelay = "-1s";
|
|
|
|
// Then cancel the animation by updating its duration.
|
|
e.style.animationDuration = "0.5s";
|
|
|
|
// We should get a single removal notification.
|
|
yield await_frame();
|
|
assert_records([{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
e.style = "";
|
|
});
|
|
|
|
// Test that attempting to start an animation that should already be finished
|
|
// does not send any notifications.
|
|
addAsyncAnimTest("already_finished", aOptions, function*() {
|
|
// Start an animation that should already be finished.
|
|
e.style = "animation: anim 1s -2s;";
|
|
|
|
// The animation should cause no Animations to be created.
|
|
var animations = e.getAnimations();
|
|
is(animations.length, 0, "getAnimations().length after animation start");
|
|
|
|
// And we should get no notifications.
|
|
yield await_frame();
|
|
assert_records([], "records after attempted animation start");
|
|
|
|
e.style = "";
|
|
});
|
|
});
|
|
|
|
addAsyncAnimTest("tree_ordering", { observe: div, subtree: true }, function*() {
|
|
|
|
// Create a tree with two children:
|
|
//
|
|
// div
|
|
// / \
|
|
// childA childB
|
|
var childA = document.createElement("div");
|
|
var childB = document.createElement("div");
|
|
|
|
div.appendChild(childA);
|
|
div.appendChild(childB);
|
|
|
|
// Start an animation on each (using order: childB, div, childA)
|
|
//
|
|
// We include multiple animations on some nodes so that we can test batching
|
|
// works as expected later in this test.
|
|
childB.style = "animation: anim 100s";
|
|
div.style = "animation: anim 100s, anim 100s, anim 100s";
|
|
childA.style = "animation: anim 100s, anim 100s";
|
|
|
|
var divAnimations = div.getAnimations();
|
|
var childAAnimations = childA.getAnimations();
|
|
var childBAnimations = childB.getAnimations();
|
|
|
|
// The order in which we get the corresponding records is currently
|
|
// based on the order we visit these nodes when updating styles.
|
|
//
|
|
// That is because we don't do any document-level batching of animation
|
|
// mutation records when we flush styles. We may introduce that in the
|
|
// future but for now all we are interested in testing here is that the order
|
|
// these records are dispatched is consistent between runs.
|
|
//
|
|
// We currently expect to get records in order childA, childB, div
|
|
yield await_frame();
|
|
assert_records([{ added: childAAnimations, changed: [], removed: [] },
|
|
{ added: childBAnimations, changed: [], removed: [] },
|
|
{ added: divAnimations, changed: [], removed: [] }],
|
|
"records after simultaneous animation start");
|
|
|
|
// The one case where we *do* currently perform document-level (or actually
|
|
// timeline-level) batching is when animations are updated from a refresh
|
|
// driver tick. In particular, this means that when animations finish
|
|
// naturally the removed records should be dispatched according to the
|
|
// position of the elements in the tree.
|
|
//
|
|
|
|
// First, flatten the set of animations.
|
|
var animations = [ divAnimations,
|
|
childAAnimations,
|
|
childBAnimations ].reduce(
|
|
(a, b) => a.concat(b), []
|
|
);
|
|
|
|
// Fast-forward to *just* before the end of the animation.
|
|
animations.forEach(animation => animation.currentTime = 99999);
|
|
|
|
// Prepare the set of expected change MutationRecords, one for each
|
|
// animation that was seeked.
|
|
var seekRecords = animations.map(
|
|
p => ({ added: [], changed: [p], removed: [] })
|
|
);
|
|
|
|
yield await_event(div, "animationend");
|
|
|
|
// After the changed notifications, which will be dispatched in the order that
|
|
// the animations were seeked, we should get removal MutationRecords in order
|
|
// div, childA, childB
|
|
assert_records(seekRecords.concat(
|
|
{ added: [], changed: [], removed: divAnimations },
|
|
{ added: [], changed: [], removed: childAAnimations },
|
|
{ added: [], changed: [], removed: childBAnimations }),
|
|
"records after finishing");
|
|
|
|
// Clean up
|
|
div.style = "";
|
|
childA.remove();
|
|
childB.remove();
|
|
});
|
|
|
|
// Run the tests.
|
|
SimpleTest.requestLongerTimeout(2);
|
|
SimpleTest.waitForExplicitFinish();
|
|
|
|
runAllAsyncTests().then(function() {
|
|
SimpleTest.finish();
|
|
}, function(aError) {
|
|
ok(false, "Something failed: " + aError);
|
|
});
|
|
</script>
|