new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/file_event-order.html
@@ -0,0 +1,177 @@
+<!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';
+
+/**
+ * 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 elapsedTime
+ * of event and receive time of the most recent event.
+ */
+function AnimationEventHandler(target) {
+ this.target = target;
+ this.start = this.end = this.iteration = {};
+ ['start', 'iteration', 'end'].forEach(name => {
+ this.target['onanimation' + name] = function(evt) {
+ this[name].elapsedTime = evt.elapsedTime;
+ this[name].receiveTime = performance.now();
+ }.bind(this);
+ });
+}
+AnimationEventHandler.prototype.clear = function() {
+ this.start = { elapsedTime: undefined, receiveTime: undefined};
+ this.iteration = { elapsedTime: undefined, receiveTime: undefined};
+ this.end = { elapsedTime: undefined, receiveTime: 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, div, handler];
+}
+
+promise_test(function(t) {
+ const [animation1, watcher1, div1, handler1] =
+ setupAnimation(t, 'anim 5s 2 paused');
+ const [animation2, watcher2, div2, handler2] =
+ setupAnimation(t, 'anim 5s 2 paused');
+
+ return Promise.all([ watcher1.wait_for('animationstart'),
+ watcher2.wait_for('animationstart') ]).then(function() {
+ assert_true(handler1.start.receiveTime < handler2.start.receiveTime);
+ assert_equals(handler1.start.elapsedTime, 0);
+ assert_equals(handler2.start.elapsedTime, 0);
+
+ handler1.clear();
+ handler2.clear();
+
+ // Seek to first iteration
+ animation1.currentTime = 5 * MS_PER_SEC;
+ animation2.currentTime = 5 * MS_PER_SEC;
+ return Promise.all([ watcher1.wait_for('animationiteration'),
+ watcher2.wait_for('animationiteration') ]);
+
+ }).then(function() {
+ assert_true(handler1.iteration.receiveTime < handler2.iteration.receiveTime);
+ assert_equals(handler1.iteration.elapsedTime, 5);
+ assert_equals(handler2.iteration.elapsedTime, 5);
+
+ handler1.clear();
+ handler2.clear();
+
+ // Make Idle
+ animation1.finish();
+ animation2.finish();
+
+ return Promise.all([ watcher1.wait_for('animationend'),
+ watcher2.wait_for('animationend') ]);
+ }).then(function() {
+ assert_true(handler1.end.receiveTime < handler2.end.receiveTime);
+ assert_equals(handler1.end.elapsedTime, 10);
+ assert_equals(handler2.end.elapsedTime, 10);
+ });
+}, 'Fire same events on different element');
+
+promise_test(function(t) {
+ const [animation1, watcher1, div1, handler1] =
+ setupAnimation(t, 'anim 5s 6s');
+ const [animation2, watcher2, div2, handler2] =
+ setupAnimation(t, 'anim 5s 2');
+
+ return watcher2.wait_for('animationstart').then(function(evt) {
+ // Seek to start of animation1
+ animation1.currentTime = 6 * MS_PER_SEC;
+ animation2.currentTime = 6 * MS_PER_SEC;
+
+ handler1.clear();
+ handler2.clear();
+
+ return Promise.all([ watcher1.wait_for('animationstart'),
+ watcher2.wait_for('animationiteration') ]);
+ }).then(function() {
+ assert_true(handler2.iteration.receiveTime < handler1.start.receiveTime);
+ assert_equals(handler1.start.elapsedTime, 0);
+ assert_equals(handler2.iteration.elapsedTime, 5);
+ });
+}, 'Fire the start and iteration event');
+
+promise_test(function(t) {
+ const [animation1, watcher1, div1, handler1] =
+ setupAnimation(t, 'anim 5s 4s');
+ const [animation2, watcher2, div2, handler2] =
+ setupAnimation(t, 'anim 5s 2');
+
+ // Seek to start position.
+ animation1.currentTime = 4 * MS_PER_SEC;
+ animation2.currentTime = 4 * MS_PER_SEC;
+
+ return Promise.all([ watcher1.wait_for('animationstart'),
+ watcher2.wait_for('animationstart') ]).then(function() {
+ assert_true(handler2.start.receiveTime < handler1.start.receiveTime);
+ // Seek to end of animation1
+ animation1.currentTime = 9 * MS_PER_SEC;
+ animation2.currentTime = 9 * MS_PER_SEC;
+
+ return Promise.all([ watcher1.wait_for('animationend'),
+ watcher2.wait_for('animationiteration') ]);
+ }).then(function() {
+ assert_true(handler2.end.receiveTime < handler1.iteration.receiveTime);
+ assert_equals(handler1.end.elapsedTime, 5);
+ assert_equals(handler2.iteration.elapsedTime, 5);
+ });
+}, 'Fire the iteration and end event');
+
+promise_test(function(t) {
+ const [animation1, watcher1, div1, handler1] =
+ setupAnimation(t, 'anim 5s 4s');
+ const [animation2, watcher2, div2, handler2] =
+ setupAnimation(t, 'anim 5s 2');
+
+ handler1.clear();
+ handler2.clear();
+
+ // Make after phase.
+ animation1.finish();
+ animation2.finish();
+
+ return Promise.all([ watcher1.wait_for([ 'animationstart',
+ 'animationend' ]),
+ watcher2.wait_for([ 'animationstart',
+ 'animationend' ]) ]).then(function() {
+ // First test order of event
+ var expectOrder = [handler1.start.receiveTime,
+ handler1.end.receiveTime,
+ handler2.start.receiveTime,
+ handler2.end.receiveTime]
+ var sortedOrder = expectOrder.sort();
+ expectOrder.forEach((time, i) => {
+ assert_equals(time, sortedOrder[i]);
+ });
+
+ assert_equals(handler1.start.elapsedTime, 0);
+ assert_equals(handler1.end.elapsedTime, 5);
+ assert_equals(handler2.start.elapsedTime, 0);
+ assert_equals(handler2.end.elapsedTime, 10);
+ });
+}, 'Fire the start and end event');
+
+done();
+</script>
+</body>
+</html>