diff options
author | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-03-14 11:55:23 +0100 |
---|---|---|
committer | janekptacijarabaci <janekptacijarabaci@seznam.cz> | 2018-03-14 11:55:23 +0100 |
commit | c58cec26c73da9a5c48f7d75555c6a2409965693 (patch) | |
tree | 318932a7d433433d94ad2ac0f7c46a1ae66a17fe /dom | |
parent | aade91b13a50ee4f246016fa8d8d1561f58f80ee (diff) | |
download | uxp-c58cec26c73da9a5c48f7d75555c6a2409965693.tar.gz |
Bug 1202333: AnimationEvent elapsedTime should reflect playbackRate (added tests)
Issue #55
Diffstat (limited to 'dom')
-rw-r--r-- | dom/animation/test/css-animations/file_event-dispatch.html | 252 | ||||
-rw-r--r-- | dom/animation/test/css-animations/file_event-order.html | 160 | ||||
-rw-r--r-- | dom/animation/test/css-animations/test_event-dispatch.html | 15 | ||||
-rw-r--r-- | dom/animation/test/css-animations/test_event-order.html | 15 | ||||
-rw-r--r-- | dom/animation/test/mochitest.ini | 4 | ||||
-rw-r--r-- | dom/events/test/test_legacy_event.html | 21 |
6 files changed, 453 insertions, 14 deletions
diff --git a/dom/animation/test/css-animations/file_event-dispatch.html b/dom/animation/test/css-animations/file_event-dispatch.html new file mode 100644 index 0000000000..266205bc32 --- /dev/null +++ b/dom/animation/test/css-animations/file_event-dispatch.html @@ -0,0 +1,252 @@ +<!doctype html> +<meta charset=utf-8> +<title>Tests for CSS animation event dispatch</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/> +<script src="../testcommon.js"></script> +<style> + @keyframes anim { + from { margin-left: 0px; } + to { margin-left: 100px; } + } +</style> +<body> +<script> +'use strict'; + +/** + * Helper class to record the elapsedTime member of each event. + * The EventWatcher class in testharness.js allows us to wait on + * multiple events in a certain order but only records the event + * parameters of the most recent event. + */ +function AnimationEventHandler(target) { + this.target = target; + this.target.onanimationstart = function(evt) { + this.animationstart = evt.elapsedTime; + }.bind(this); + this.target.onanimationiteration = function(evt) { + this.animationiteration = evt.elapsedTime; + }.bind(this); + this.target.onanimationend = function(evt) { + this.animationend = evt.elapsedTime; + }.bind(this); +} +AnimationEventHandler.prototype.clear = function() { + this.animationstart = undefined; + this.animationiteration = undefined; + this.animationend = undefined; +} + +function setupAnimation(t, animationStyle) { + var div = addDiv(t, { style: "animation: " + animationStyle }); + var watcher = new EventWatcher(t, div, [ 'animationstart', + 'animationiteration', + 'animationend' ]); + var handler = new AnimationEventHandler(div); + var animation = div.getAnimations()[0]; + + return [animation, watcher, handler, div]; +} + +promise_test(function(t) { + // Add 1ms delay to ensure that the delay is not included in the elapsedTime. + const [animation, watcher] = setupAnimation(t, 'anim 100s 1ms'); + + return watcher.wait_for('animationstart').then(function(evt) { + assert_equals(evt.elapsedTime, 0.0); + }); +}, 'Idle -> Active'); + +promise_test(function(t) { + const [animation, watcher, handler] = setupAnimation(t, 'anim 100s'); + + // Seek to After phase. + animation.finish(); + return watcher.wait_for([ 'animationstart', + 'animationend' ]).then(function() { + assert_equals(handler.animationstart, 0.0); + assert_equals(handler.animationend, 100); + }); +}, 'Idle -> After'); + +promise_test(function(t) { + const [animation, watcher, handler] = + setupAnimation(t, 'anim 100s 100s paused'); + + return animation.ready.then(function() { + // Seek to Active phase. + animation.currentTime = 100 * MS_PER_SEC; + return watcher.wait_for('animationstart'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 0.0); + }); +}, 'Before -> Active'); + +promise_test(function(t) { + const [animation, watcher, handler] = + setupAnimation(t, 'anim 100s 100s paused'); + + return animation.ready.then(function() { + // Seek to After phase. + animation.finish(); + return watcher.wait_for([ 'animationstart', 'animationend' ]); + }).then(function(evt) { + assert_equals(handler.animationstart, 0.0); + assert_equals(handler.animationend, 100.0); + }); +}, 'Before -> After'); + +promise_test(function(t) { + const [animation, watcher, handler] = + setupAnimation(t, 'anim 100s 100s paused'); + + // Seek to Active phase. + animation.currentTime = 100 * MS_PER_SEC; + return watcher.wait_for('animationstart').then(function() { + // Seek to Before phase. + animation.currentTime = 0; + return watcher.wait_for('animationend'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 0.0); + }); +}, 'Active -> Before'); + +promise_test(function(t) { + const [animation, watcher, handler] = setupAnimation(t, 'anim 100s paused'); + + return watcher.wait_for('animationstart').then(function(evt) { + // Seek to After phase. + animation.finish(); + return watcher.wait_for('animationend'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 100.0); + }); +}, 'Active -> After'); + +promise_test(function(t) { + const [animation, watcher, handler] = + setupAnimation(t, 'anim 100s 100s paused'); + + // Seek to After phase. + animation.finish(); + return watcher.wait_for([ 'animationstart', + 'animationend' ]).then(function() { + // Seek to Before phase. + animation.currentTime = 0; + handler.clear(); + return watcher.wait_for([ 'animationstart', 'animationend' ]); + }).then(function() { + assert_equals(handler.animationstart, 100.0); + assert_equals(handler.animationend, 0.0); + }); +}, 'After -> Before'); + +promise_test(function(t) { + const [animation, watcher, handler] = + setupAnimation(t, 'anim 100s 100s paused'); + + // Seek to After phase. + animation.finish(); + return watcher.wait_for([ 'animationstart', + 'animationend' ]).then(function() { + // Seek to Active phase. + animation.currentTime = 100 * MS_PER_SEC; + handler.clear(); + return watcher.wait_for('animationstart'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 100.0); + }); +}, 'After -> Active'); + +promise_test(function(t) { + const [animation, watcher, handler] + = setupAnimation(t, 'anim 100s 100s 3 paused'); + + return animation.ready.then(function() { + // Seek to iteration 0 (no animationiteration event should be dispatched) + animation.currentTime = 100 * MS_PER_SEC; + return watcher.wait_for('animationstart'); + }).then(function(evt) { + // Seek to iteration 2 + animation.currentTime = 300 * MS_PER_SEC; + handler.clear(); + return watcher.wait_for('animationiteration'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 200); + // Seek to After phase (no animationiteration event should be dispatched) + animation.currentTime = 400 * MS_PER_SEC; + return watcher.wait_for('animationend'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 300); + }); +}, 'Active -> Active (forwards)'); + +promise_test(function(t) { + const [animation, watcher, handler] = setupAnimation(t, 'anim 100s 100s 3'); + + // Seek to After phase. + animation.finish(); + return watcher.wait_for([ 'animationstart', + 'animationend' ]).then(function() { + // Seek to iteration 2 (no animationiteration event should be dispatched) + animation.pause(); + animation.currentTime = 300 * MS_PER_SEC; + return watcher.wait_for('animationstart'); + }).then(function() { + // Seek to mid of iteration 0 phase. + animation.currentTime = 200 * MS_PER_SEC; + return watcher.wait_for('animationiteration'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 200.0); + // Seek to before phase (no animationiteration event should be dispatched) + animation.currentTime = 0; + return watcher.wait_for('animationend'); + }); +}, 'Active -> Active (backwards)'); + +promise_test(function(t) { + const [animation, watcher, handler, div] = + setupAnimation(t, 'anim 100s paused'); + return watcher.wait_for('animationstart').then(function(evt) { + // Seek to Idle phase. + div.style.display = 'none'; + flushComputedStyle(div); + + // FIXME: bug 1302648: Add test for animationcancel event here. + + // Restart this animation. + div.style.display = ''; + return watcher.wait_for('animationstart'); + }); +}, 'Active -> Idle -> Active: animationstart is fired by restarting animation'); + +promise_test(function(t) { + const [animation, watcher, handler, div] = + setupAnimation(t, 'anim 100s 100s 2 paused'); + + // Make After. + animation.finish(); + return watcher.wait_for([ 'animationstart', + 'animationend' ]).then(function(evt) { + animation.playbackRate = -1; + return watcher.wait_for('animationstart'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 200); + // Seek to 1st iteration + animation.currentTime = 200 * MS_PER_SEC - 1; + return watcher.wait_for('animationiteration'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 100); + // Seek to before + animation.currentTime = 100 * MS_PER_SEC - 1; + return watcher.wait_for('animationend'); + }).then(function(evt) { + assert_equals(evt.elapsedTime, 0); + assert_equals(animation.playState, 'running'); // delay + }); +}, 'Negative playbackRate sanity test(Before -> Active -> Before)'); + +done(); +</script> +</body> +</html> diff --git a/dom/animation/test/css-animations/file_event-order.html b/dom/animation/test/css-animations/file_event-order.html new file mode 100644 index 0000000000..da78b6541c --- /dev/null +++ b/dom/animation/test/css-animations/file_event-order.html @@ -0,0 +1,160 @@ +<!doctype html> +<meta charset=utf-8> +<title>Tests for CSS animation event order</title> +<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/> +<script src="../testcommon.js"></script> +<style> + @keyframes anim { + from { margin-left: 0px; } + to { margin-left: 100px; } + } +</style> +<body> +<script type='text/javascript'> +'use strict'; + +/** + * Asserts that the set of actual and received events match. + * @param actualEvents An array of the received AnimationEvent objects. + * @param expectedEvents A series of array objects representing the expected + * events, each having the form: + * [ event type, target element, elapsed time ] + */ +function checkEvents(actualEvents, ...expectedEvents) { + assert_equals(actualEvents.length, expectedEvents.length, + `Number of actual events (${actualEvents.length}: \ +${actualEvents.map(event => event.type).join(', ')}) should match expected \ +events (${expectedEvents.map(event => event.type).join(', ')})`); + + actualEvents.forEach((actualEvent, i) => { + assert_equals(expectedEvents[i][0], actualEvent.type, + 'Event type should match'); + assert_equals(expectedEvents[i][1], actualEvent.target, + 'Event target should match'); + assert_equals(expectedEvents[i][2], actualEvent.elapsedTime, + 'Event\'s elapsed time should match'); + }); +} + +function setupAnimation(t, animationStyle, receiveEvents) { + const div = addDiv(t, { style: "animation: " + animationStyle }); + const watcher = new EventWatcher(t, div, [ 'animationstart', + 'animationiteration', + 'animationend' ]); + + ['start', 'iteration', 'end'].forEach(name => { + div['onanimation' + name] = function(evt) { + receiveEvents.push({ type: evt.type, + target: evt.target, + elapsedTime: evt.elapsedTime }); + }.bind(this); + }); + + const animation = div.getAnimations()[0]; + + return [animation, watcher, div]; +} + +promise_test(function(t) { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 100s 2 paused', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 100s 2 paused', events); + + return Promise.all([ watcher1.wait_for('animationstart'), + watcher2.wait_for('animationstart') ]).then(function() { + checkEvents(events, ['animationstart', div1, 0], + ['animationstart', div2, 0]); + + events.length = 0; // Clear received event array + + animation1.currentTime = 100 * MS_PER_SEC; + animation2.currentTime = 100 * MS_PER_SEC; + return Promise.all([ watcher1.wait_for('animationiteration'), + watcher2.wait_for('animationiteration') ]); + }).then(function() { + checkEvents(events, ['animationiteration', div1, 100], + ['animationiteration', div2, 100]); + + events.length = 0; // Clear received event array + + animation1.finish(); + animation2.finish(); + + return Promise.all([ watcher1.wait_for('animationend'), + watcher2.wait_for('animationend') ]); + }).then(function() { + checkEvents(events, ['animationend', div1, 200], + ['animationend', div2, 200]); + }); +}, 'Test same events are ordered by elements.'); + +promise_test(function(t) { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 200s 400s', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 300s 2', events); + + return watcher2.wait_for('animationstart').then(function(evt) { + animation1.currentTime = 400 * MS_PER_SEC; + animation2.currentTime = 400 * MS_PER_SEC; + + events.length = 0; // Clear received event array + + return Promise.all([ watcher1.wait_for('animationstart'), + watcher2.wait_for('animationiteration') ]); + }).then(function() { + checkEvents(events, ['animationiteration', div2, 300], + ['animationstart', div1, 0]); + }); +}, 'Test start and iteration events are ordered by time.'); + +promise_test(function(t) { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 150s', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 100s 2', events); + + return Promise.all([ watcher1.wait_for('animationstart'), + watcher2.wait_for('animationstart') ]).then(function() { + animation1.currentTime = 150 * MS_PER_SEC; + animation2.currentTime = 150 * MS_PER_SEC; + + events.length = 0; // Clear received event array + + return Promise.all([ watcher1.wait_for('animationend'), + watcher2.wait_for('animationiteration') ]); + }).then(function() { + checkEvents(events, ['animationiteration', div2, 100], + ['animationend', div1, 150]); + }); +}, 'Test iteration and end events are ordered by time.'); + +promise_test(function(t) { + let events = []; + const [animation1, watcher1, div1] = + setupAnimation(t, 'anim 100s 100s', events); + const [animation2, watcher2, div2] = + setupAnimation(t, 'anim 100s 2', events); + + animation1.finish(); + animation2.finish(); + + return Promise.all([ watcher1.wait_for([ 'animationstart', + 'animationend' ]), + watcher2.wait_for([ 'animationstart', + 'animationend' ]) ]).then(function() { + checkEvents(events, ['animationstart', div2, 0], + ['animationstart', div1, 0], + ['animationend', div1, 100], + ['animationend', div2, 200]); + }); +}, 'Test start and end events are sorted correctly when fired simultaneously'); + +done(); +</script> +</body> +</html> diff --git a/dom/animation/test/css-animations/test_event-dispatch.html b/dom/animation/test/css-animations/test_event-dispatch.html new file mode 100644 index 0000000000..de3be03017 --- /dev/null +++ b/dom/animation/test/css-animations/test_event-dispatch.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +'use strict'; +setup({explicit_done: true}); +SpecialPowers.pushPrefEnv( + { "set": [["dom.animations-api.core.enabled", true]]}, + function() { + window.open("file_event-dispatch.html"); + }); +</script> +</html> diff --git a/dom/animation/test/css-animations/test_event-order.html b/dom/animation/test/css-animations/test_event-order.html new file mode 100644 index 0000000000..57f1f3876b --- /dev/null +++ b/dom/animation/test/css-animations/test_event-order.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +'use strict'; +setup({explicit_done: true}); +SpecialPowers.pushPrefEnv( + { "set": [["dom.animations-api.core.enabled", true]]}, + function() { + window.open("file_event-order.html"); + }); +</script> +</html> diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index c694e4d25a..07e04d4d61 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -19,6 +19,8 @@ support-files = css-animations/file_document-get-animations.html css-animations/file_effect-target.html css-animations/file_element-get-animations.html + css-animations/file_event-dispatch.html + css-animations/file_event-order.html css-animations/file_keyframeeffect-getkeyframes.html css-animations/file_pseudoElement-get-animations.html css-transitions/file_animation-cancel.html @@ -73,6 +75,8 @@ support-files = [css-animations/test_document-get-animations.html] [css-animations/test_effect-target.html] [css-animations/test_element-get-animations.html] +[css-animations/test_event-dispatch.html] +[css-animations/test_event-order.html] [css-animations/test_keyframeeffect-getkeyframes.html] [css-animations/test_pseudoElement-get-animations.html] [css-transitions/test_animation-cancel.html] diff --git a/dom/events/test/test_legacy_event.html b/dom/events/test/test_legacy_event.html index d772be1066..b2105a6dfc 100644 --- a/dom/events/test/test_legacy_event.html +++ b/dom/events/test/test_legacy_event.html @@ -73,22 +73,15 @@ function triggerShortAnimation(node) { node.style.animation = "anim1 1ms linear"; } -// This function triggers a long animation with two iterations, which is -// *nearly* at the end of its first iteration. It will hit the end of that -// iteration (firing an event) almost immediately, 1ms in the future. +// This function triggers a very short (10ms long) animation with many +// iterations, which will cause a start event followed by an iteration event +// on each subsequent tick, to fire. // -// NOTE: It's important that this animation have a *long* duration. If it were -// short (e.g. 1ms duration), then we might jump past all its iterations in -// a single refresh-driver tick. And if that were to happens, we'd *never* fire -// any animationiteration events -- the CSS Animations spec says this event -// must not be fired "...when an animationend event would fire at the same time" -// (which would be the case in this example with a 1ms duration). So, to make -// sure our event does fire, we use a long duration and a nearly-as-long -// negative delay. This ensures we hit the end of the first iteration right -// away, and that we don't risk hitting the end of the second iteration at the -// same time. +// NOTE: We need the many iterations since if an animation frame coincides +// with the animation starting or ending we dispatch only the start or end +// event and not the iteration event. function triggerAnimationIteration(node) { - node.style.animation = "anim1 300s -299.999s linear 2"; + node.style.animation = "anim1 10ms linear 20000"; } // GENERAL UTILITY FUNCTIONS |