summaryrefslogtreecommitdiffstats
path: root/dom/animation/test/chrome/test_observers_for_sync_api.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/animation/test/chrome/test_observers_for_sync_api.html')
-rw-r--r--dom/animation/test/chrome/test_observers_for_sync_api.html854
1 files changed, 854 insertions, 0 deletions
diff --git a/dom/animation/test/chrome/test_observers_for_sync_api.html b/dom/animation/test/chrome/test_observers_for_sync_api.html
new file mode 100644
index 000000000..20c3f3670
--- /dev/null
+++ b/dom/animation/test/chrome/test_observers_for_sync_api.html
@@ -0,0 +1,854 @@
+<!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>