new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/file_events-order.html
@@ -0,0 +1,201 @@
+<!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';
+
+/**
+ * This is EventWatcher for two element.
+ * It can determine the order of tow element's event.
+ */
+function EventWatcherWithTwoElement(test, target1, target2, eventTypes)
+{
+ var waitingFor = null;
+ target1.resultEvents = [];
+ target2.resultEvents = [];
+
+ var eventHandler = test.step_func(function(evt) {
+ assert_true(!!waitingFor,
+ 'Not expecting event, but got ' + evt.type + ' event');
+ assert_equals(evt.type, waitingFor.types[0][1],
+ 'Expected ' + waitingFor.types[0][1] + ' event, but got ' +
+ evt.type + ' event instead');
+ assert_equals(evt.target, waitingFor.types[0][0],
+ 'Expected ' + waitingFor.types[0][0] + ' target of event,' +
+ 'but got ' + evt.type + ' event instead');
+ evt.target.resultEvents.push(evt);
+ if (waitingFor.types.length > 1) {
+ // Pop first event from array
+ waitingFor.types.shift();
+ return;
+ }
+
+ // We need to null out waitingFor before calling the resolve function
+ // since the Promise's resolve handlers may call wait_for() which will
+ // need to set waitingFor.
+ var resolveFunc = waitingFor.resolve;
+ waitingFor = null;
+ resolveFunc();
+ });
+
+ for (var i = 0; i < eventTypes.length; i++) {
+ target1.addEventListener(eventTypes[i], eventHandler);
+ target2.addEventListener(eventTypes[i], eventHandler);
+ }
+
+ this.wait_for = function(types) {
+ if (waitingFor) {
+ return Promise.reject('Already waiting for an event or events');
+ }
+
+ target1.resultEvents = [];
+ target2.resultEvents = [];
+
+ return new Promise(function(resolve, reject) {
+ waitingFor = {
+ types: types,
+ resolve: resolve,
+ reject: reject
+ };
+ });
+ };
+
+ function stop_watching() {
+ for (var i = 0; i < eventTypes.length; i++) {
+ target1.removeEventListener(eventTypes[i], eventHandler);
+ target2.removeEventListener(eventTypes[i], eventHandler);
+ }
+ };
+
+ test.add_cleanup(stop_watching);
+ return this;
+}
+
+function setupAnimationWithTwoElement(t, animationStyle1, animationStyle2) {
+ var div1 = addDiv(t, { style: "animation: " + animationStyle1 });
+ var div2 = addDiv(t, { style: "animation: " + animationStyle2 });
+ var watcher = new EventWatcherWithTwoElement(t, div1, div2,
+ [ 'animationstart',
+ 'animationiteration',
+ 'animationend' ]);
+ var animation1 = div1.getAnimations()[0];
+ var animation2 = div2.getAnimations()[0];
+
+ return [animation1, animation2, watcher, div1, div2];
+}
+
+promise_test(function(t) {
+ var [animation1, animation2, watcher, div1, div2] =
+ setupAnimationWithTwoElement(t, 'anim 5s 2 paused', 'anim 5s 2 paused');
+
+ return watcher.wait_for([ [ div1, 'animationstart' ],
+ [ div2, 'animationstart' ] ]).then(function() {
+ assert_equals(div1.resultEvents.length, 1);
+ assert_equals(div2.resultEvents.length, 1);
+ assert_equals(div1.resultEvents[0].elapsedTime, 0);
+ assert_equals(div2.resultEvents[0].elapsedTime, 0);
+
+ // Seek to first iteration
+ animation1.currentTime = 5 * MS_PER_SEC;
+ animation2.currentTime = 5 * MS_PER_SEC;
+ return watcher.wait_for([ [ div1, 'animationiteration' ],
+ [ div2, 'animationiteration' ] ]);
+ }).then(function() {
+ assert_equals(div1.resultEvents.length, 1);
+ assert_equals(div2.resultEvents.length, 1);
+ assert_equals(div1.resultEvents[0].elapsedTime, 5);
+ assert_equals(div2.resultEvents[0].elapsedTime, 5);
+
+ // Seek to end
+ animation1.finish();
+ animation2.finish();
+
+ return watcher.wait_for([ [ div1, 'animationend' ],
+ [ div2, 'animationend' ] ]);
+ }).then(function() {
+ assert_equals(div1.resultEvents.length, 1);
+ assert_equals(div2.resultEvents.length, 1);
+ assert_equals(div1.resultEvents[0].elapsedTime, 10);
+ assert_equals(div2.resultEvents[0].elapsedTime, 10);
+ });
+}, 'Fire same events on different element');
+
+promise_test(function(t) {
+ var [animation1, animation2, watcher, div1, div2] =
+ setupAnimationWithTwoElement(t, 'anim 5s 6s', 'anim 5s 2');
+
+ return watcher.wait_for([ [ div2, 'animationstart'] ]).then(function() {
+ // Seek to start of animation1
+ animation1.currentTime = 6 * MS_PER_SEC;
+ animation2.currentTime = 6 * MS_PER_SEC;
+
+ return watcher.wait_for([ [ div2, 'animationiteration' ],
+ [ div1, 'animationstart' ] ]);
+ }).then(function() {
+ assert_equals(div1.resultEvents.length, 1);
+ assert_equals(div2.resultEvents.length, 1);
+ assert_equals(div1.resultEvents[0].elapsedTime, 0);
+ assert_equals(div2.resultEvents[0].elapsedTime, 5);
+ });
+}, 'Fire the start and iteration event');
+
+promise_test(function(t) {
+ var [animation1, animation2, watcher, div1, div2] =
+ setupAnimationWithTwoElement(t, 'anim 5s 4s', 'anim 5s 2');
+
+ // Seek to start position.
+ animation1.currentTime = 4 * MS_PER_SEC;
+ animation2.currentTime = 4 * MS_PER_SEC;
+
+ return watcher.wait_for([ [ div2, 'animationstart' ],
+ [ div1, 'animationstart' ] ]).then(function() {
+ // Seek to end of animation1
+ animation1.currentTime = 9 * MS_PER_SEC;
+ animation2.currentTime = 9 * MS_PER_SEC;
+
+ return watcher.wait_for([ [ div2, 'animationiteration' ],
+ [ div1, 'animationend' ] ]);
+ }).then(function() {
+ assert_equals(div1.resultEvents.length, 1);
+ assert_equals(div2.resultEvents.length, 1);
+ assert_equals(div1.resultEvents[0].elapsedTime, 5);
+ assert_equals(div2.resultEvents[0].elapsedTime, 5);
+ });
+}, 'Fire the iteration and end event');
+
+promise_test(function(t) {
+ var [animation1, animation2, watcher, div1, div2] =
+ setupAnimationWithTwoElement(t,
+ 'anim 5s 4s',
+ 'anim 5s 2');
+
+ // Make after phase.
+ animation1.finish();
+ animation2.finish();
+
+ return watcher.wait_for([ [ div2, 'animationstart' ],
+ [ div1, 'animationstart' ],
+ [ div1, 'animationend' ],
+ [div2, 'animationend' ] ]).then(function() {
+ assert_equals(div1.resultEvents.length, 2);
+ assert_equals(div2.resultEvents.length, 2);
+ assert_equals(div1.resultEvents[0].elapsedTime, 0);
+ assert_equals(div1.resultEvents[1].elapsedTime, 5);
+ assert_equals(div2.resultEvents[0].elapsedTime, 0);
+ assert_equals(div2.resultEvents[1].elapsedTime, 10);
+ });
+}, 'Fire the start and end event');
+
+done();
+</script>
+</body>
+</html>