855 lines
31 KiB
HTML
855 lines
31 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset=utf-8>
|
|
<title>
|
|
Test chrome-only MutationObserver animation notifications for sync APIs
|
|
</title>
|
|
<script type="application/javascript" src="../testharness.js"></script>
|
|
<script type="application/javascript" src="../testharnessreport.js"></script>
|
|
<script type="application/javascript" src="../testcommon.js"></script>
|
|
<div id="log"></div>
|
|
<style>
|
|
@keyframes anim {
|
|
to { transform: translate(100px); }
|
|
}
|
|
@keyframes anotherAnim {
|
|
to { transform: translate(0px); }
|
|
}
|
|
</style>
|
|
<script>
|
|
|
|
function assert_record_list(actual, expected, desc, index, listName) {
|
|
assert_equals(actual.length, expected.length,
|
|
`${desc} - record[${index}].${listName} length`);
|
|
if (actual.length != expected.length) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < actual.length; i++) {
|
|
assert_not_equals(actual.indexOf(expected[i]), -1,
|
|
`${desc} - record[${index}].${listName} contains expected Animation`);
|
|
}
|
|
}
|
|
|
|
function assert_equals_records(actual, expected, desc) {
|
|
assert_equals(actual.length, expected.length, `${desc} - number of records`);
|
|
if (actual.length != expected.length) {
|
|
return;
|
|
}
|
|
for (var i = 0; i < actual.length; i++) {
|
|
assert_record_list(actual[i].addedAnimations,
|
|
expected[i].added, desc, i, "addedAnimations");
|
|
assert_record_list(actual[i].changedAnimations,
|
|
expected[i].changed, desc, i, "changedAnimations");
|
|
assert_record_list(actual[i].removedAnimations,
|
|
expected[i].removed, desc, i, "removedAnimations");
|
|
}
|
|
}
|
|
|
|
// Create a pseudo element
|
|
function createPseudo(test, element, type) {
|
|
addStyle(test, { '@keyframes anim': '',
|
|
['.pseudo::' + type]: 'animation: anim 10s;' });
|
|
element.classList.add('pseudo');
|
|
var anims = document.getAnimations();
|
|
assert_true(anims.length >= 1);
|
|
var anim = anims[anims.length - 1];
|
|
assert_equals(anim.effect.target.parentElement, element);
|
|
assert_equals(anim.effect.target.type, '::' + type);
|
|
anim.cancel();
|
|
return anim.effect.target;
|
|
}
|
|
|
|
[ { subtree: false },
|
|
{ subtree: true }
|
|
].forEach(aOptions => {
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.timing.duration = 100 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after duration is changed");
|
|
|
|
anim.effect.timing.duration = 100 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value");
|
|
|
|
anim.currentTime = anim.effect.timing.duration * 2;
|
|
anim.finish();
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after animation end");
|
|
|
|
anim.effect.timing.duration = anim.effect.timing.duration * 3;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation restarted");
|
|
|
|
anim.effect.timing.duration = "auto";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after duration set \"auto\"");
|
|
|
|
anim.effect.timing.duration = "auto";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value \"auto\"");
|
|
}, "change_duration_and_currenttime");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.timing.endDelay = 10 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after endDelay is changed");
|
|
|
|
anim.effect.timing.endDelay = 10 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value");
|
|
|
|
anim.currentTime = 109 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after currentTime during endDelay");
|
|
|
|
anim.effect.timing.endDelay = -110 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning negative value");
|
|
}, "change_enddelay_and_currenttime");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC,
|
|
endDelay: -100 * MS_PER_SEC });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after animation is added");
|
|
}, "zero_end_time");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.timing.iterations = 2;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after iterations is changed");
|
|
|
|
anim.effect.timing.iterations = 2;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value");
|
|
|
|
anim.effect.timing.iterations = 0;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after animation end");
|
|
|
|
anim.effect.timing.iterations = Infinity;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation restarted");
|
|
}, "change_iterations");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.timing.delay = 100;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after delay is changed");
|
|
|
|
anim.effect.timing.delay = 100;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value");
|
|
|
|
anim.effect.timing.delay = -100 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after animation end");
|
|
|
|
anim.effect.timing.delay = 0;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation restarted");
|
|
}, "change_delay");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC,
|
|
easing: "steps(2, start)" });
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.timing.easing = "steps(2, end)";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after easing is changed");
|
|
|
|
anim.effect.timing.easing = "steps(2, end)";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value");
|
|
}, "change_easing");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100, delay: -100 });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning negative value");
|
|
}, "negative_delay_in_constructor");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var effect = new KeyframeEffectReadOnly(null,
|
|
{ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC });
|
|
var anim = new Animation(effect, document.timeline);
|
|
anim.play();
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "no records after animation is added");
|
|
}, "create_animation_without_target");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.target = div;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "no records after setting the same target");
|
|
|
|
anim.effect.target = null;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after setting null");
|
|
|
|
anim.effect.target = null;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after setting redundant null");
|
|
}, "set_redundant_animation_target");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect = null;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after animation is removed");
|
|
}, "set_null_animation_effect");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = new Animation();
|
|
anim.play();
|
|
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
|
|
100 * MS_PER_SEC);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
}, "set_effect_on_null_effect_animation");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ marginLeft: [ "0px", "100px" ] },
|
|
100 * MS_PER_SEC);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
|
|
100 * MS_PER_SEC);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after replace effects");
|
|
}, "replace_effect_targeting_on_the_same_element");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate({ marginLeft: [ "0px", "100px" ] },
|
|
100 * MS_PER_SEC);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.currentTime = 60 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after animation is changed");
|
|
|
|
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
|
|
50 * MS_PER_SEC);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after replacing effects");
|
|
}, "replace_effect_targeting_on_the_same_element_not_in_effect");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate([ { marginLeft: "0px" },
|
|
{ marginLeft: "-20px" },
|
|
{ marginLeft: "100px" },
|
|
{ marginLeft: "50px" } ],
|
|
{ duration: 100 * MS_PER_SEC });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.spacing = "paced(margin-left)";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after animation is changed");
|
|
}, "set_spacing");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate([ { marginLeft: "0px" },
|
|
{ marginLeft: "-20px" },
|
|
{ marginLeft: "100px" },
|
|
{ marginLeft: "50px" } ],
|
|
{ duration: 100 * MS_PER_SEC,
|
|
spacing: "paced(margin-left)" });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.spacing = "paced(animation-duration)";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after setting a non-animatable paced property");
|
|
}, "set_spacing_on_a_non-animatable_property");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var anim = div.animate([ { marginLeft: "0px" },
|
|
{ marginLeft: "-20px" },
|
|
{ marginLeft: "100px" },
|
|
{ marginLeft: "50px" } ],
|
|
{ duration: 100 * MS_PER_SEC,
|
|
spacing: "paced(margin-left)" });
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.spacing = "paced(margin-left)";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "no record after setting the same spacing");
|
|
}, "set_the_same_spacing");
|
|
|
|
// Test that starting a single animation that is cancelled by calling
|
|
// cancel() dispatches an added notification and then a removed
|
|
// notification.
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s forwards" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
animations[0].cancel();
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: animations }],
|
|
"records after animation end");
|
|
|
|
// Re-trigger the animation.
|
|
animations[0].play();
|
|
|
|
// Single MutationRecord for the Animation (re-)addition.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
}, "single_animation_cancelled_api");
|
|
|
|
// Test that updating a property on the Animation object dispatches a changed
|
|
// notification.
|
|
[
|
|
{ prop: "playbackRate", val: 0.5 },
|
|
{ prop: "startTime", val: 50 * MS_PER_SEC },
|
|
{ prop: "currentTime", val: 50 * MS_PER_SEC },
|
|
].forEach(function(aChangeTest) {
|
|
test(t => {
|
|
// 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.
|
|
var div = addDiv(t, { style: "animation: anim 100s forwards" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Update the property.
|
|
animations[0][aChangeTest.prop] = aChangeTest.val;
|
|
|
|
// Make a redundant change.
|
|
animations[0][aChangeTest.prop] = animations[0][aChangeTest.prop];
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: animations, removed: [] }],
|
|
"records after animation property change");
|
|
}, `single_animation_api_change_${aChangeTest.prop}`);
|
|
});
|
|
|
|
// 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.
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
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;
|
|
|
|
// Two MutationRecords for the Animation changes: one for pausing, one
|
|
// for aborting the pause.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after pausing then seeking");
|
|
}, "change_currentTime_while_pause_pending");
|
|
|
|
// Test that calling finish() on a forwards-filling Animation dispatches
|
|
// a changed notification.
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s forwards" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
animations[0].finish();
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: animations, removed: [] }],
|
|
"records after finish()");
|
|
|
|
// Redundant finish.
|
|
animations[0].finish();
|
|
|
|
// Ensure no change records.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after redundant finish()");
|
|
}, "finish_with_forwards_fill");
|
|
|
|
// Test that calling finish() on an Animation that does not fill forwards,
|
|
// dispatches a removal notification.
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
animations[0].finish();
|
|
|
|
// Single MutationRecord for the Animation removal.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: animations }],
|
|
"records after finishing");
|
|
}, "finish_without_fill");
|
|
|
|
// Test that calling finish() on a forwards-filling Animation dispatches
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animation = div.getAnimations()[0];
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [animation], changed: [], removed: []}],
|
|
"records after creation");
|
|
animation.id = "new id";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [animation], removed: []}],
|
|
"records after id is changed");
|
|
|
|
animation.id = "new id";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value with id");
|
|
}, "change_id");
|
|
|
|
// Test that calling reverse() dispatches a changed notification.
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s both" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
animations[0].reverse();
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: animations, removed: [] }],
|
|
"records after calling reverse()");
|
|
}, "reverse");
|
|
|
|
// Test that calling reverse() does *not* dispatch a changed notification
|
|
// when playbackRate == 0.
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s both" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 1,
|
|
"getAnimations().length after animation start");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: [] }],
|
|
"records after animation start");
|
|
|
|
// Seek to the middle and set playbackRate to zero.
|
|
animations[0].currentTime = 50 * MS_PER_SEC;
|
|
animations[0].playbackRate = 0;
|
|
|
|
// Two MutationRecords, one for each change.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: animations, removed: [] },
|
|
{ added: [], changed: animations, removed: [] }],
|
|
"records after seeking and setting playbackRate");
|
|
|
|
animations[0].reverse();
|
|
|
|
// We should get no notifications.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after calling reverse()");
|
|
}, "reverse_with_zero_playbackRate");
|
|
|
|
// Test that attempting to start an animation that should already be finished
|
|
// does not send any notifications.
|
|
test(t => {
|
|
// Start an animation that should already be finished.
|
|
var div = addDiv(t, { style: "animation: anim 1s -2s;" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
// The animation should cause no Animations to be created.
|
|
var animations = div.getAnimations();
|
|
assert_equals(animations.length, 0,
|
|
"getAnimations().length after animation start");
|
|
|
|
// And we should get no notifications.
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after attempted animation start");
|
|
}, "already_finished");
|
|
|
|
test(t => {
|
|
var div = addDiv(t, { style: "animation: anim 100s, anotherAnim 100s" });
|
|
var observer =
|
|
setupSynchronousObserver(t,
|
|
aOptions.subtree ? div.parentNode : div,
|
|
aOptions.subtree);
|
|
|
|
var animations = div.getAnimations();
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: animations, changed: [], removed: []}],
|
|
"records after creation");
|
|
|
|
div.style.animation = "anotherAnim 100s, anim 100s";
|
|
animations = div.getAnimations();
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: animations, removed: []}],
|
|
"records after the order is changed");
|
|
|
|
div.style.animation = "anotherAnim 100s, anim 100s";
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "no records after applying the same order");
|
|
}, "animtion_order_change");
|
|
|
|
});
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer = setupSynchronousObserver(t, div, true);
|
|
|
|
var child = document.createElement("div");
|
|
div.appendChild(child);
|
|
|
|
var anim1 = div.animate({ marginLeft: [ "0px", "50px" ] },
|
|
100 * MS_PER_SEC);
|
|
var anim2 = child.animate({ marginLeft: [ "0px", "100px" ] },
|
|
50 * MS_PER_SEC);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim1], changed: [], removed: [] },
|
|
{ added: [anim2], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
// After setting a new effect, we remove the current animation, anim1,
|
|
// because it is no longer attached to |div|, and then remove the previous
|
|
// animation, anim2. Finally, add back the anim1 which is in effect on
|
|
// |child| now. In addition, we sort them by tree order and they are
|
|
// batched.
|
|
anim1.effect = anim2.effect;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim1] }, // div
|
|
{ added: [anim1], changed: [], removed: [anim2] }], // child
|
|
"records after animation effects are changed");
|
|
}, "set_effect_with_previous_animation");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var observer = setupSynchronousObserver(t, document, true);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC });
|
|
|
|
var newTarget = document.createElement("div");
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.target = null;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after setting null");
|
|
|
|
anim.effect.target = div;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after setting a target");
|
|
|
|
anim.effect.target = addDiv(t);
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] },
|
|
{ added: [anim], changed: [], removed: [] }],
|
|
"records after setting a different target");
|
|
}, "set_animation_target");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var pseudoTarget = createPseudo(t, div, 'before');
|
|
var observer = setupSynchronousObserver(t, div, true);
|
|
|
|
var anim = pseudoTarget.animate({ opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.effect.timing.duration = 100 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [anim], removed: [] }],
|
|
"records after duration is changed");
|
|
|
|
anim.effect.timing.duration = 100 * MS_PER_SEC;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value");
|
|
|
|
anim.currentTime = anim.effect.timing.duration * 2;
|
|
anim.finish();
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after animation end");
|
|
|
|
anim.effect.timing.duration = anim.effect.timing.duration * 3;
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation restarted");
|
|
|
|
anim.effect.timing.duration = "auto";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after duration set \"auto\"");
|
|
|
|
anim.effect.timing.duration = "auto";
|
|
assert_equals_records(observer.takeRecords(),
|
|
[], "records after assigning same value \"auto\"");
|
|
}, "change_duration_and_currenttime_on_pseudo_elements");
|
|
|
|
test(t => {
|
|
var div = addDiv(t);
|
|
var pseudoTarget = createPseudo(t, div, 'before');
|
|
var observer = setupSynchronousObserver(t, div, false);
|
|
|
|
var anim = div.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC });
|
|
var pAnim = pseudoTarget.animate({ opacity: [ 0, 1 ] },
|
|
{ duration: 100 * MS_PER_SEC });
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [anim], changed: [], removed: [] }],
|
|
"records after animation is added");
|
|
|
|
anim.finish();
|
|
pAnim.finish();
|
|
|
|
assert_equals_records(observer.takeRecords(),
|
|
[{ added: [], changed: [], removed: [anim] }],
|
|
"records after animation is finished");
|
|
}, "exclude_animations_targeting_pseudo_elements");
|
|
|
|
</script>
|