gecko-dev/dom/animation/test/chrome/test_animation_observers_sy...

945 строки
35 KiB
HTML

<!DOCTYPE html>
<meta charset=utf-8>
<title>
Test chrome-only MutationObserver animation notifications (sync tests)
</title>
<!--
This file contains synchronous tests for animation mutation observers.
In general we prefer to write synchronous tests since they are less likely to
timeout when run on automation. Tests that require asynchronous steps (e.g.
waiting on events) should be added to test_animations_observers_async.html
instead.
-->
<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>
/**
* Return a new MutationObserver which observing |target| element
* with { animations: true, subtree: |subtree| } option.
*
* NOTE: This observer should be used only with takeRecords(). If any of
* MutationRecords are observed in the callback of the MutationObserver,
* it will raise an assertion.
*/
function setupSynchronousObserver(t, target, subtree) {
var observer = new MutationObserver(records => {
assert_unreached("Any MutationRecords should not be observed in this " +
"callback");
});
t.add_cleanup(() => {
observer.disconnect();
});
observer.observe(target, { animations: true, subtree: subtree });
return observer;
}
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; ' +
'content: \'\';' });
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;
}
function runTest() {
[ { 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({ }, 100 * MS_PER_SEC);
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.composite = "add";
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after composite is changed");
anim.effect.composite = "add";
assert_equals_records(observer.takeRecords(),
[], "no record after setting the same composite");
}, "set_composite");
// 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(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 reverse() on an Animation does *not* dispatch a changed
// notification when it throws an exception.
test(t => {
// Start an infinite animation
var div = addDiv(t, { style: "animation: anim 10s infinite" });
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");
// Shift the animation into the future such that when we call reverse
// it will try to seek to the (infinite) end.
animations[0].startTime = 100 * MS_PER_SEC;
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: animations, removed: [] }],
"records after adjusting startTime");
// Reverse: should throw
assert_throws('InvalidStateError', () => {
animations[0].reverse();
}, 'reverse() on future infinite animation throws an exception');
// We should get no notifications.
assert_equals_records(observer.takeRecords(),
[], "records after calling reverse()");
}, "reverse_with_exception");
// 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,
aOptions.subtree ? div.parentNode : div,
aOptions.subtree);
var anim = div.animate({ opacity: [ 0, 1 ] },
{ duration: 100 * MS_PER_SEC,
iterationComposite: 'replace' });
assert_equals_records(observer.takeRecords(),
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.iterationComposite = 'accumulate';
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after iterationComposite is changed");
anim.effect.iterationComposite = 'accumulate';
assert_equals_records(observer.takeRecords(),
[], "no record after setting the same iterationComposite");
}, "set_iterationComposite");
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.setKeyframes({ opacity: 0.1 });
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after keyframes are changed");
anim.effect.setKeyframes({ opacity: 0.1 });
assert_equals_records(observer.takeRecords(),
[], "no record after setting the same keyframes");
anim.effect.setKeyframes(null);
assert_equals_records(observer.takeRecords(),
[{ added: [], changed: [anim], removed: [] }],
"records after keyframes are set to empty");
}, "set_keyframes");
});
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");
}
setup({explicit_done: true});
SpecialPowers.pushPrefEnv(
{ set: [["dom.animations-api.core.enabled", true]] },
function() {
runTest();
done();
}
);
</script>