summaryrefslogtreecommitdiff
path: root/dom/animation/test/css-animations/file_element-get-animations.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/animation/test/css-animations/file_element-get-animations.html')
-rw-r--r--dom/animation/test/css-animations/file_element-get-animations.html445
1 files changed, 445 insertions, 0 deletions
diff --git a/dom/animation/test/css-animations/file_element-get-animations.html b/dom/animation/test/css-animations/file_element-get-animations.html
new file mode 100644
index 0000000000..68386c98b0
--- /dev/null
+++ b/dom/animation/test/css-animations/file_element-get-animations.html
@@ -0,0 +1,445 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+@keyframes anim1 {
+ to { left: 100px }
+}
+@keyframes anim2 {
+ to { top: 100px }
+}
+@keyframes multiPropAnim {
+ to { background: green, opacity: 0.5, left: 100px, top: 100px }
+}
+@keyframes empty { }
+</style>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = addDiv(t);
+ assert_equals(div.getAnimations().length, 0,
+ 'getAnimations returns an empty sequence for an element'
+ + ' with no animations');
+}, 'getAnimations for non-animated content');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+
+ // FIXME: This test does too many things. It should be split up.
+
+ // Add an animation
+ div.style.animation = 'anim1 100s';
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 1,
+ 'getAnimations returns an Animation running CSS Animations');
+ return animations[0].ready.then(function() {
+ var startTime = animations[0].startTime;
+ assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
+ 'CSS animation has a sensible start time');
+
+ // Wait a moment then add a second animation.
+ //
+ // We wait for the next frame so that we can test that the start times of
+ // the animations differ.
+ return waitForFrame();
+ }).then(function() {
+ div.style.animation = 'anim1 100s, anim2 100s';
+ animations = div.getAnimations();
+ assert_equals(animations.length, 2,
+ 'getAnimations returns one Animation for each value of'
+ + ' animation-name');
+ // Wait until both Animations are ready
+ // (We don't make any assumptions about the order of the Animations since
+ // that is the purpose of the following test.)
+ return waitForAllAnimations(animations);
+ }).then(function() {
+ assert_true(animations[0].startTime < animations[1].startTime,
+ 'Additional Animations for CSS animations start after the original'
+ + ' animation and appear later in the list');
+ });
+}, 'getAnimations for CSS Animations');
+
+test(function(t) {
+ var div = addDiv(t, { style: 'animation: anim1 100s' });
+ assert_class_string(div.getAnimations()[0], 'CSSAnimation',
+ 'Interface of returned animation is CSSAnimation');
+}, 'getAnimations returns CSSAnimation objects for CSS Animations');
+
+test(function(t) {
+ var div = addDiv(t);
+
+ // Add an animation that targets multiple properties
+ div.style.animation = 'multiPropAnim 100s';
+ assert_equals(div.getAnimations().length, 1,
+ 'getAnimations returns only one Animation for a CSS Animation'
+ + ' that targets multiple properties');
+}, 'getAnimations for multi-property animations');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+
+ // Add an animation
+ div.style.backgroundColor = 'red';
+ div.style.animation = 'anim1 100s';
+ window.getComputedStyle(div).backgroundColor;
+
+ // Wait until a frame after the animation starts, then add a transition
+ var animations = div.getAnimations();
+ return animations[0].ready.then(waitForFrame).then(function() {
+ div.style.transition = 'all 100s';
+ div.style.backgroundColor = 'green';
+
+ animations = div.getAnimations();
+ assert_equals(animations.length, 2,
+ 'getAnimations returns Animations for both animations and'
+ + ' transitions that run simultaneously');
+ assert_class_string(animations[0], 'CSSTransition',
+ 'First-returned animation is the CSS Transition');
+ assert_class_string(animations[1], 'CSSAnimation',
+ 'Second-returned animation is the CSS Animation');
+ });
+}, 'getAnimations for both CSS Animations and CSS Transitions at once');
+
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // Set up event listener
+ div.addEventListener('animationend', t.step_func(function() {
+ assert_equals(div.getAnimations().length, 0,
+ 'getAnimations does not return Animations for finished '
+ + ' (and non-forwards-filling) CSS Animations');
+ t.done();
+ }));
+
+ // Add a very short animation
+ div.style.animation = 'anim1 0.01s';
+}, 'getAnimations for CSS Animations that have finished');
+
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // Set up event listener
+ div.addEventListener('animationend', t.step_func(function() {
+ assert_equals(div.getAnimations().length, 1,
+ 'getAnimations returns Animations for CSS Animations that have'
+ + ' finished but are filling forwards');
+ t.done();
+ }));
+
+ // Add a very short animation
+ div.style.animation = 'anim1 0.01s forwards';
+}, 'getAnimations for CSS Animations that have finished but are'
+ + ' forwards filling');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'none 100s';
+
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 0,
+ 'getAnimations returns an empty sequence for an element'
+ + ' with animation-name: none');
+
+ div.style.animation = 'none 100s, anim1 100s';
+ animations = div.getAnimations();
+ assert_equals(animations.length, 1,
+ 'getAnimations returns Animations only for those CSS Animations whose'
+ + ' animation-name is not none');
+}, 'getAnimations for CSS Animations with animation-name: none');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'missing 100s';
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 0,
+ 'getAnimations returns an empty sequence for an element'
+ + ' with animation-name: missing');
+
+ div.style.animation = 'anim1 100s, missing 100s';
+ animations = div.getAnimations();
+ assert_equals(animations.length, 1,
+ 'getAnimations returns Animations only for those CSS Animations whose'
+ + ' animation-name is found');
+}, 'getAnimations for CSS Animations with animation-name: missing');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim1 100s, notyet 100s';
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 1,
+ 'getAnimations initally only returns Animations for CSS Animations whose'
+ + ' animation-name is found');
+
+ return animations[0].ready.then(waitForFrame).then(function() {
+ var keyframes = '@keyframes notyet { to { left: 100px; } }';
+ document.styleSheets[0].insertRule(keyframes, 0);
+ animations = div.getAnimations();
+ assert_equals(animations.length, 2,
+ 'getAnimations includes Animation when @keyframes rule is added'
+ + ' later');
+ return waitForAllAnimations(animations);
+ }).then(function() {
+ assert_true(animations[0].startTime < animations[1].startTime,
+ 'Newly added animation has a later start time');
+ document.styleSheets[0].deleteRule(0);
+ });
+}, 'getAnimations for CSS Animations where the @keyframes rule is added'
+ + ' later');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim1 100s, anim1 100s';
+ assert_equals(div.getAnimations().length, 2,
+ 'getAnimations returns one Animation for each CSS animation-name'
+ + ' even if the names are duplicated');
+}, 'getAnimations for CSS Animations with duplicated animation-name');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'empty 100s';
+ assert_equals(div.getAnimations().length, 1,
+ 'getAnimations returns Animations for CSS animations with an'
+ + ' empty keyframes rule');
+}, 'getAnimations for CSS Animations with empty keyframes rule');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim1 100s 100s';
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 1,
+ 'getAnimations returns animations for CSS animations whose'
+ + ' delay makes them start later');
+ return animations[0].ready.then(waitForFrame).then(function() {
+ assert_true(animations[0].startTime <= document.timeline.currentTime,
+ 'For CSS Animations in delay phase, the start time of the Animation is'
+ + ' not in the future');
+ });
+}, 'getAnimations for CSS animations in delay phase');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim1 0s 100s';
+ assert_equals(div.getAnimations().length, 1,
+ 'getAnimations returns animations for CSS animations whose'
+ + ' duration is zero');
+ div.remove();
+}, 'getAnimations for zero-duration CSS Animations');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim1 100s';
+ var originalAnimation = div.getAnimations()[0];
+
+ // Update pause state (an Animation change)
+ div.style.animationPlayState = 'paused';
+ var pendingAnimation = div.getAnimations()[0];
+ assert_equals(pendingAnimation.playState, 'pending',
+ 'animation\'s play state is updated');
+ assert_equals(originalAnimation, pendingAnimation,
+ 'getAnimations returns the same objects even when their'
+ + ' play state changes');
+
+ // Update duration (an Animation change)
+ div.style.animationDuration = '200s';
+ var extendedAnimation = div.getAnimations()[0];
+ // FIXME: Check extendedAnimation.effect.timing.duration has changed once the
+ // API is available
+ assert_equals(originalAnimation, extendedAnimation,
+ 'getAnimations returns the same objects even when their'
+ + ' duration changes');
+}, 'getAnimations returns objects with the same identity');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim1 100s';
+
+ assert_equals(div.getAnimations().length, 1,
+ 'getAnimations returns an animation before cancelling');
+
+ var animation = div.getAnimations()[0];
+
+ animation.cancel();
+ assert_equals(div.getAnimations().length, 0,
+ 'getAnimations does not return cancelled animations');
+
+ animation.play();
+ assert_equals(div.getAnimations().length, 1,
+ 'getAnimations returns cancelled animations that have been re-started');
+
+}, 'getAnimations for CSS Animations that are cancelled');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.animation = 'anim2 100s';
+
+ return div.getAnimations()[0].ready.then(function() {
+ // Prepend to the list and test that even though anim1 was triggered
+ // *after* anim2, it should come first because it appears first
+ // in the animation-name property.
+ div.style.animation = 'anim1 100s, anim2 100s';
+ var anims = div.getAnimations();
+ assert_equals(anims[0].animationName, 'anim1',
+ 'animation order after prepending to list');
+ assert_equals(anims[1].animationName, 'anim2',
+ 'animation order after prepending to list');
+
+ // Normally calling cancel and play would this push anim1 to the top of
+ // the stack but it shouldn't for CSS animations that map an the
+ // animation-name property.
+ var anim1 = anims[0];
+ anim1.cancel();
+ anim1.play();
+ anims = div.getAnimations();
+ assert_equals(anims[0].animationName, 'anim1',
+ 'animation order after cancelling and restarting');
+ assert_equals(anims[1].animationName, 'anim2',
+ 'animation order after cancelling and restarting');
+ });
+}, 'getAnimations for CSS Animations follows animation-name order');
+
+test(function(t) {
+ addStyle(t, { '#target::after': 'animation: anim1 10s;',
+ '#target::before': 'animation: anim1 10s;' });
+ var target = addDiv(t, { 'id': 'target' });
+ target.style.animation = 'anim1 100s';
+
+ var animations = target.getAnimations({ subtree: false });
+ assert_equals(animations.length, 1,
+ 'Should find only the element');
+ assert_equals(animations[0].effect.target, target,
+ 'Effect target should be the element');
+}, 'Test AnimationFilter{ subtree: false } with single element');
+
+test(function(t) {
+ addStyle(t, { '#target::after': 'animation: anim1 10s;',
+ '#target::before': 'animation: anim1 10s;' });
+ var target = addDiv(t, { 'id': 'target' });
+ target.style.animation = 'anim1 100s';
+
+ var animations = target.getAnimations({ subtree: true });
+ assert_equals(animations.length, 3,
+ 'getAnimations({ subtree: true }) ' +
+ 'should return animations on pseudo-elements');
+ assert_equals(animations[0].effect.target, target,
+ 'The animation targeting the parent element ' +
+ 'should be returned first');
+ assert_equals(animations[1].effect.target.type, '::before',
+ 'The animation targeting the ::before pseudo-element ' +
+ 'should be returned second');
+ assert_equals(animations[2].effect.target.type, '::after',
+ 'The animation targeting the ::after pesudo-element ' +
+ 'should be returned last');
+}, 'Test AnimationFilter{ subtree: true } with single element');
+
+test(function(t) {
+ addStyle(t, { '#parent::after': 'animation: anim1 10s;',
+ '#parent::before': 'animation: anim1 10s;',
+ '#child::after': 'animation: anim1 10s;',
+ '#child::before': 'animation: anim1 10s;' });
+ var parent = addDiv(t, { 'id': 'parent' });
+ parent.style.animation = 'anim1 100s';
+ var child = addDiv(t, { 'id': 'child' });
+ child.style.animation = 'anim1 100s';
+ parent.appendChild(child);
+
+ var animations = parent.getAnimations({ subtree: false });
+ assert_equals(animations.length, 1,
+ 'Should find only the element even if it has a child');
+ assert_equals(animations[0].effect.target, parent,
+ 'Effect target shuld be the element');
+}, 'Test AnimationFilter{ subtree: false } with element that has a child');
+
+test(function(t) {
+ addStyle(t, { '#parent::after': 'animation: anim1 10s;',
+ '#parent::before': 'animation: anim1 10s;',
+ '#child::after': 'animation: anim1 10s;',
+ '#child::before': 'animation: anim1 10s;' });
+ var parent = addDiv(t, { 'id': 'parent' });
+ var child = addDiv(t, { 'id': 'child' });
+ parent.style.animation = 'anim1 100s';
+ child.style.animation = 'anim1 100s';
+ parent.appendChild(child);
+
+ var animations = parent.getAnimations({ subtree: true });
+ assert_equals(animations.length, 6,
+ 'Should find all elements, pesudo-elements that parent has');
+
+ assert_equals(animations[0].effect.target, parent,
+ 'The animation targeting the parent element ' +
+ 'should be returned first');
+ assert_equals(animations[1].effect.target.type, '::before',
+ 'The animation targeting the ::before pseudo-element ' +
+ 'should be returned second');
+ assert_equals(animations[1].effect.target.parentElement, parent,
+ 'This ::before element should be child of parent element');
+ assert_equals(animations[2].effect.target.type, '::after',
+ 'The animation targeting the ::after pesudo-element ' +
+ 'should be returned third');
+ assert_equals(animations[2].effect.target.parentElement, parent,
+ 'This ::after element should be child of parent element');
+
+ assert_equals(animations[3].effect.target, child,
+ 'The animation targeting the child element ' +
+ 'should be returned fourth');
+ assert_equals(animations[4].effect.target.type, '::before',
+ 'The animation targeting the ::before pseudo-element ' +
+ 'should be returned fifth');
+ assert_equals(animations[4].effect.target.parentElement, child,
+ 'This ::before element should be child of child element');
+ assert_equals(animations[5].effect.target.type, '::after',
+ 'The animation targeting the ::after pesudo-element ' +
+ 'should be returned last');
+ assert_equals(animations[5].effect.target.parentElement, child,
+ 'This ::after element should be child of child element');
+}, 'Test AnimationFilter{ subtree: true } with element that has a child');
+
+test(function(t) {
+ var parent = addDiv(t, { 'id': 'parent' });
+ var child1 = addDiv(t, { 'id': 'child1' });
+ var grandchild1 = addDiv(t, { 'id': 'grandchild1' });
+ var grandchild2 = addDiv(t, { 'id': 'grandchild2' });
+ var child2 = addDiv(t, { 'id': 'child2' });
+
+ parent.style.animation = 'anim1 100s';
+ child1.style.animation = 'anim1 100s';
+ grandchild1.style.animation = 'anim1 100s';
+ grandchild2.style.animation = 'anim1 100s';
+ child2.style.animation = 'anim1 100s';
+
+ parent.appendChild(child1);
+ child1.appendChild(grandchild1);
+ child1.appendChild(grandchild2);
+ parent.appendChild(child2);
+
+ var animations = parent.getAnimations({ subtree: true });
+ assert_equals(
+ parent.getAnimations({ subtree: true }).length, 5,
+ 'Should find all descendants of the element');
+
+ assert_equals(animations[0].effect.target, parent,
+ 'The animation targeting the parent element ' +
+ 'should be returned first');
+
+ assert_equals(animations[1].effect.target, child1,
+ 'The animation targeting the child1 element ' +
+ 'should be returned second');
+
+ assert_equals(animations[2].effect.target, grandchild1,
+ 'The animation targeting the grandchild1 element ' +
+ 'should be returned third');
+
+ assert_equals(animations[3].effect.target, grandchild2,
+ 'The animation targeting the grandchild2 element ' +
+ 'should be returned fourth');
+
+ assert_equals(animations[4].effect.target, child2,
+ 'The animation targeting the child2 element ' +
+ 'should be returned last');
+
+}, 'Test AnimationFilter{ subtree: true } with element that has many descendant');
+
+done();
+</script>
+</body>