4 https://bugzilla.mozilla.org/show_bug.cgi?id=1183461
7 This test is similar to those in test_animations.html with the exception
8 that those tests interact with a single element at a time. The tests in this
9 file are specifically concerned with testing the ordering of events between
10 elements for which most of the utilities in animation_utils.js are not
15 <title>Test for CSS Animation and Transition event ordering
17 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
18 <!-- We still need animation_utils.js for advance_clock -->
19 <script type=
"application/javascript" src=
"animation_utils.js"></script>
20 <link rel=
"stylesheet" type=
"text/css" href=
"/tests/SimpleTest/test.css"/>
22 @keyframes anim { to { margin-left:
100px } }
23 @keyframes animA { to { margin-left:
100px } }
24 @keyframes animB { to { margin-left:
100px } }
25 @keyframes animC { to { margin-left:
100px } }
30 href=
"https://bugzilla.mozilla.org/show_bug.cgi?id=1183461">Mozilla Bug
32 <div id=
"display"></div>
34 <script type=
"application/javascript">
37 /* eslint-disable no-shadow */
39 // Take over the refresh driver right from the start.
42 // Common test scaffolding
44 var gEventsReceived = [];
45 var gDisplay = document.getElementById('display');
56 gDisplay.addEventListener(event,
57 event =
> gEventsReceived.push(event)));
59 function checkEventOrder(...args) {
61 // Arguments = ExpectedEvent*, desc
63 // [ target|animationName|transitionProperty, (pseudo,) message ]
64 var expectedEvents = args.slice(
0, -
1);
65 var desc = args[args.length -
1];
66 var isTestingNameOrProperty = expectedEvents.length &&
67 typeof expectedEvents[
0][
0] == 'string';
69 var formatEvent = (target, nameOrProperty, pseudo, message) =
>
70 isTestingNameOrProperty ?
71 `${nameOrProperty}${pseudo}:${message}` :
72 `${target.id}${pseudo}:${message}`;
74 var actual = gEventsReceived.map(
75 event =
> formatEvent(event.target,
76 event.animationName || event.propertyName,
77 event.pseudoElement, event.type)
79 var expected = expectedEvents.map(
80 event =
> event.length ==
3 ?
81 formatEvent(event[
0], event[
0], event[
1], event[
2]) :
82 formatEvent(event[
0], event[
0], '', event[
1])
84 is(actual, expected, desc);
88 //
1. TESTS FOR SORTING BY TREE ORDER
90 //
1a. Test that simultaneous events are sorted by tree order (siblings)
92 var divs = [ document.createElement('div'),
93 document.createElement('div'),
94 document.createElement('div') ];
95 divs.forEach((div, i) =
> {
96 gDisplay.appendChild(div);
97 div.setAttribute('style', 'animation: anim
10s');
98 div.setAttribute('id', 'div' + i);
99 getComputedStyle(div).animationName; // trigger building of animation
103 checkEventOrder([ divs[
0], 'animationstart' ],
104 [ divs[
1], 'animationstart' ],
105 [ divs[
2], 'animationstart' ],
106 'Simultaneous start on siblings');
108 divs.forEach(div =
> div.remove());
111 //
1b. Test that simultaneous events are sorted by tree order (children)
113 divs = [ document.createElement('div'),
114 document.createElement('div'),
115 document.createElement('div') ];
117 // Create the following arrangement:
125 gDisplay.appendChild(divs[
0]);
126 gDisplay.appendChild(divs[
1]);
127 divs[
0].appendChild(divs[
2]);
129 divs.forEach((div, i) =
> {
130 div.setAttribute('style', 'animation: anim
10s');
131 div.setAttribute('id', 'div' + i);
132 getComputedStyle(div).animationName; // trigger building of animation
136 checkEventOrder([ divs[
0], 'animationstart' ],
137 [ divs[
2], 'animationstart' ],
138 [ divs[
1], 'animationstart' ],
139 'Simultaneous start on children');
141 divs.forEach(div =
> div.remove());
144 //
1c. Test that simultaneous events are sorted by tree order (pseudos)
146 divs = [ document.createElement('div'),
147 document.createElement('div') ];
149 // Create the following arrangement:
159 gDisplay.appendChild(divs[
0]);
160 divs[
0].appendChild(divs[
1]);
162 divs.forEach((div, i) =
> {
163 div.setAttribute('style', 'animation: anim
10s');
164 div.setAttribute('id', 'div' + i);
167 var extraStyle = document.createElement('style');
168 document.head.appendChild(extraStyle);
169 var sheet = extraStyle.sheet;
170 sheet.insertRule('#div0::after { animation: anim
10s }',
0);
171 sheet.insertRule('#div0::before { animation: anim
10s }',
1);
172 sheet.insertRule('#div0::after, #div0::before { ' +
173 ' content:
" " }',
2);
174 getComputedStyle(divs[
0]).animationName; // build animation
175 getComputedStyle(divs[
1]).animationName; // build animation
178 checkEventOrder([ divs[
0], 'animationstart' ],
179 [ divs[
0], '::before', 'animationstart' ],
180 [ divs[
0], '::after', 'animationstart' ],
181 [ divs[
1], 'animationstart' ],
182 'Simultaneous start on pseudo-elements');
184 divs.forEach(div =
> div.remove());
189 extraStyle = undefined;
191 //
2. TESTS FOR SORTING BY TIME
193 //
2a. Test that events are sorted by time
195 divs = [ document.createElement('div'),
196 document.createElement('div') ];
197 divs.forEach((div, i) =
> {
198 gDisplay.appendChild(div);
199 div.setAttribute('style', 'animation: anim
10s');
200 div.setAttribute('id', 'div' + i);
203 divs[
0].style.animationDelay = '
5s';
206 advance_clock(
20000);
208 checkEventOrder([ divs[
1], 'animationstart' ],
209 [ divs[
0], 'animationstart' ],
210 [ divs[
1], 'animationend' ],
211 [ divs[
0], 'animationend' ],
212 'Sorting of start and end events by time');
214 divs.forEach(div =
> div.remove());
217 //
2b. Test different events within the one element
219 var div = document.createElement('div');
220 gDisplay.appendChild(div);
221 div.style.animation = 'anim
4s
2, ' + // Repeat at t=
4s
222 'anim
10s
5s, ' + // Start at t=
5s
223 'anim
3s'; // End at t=
3s
224 div.setAttribute('id', 'div');
225 getComputedStyle(div).animationName; // build animation
230 checkEventOrder([ div, 'animationstart' ],
231 [ div, 'animationstart' ],
232 [ div, 'animationend' ],
233 [ div, 'animationiteration' ],
234 [ div, 'animationstart' ],
235 'Sorting of different events by time within an element');
240 //
2c. Test negative delay is sorted equal to zero delay but before
243 divs = [ document.createElement('div'),
244 document.createElement('div'),
245 document.createElement('div') ];
246 divs.forEach((div, i) =
> {
247 gDisplay.appendChild(div);
248 div.setAttribute('id', 'div' + i);
251 divs[
0].style.animation = 'anim
20s
5s'; // Positive delay, sorts last
252 divs[
1].style.animation = 'anim
20s'; //
0s delay
253 divs[
2].style.animation = 'anim
20s -
5s'; // Negative delay, sorts same as
254 //
0s delay, i.e. use document
259 checkEventOrder([ divs[
1], 'animationstart' ],
260 [ divs[
2], 'animationstart' ],
261 [ divs[
0], 'animationstart' ],
262 'Sorting of events including negative delay');
264 divs.forEach(div =
> div.remove());
267 //
3. TESTS FOR SORTING BY animation-name POSITION
269 //
3a. Test animation-name position
271 div = document.createElement('div');
272 gDisplay.appendChild(div);
273 div.style.animation = 'animA
10s, animB
5s, animC
5s
2';
274 div.setAttribute('id', 'div');
275 getComputedStyle(div).animationName; // build animation
279 checkEventOrder([ 'animA', 'animationstart' ],
280 [ 'animB', 'animationstart' ],
281 [ 'animC', 'animationstart' ],
282 'Sorting of simultaneous animationstart events by ' +
287 checkEventOrder([ 'animB', 'animationend' ],
288 [ 'animC', 'animationiteration' ],
289 'Sorting of different types of events by animation-name');
294 //
3b. Test time trumps animation-name position
296 div = document.createElement('div');
297 gDisplay.appendChild(div);
298 div.style.animation = 'animA
10s
2s, animB
10s
1s';
299 div.setAttribute('id', 'div');
304 checkEventOrder([ 'animB', 'animationstart' ],
305 [ 'animA', 'animationstart' ],
306 'Events are sorted by time first, before animation-position');
311 //
4. TESTS FOR TRANSITIONS
313 //
4a. Test sorting transitions by document position
315 divs = [ document.createElement('div'),
316 document.createElement('div') ];
317 divs.forEach((div, i) =
> {
318 gDisplay.appendChild(div);
319 div.style.marginLeft = '
0px';
320 div.style.transition = 'margin-left
10s';
321 div.setAttribute('id', 'div' + i);
324 getComputedStyle(divs[
0]).marginLeft;
325 divs.forEach(div =
> div.style.marginLeft = '
100px');
326 getComputedStyle(divs[
0]).marginLeft;
329 advance_clock(
10000);
331 checkEventOrder([ divs[
0], 'transitionrun' ],
332 [ divs[
0], 'transitionstart' ],
333 [ divs[
1], 'transitionrun' ],
334 [ divs[
1], 'transitionstart' ],
335 [ divs[
0], 'transitionend' ],
336 [ divs[
1], 'transitionend' ],
337 'Simultaneous transitionrun/start/end on siblings');
339 divs.forEach(div =
> div.remove());
342 //
4b. Test sorting transitions by document position (children)
344 divs = [ document.createElement('div'),
345 document.createElement('div'),
346 document.createElement('div') ];
348 // Create the following arrangement:
356 gDisplay.appendChild(divs[
0]);
357 gDisplay.appendChild(divs[
1]);
358 divs[
0].appendChild(divs[
2]);
360 divs.forEach((div, i) =
> {
361 div.style.marginLeft = '
0px';
362 div.style.transition = 'margin-left
10s';
363 div.setAttribute('id', 'div' + i);
366 getComputedStyle(divs[
0]).marginLeft;
367 divs.forEach(div =
> div.style.marginLeft = '
100px');
368 getComputedStyle(divs[
0]).marginLeft;
371 advance_clock(
10000);
373 checkEventOrder([ divs[
0], 'transitionrun' ],
374 [ divs[
0], 'transitionstart' ],
375 [ divs[
2], 'transitionrun' ],
376 [ divs[
2], 'transitionstart' ],
377 [ divs[
1], 'transitionrun' ],
378 [ divs[
1], 'transitionstart' ],
379 [ divs[
0], 'transitionend' ],
380 [ divs[
2], 'transitionend' ],
381 [ divs[
1], 'transitionend' ],
382 'Simultaneous transitionrun/start/end on children');
384 divs.forEach(div =
> div.remove());
387 //
4c. Test sorting transitions by document position (pseudos)
389 divs = [ document.createElement('div'),
390 document.createElement('div') ];
392 // Create the following arrangement:
402 gDisplay.appendChild(divs[
0]);
403 divs[
0].appendChild(divs[
1]);
405 divs.forEach((div, i) =
> {
406 div.setAttribute('id', 'div' + i);
409 extraStyle = document.createElement('style');
410 document.head.appendChild(extraStyle);
411 sheet = extraStyle.sheet;
412 sheet.insertRule('div, #div0::after, #div0::before { ' +
413 ' transition: margin-left
10s; ' +
414 ' margin-left:
0px }',
0);
415 sheet.insertRule('div.active, #div0.active::after, #div0.active::before { ' +
416 ' margin-left:
100px }',
1);
417 sheet.insertRule('#div0::after, #div0::before { ' +
418 ' content:
" " }',
2);
420 getComputedStyle(divs[
0]).marginLeft;
421 divs.forEach(div =
> div.classList.add('active'));
422 getComputedStyle(divs[
0]).marginLeft;
425 advance_clock(
10000);
427 checkEventOrder([ divs[
0], 'transitionrun' ],
428 [ divs[
0], 'transitionstart' ],
429 [ divs[
0], '::before', 'transitionrun' ],
430 [ divs[
0], '::before', 'transitionstart' ],
431 [ divs[
0], '::after', 'transitionrun' ],
432 [ divs[
0], '::after', 'transitionstart' ],
433 [ divs[
1], 'transitionrun' ],
434 [ divs[
1], 'transitionstart' ],
435 [ divs[
0], 'transitionend' ],
436 [ divs[
0], '::before', 'transitionend' ],
437 [ divs[
0], '::after', 'transitionend' ],
438 [ divs[
1], 'transitionend' ],
439 'Simultaneous transitionrun/start/end on pseudo-elements');
441 divs.forEach(div =
> div.remove());
446 extraStyle = undefined;
448 //
4d. Test sorting transitions by time
450 divs = [ document.createElement('div'),
451 document.createElement('div') ];
452 divs.forEach((div, i) =
> {
453 gDisplay.appendChild(div);
454 div.style.marginLeft = '
0px';
455 div.setAttribute('id', 'div' + i);
458 divs[
0].style.transition = 'margin-left
10s';
459 divs[
1].style.transition = 'margin-left
5s';
461 getComputedStyle(divs[
0]).marginLeft;
462 divs.forEach(div =
> div.style.marginLeft = '
100px');
463 getComputedStyle(divs[
0]).marginLeft;
466 advance_clock(
10000);
468 checkEventOrder([ divs[
0], 'transitionrun' ],
469 [ divs[
0], 'transitionstart' ],
470 [ divs[
1], 'transitionrun' ],
471 [ divs[
1], 'transitionstart' ],
472 [ divs[
1], 'transitionend' ],
473 [ divs[
0], 'transitionend' ],
474 'Sorting of transitionrun/start/end events by time');
476 divs.forEach(div =
> div.remove());
479 //
4e. Test sorting transitions by time (with delay)
481 divs = [ document.createElement('div'),
482 document.createElement('div') ];
483 divs.forEach((div, i) =
> {
484 gDisplay.appendChild(div);
485 div.style.marginLeft = '
0px';
486 div.setAttribute('id', 'div' + i);
489 divs[
0].style.transition = 'margin-left
5s
5s';
490 divs[
1].style.transition = 'margin-left
5s';
492 getComputedStyle(divs[
0]).marginLeft;
493 divs.forEach(div =
> div.style.marginLeft = '
100px');
494 getComputedStyle(divs[
0]).marginLeft;
497 advance_clock(
10 *
1000);
499 checkEventOrder([ divs[
0], 'transitionrun' ],
500 [ divs[
1], 'transitionrun' ],
501 [ divs[
1], 'transitionstart' ],
502 [ divs[
0], 'transitionstart' ],
503 [ divs[
1], 'transitionend' ],
504 [ divs[
0], 'transitionend' ],
505 'Sorting of transitionrun/start/end events by time' +
506 '(including delay)');
508 divs.forEach(div =
> div.remove());
511 //
4f. Test sorting transitions by transition-property
513 div = document.createElement('div');
514 gDisplay.appendChild(div);
515 div.style.opacity = '
0';
516 div.style.marginLeft = '
0px';
517 div.style.transition = 'all
5s';
519 getComputedStyle(div).marginLeft;
520 div.style.opacity = '
1';
521 div.style.marginLeft = '
100px';
522 getComputedStyle(div).marginLeft;
525 advance_clock(
10000);
527 checkEventOrder([ 'margin-left', 'transitionrun' ],
528 [ 'margin-left', 'transitionstart' ],
529 [ 'opacity', 'transitionrun' ],
530 [ 'opacity', 'transitionstart' ],
531 [ 'margin-left', 'transitionend' ],
532 [ 'opacity', 'transitionend' ],
533 'Sorting of transitionrun/start/end events by ' +
534 'transition-property')
539 //
4g. Test document position beats transition-property
541 divs = [ document.createElement('div'),
542 document.createElement('div') ];
543 divs.forEach((div, i) =
> {
544 gDisplay.appendChild(div);
545 div.style.marginLeft = '
0px';
546 div.style.opacity = '
0';
547 div.style.transition = 'all
10s';
548 div.setAttribute('id', 'div' + i);
551 getComputedStyle(divs[
0]).marginLeft;
552 divs[
0].style.opacity = '
1';
553 divs[
1].style.marginLeft = '
100px';
554 getComputedStyle(divs[
0]).marginLeft;
557 advance_clock(
10000);
559 checkEventOrder([ divs[
0], 'transitionrun' ],
560 [ divs[
0], 'transitionstart' ],
561 [ divs[
1], 'transitionrun' ],
562 [ divs[
1], 'transitionstart' ],
563 [ divs[
0], 'transitionend' ],
564 [ divs[
1], 'transitionend' ],
565 'Transition events are sorted by document position first, ' +
566 'before transition-property');
568 divs.forEach(div =
> div.remove());
571 //
4h. Test time beats transition-property
573 div = document.createElement('div');
574 gDisplay.appendChild(div);
575 div.style.opacity = '
0';
576 div.style.marginLeft = '
0px';
577 div.style.transition = 'margin-left
10s, opacity
5s';
579 getComputedStyle(div).marginLeft;
580 div.style.opacity = '
1';
581 div.style.marginLeft = '
100px';
582 getComputedStyle(div).marginLeft;
585 advance_clock(
10000);
587 checkEventOrder([ 'margin-left', 'transitionrun' ],
588 [ 'margin-left', 'transitionstart' ],
589 [ 'opacity', 'transitionrun' ],
590 [ 'opacity', 'transitionstart' ],
591 [ 'opacity', 'transitionend' ],
592 [ 'margin-left', 'transitionend' ],
593 'Transition events are sorted by time first, before ' +
594 'transition-property');
599 //
4i. Test sorting transitions by document position (negative delay)
601 divs = [ document.createElement('div'),
602 document.createElement('div') ];
603 divs.forEach((div, i) =
> {
604 gDisplay.appendChild(div);
605 div.style.marginLeft = '
0px';
606 div.setAttribute('id', 'div' + i);
609 divs[
0].style.transition = 'margin-left
10s
5s';
610 divs[
1].style.transition = 'margin-left
10s';
612 getComputedStyle(divs[
0]).marginLeft;
613 divs.forEach(div =
> div.style.marginLeft = '
100px');
614 getComputedStyle(divs[
0]).marginLeft;
617 advance_clock(
15 *
1000);
619 checkEventOrder([ divs[
0], 'transitionrun' ],
620 [ divs[
1], 'transitionrun' ],
621 [ divs[
1], 'transitionstart' ],
622 [ divs[
0], 'transitionstart' ],
623 [ divs[
1], 'transitionend' ],
624 [ divs[
0], 'transitionend' ],
625 'Simultaneous transitionrun/start/end on siblings');
627 divs.forEach(div =
> div.remove());
630 //
4j. Test sorting transitions with cancel
631 // The order of transitioncancel is based on StyleManager.
633 divs = [ document.createElement('div'),
634 document.createElement('div') ];
635 divs.forEach((div, i) =
> {
636 gDisplay.appendChild(div);
637 div.style.marginLeft = '
0px';
638 div.setAttribute('id', 'div' + i);
641 divs[
0].style.transition = 'margin-left
10s
5s';
642 divs[
1].style.transition = 'margin-left
10s';
644 getComputedStyle(divs[
0]).marginLeft;
645 divs.forEach(div =
> div.style.marginLeft = '
100px');
646 getComputedStyle(divs[
0]).marginLeft;
649 advance_clock(
5 *
1000);
650 divs.forEach(div =
> {
651 div.style.display = 'none';
652 // The transitioncancel event order is not absolute when firing siblings
653 // transitioncancel on same elapsed time.
654 // Force to flush style for the element so that the transition on the element
655 // iscancelled and corresponding cancel event is queued respectively.
656 getComputedStyle(div).display;
658 advance_clock(
10 *
1000);
660 checkEventOrder([ divs[
0], 'transitionrun' ],
661 [ divs[
1], 'transitionrun' ],
662 [ divs[
1], 'transitionstart' ],
663 [ divs[
0], 'transitionstart' ],
664 [ divs[
0], 'transitioncancel' ],
665 [ divs[
1], 'transitioncancel' ],
666 'Simultaneous transitionrun/start/cancel on siblings');
668 divs.forEach(div =
> div.remove());
672 //
4k. Test sorting animations with cancel
674 divs = [ document.createElement('div'),
675 document.createElement('div') ];
677 divs.forEach((div, i) =
> {
678 gDisplay.appendChild(div);
679 div.style.marginLeft = '
0px';
680 div.setAttribute('id', 'div' + i);
683 divs[
0].style.animation = 'anim
10s
5s';
684 divs[
1].style.animation = 'anim
10s';
686 getComputedStyle(divs[
0]).animation; // flush
688 advance_clock(
0); // divs[
1]'s animation start
689 advance_clock(
5 *
1000); // divs[
0]'s animation start
690 divs.forEach(div =
> {
691 div.style.display = 'none';
692 // The animationcancel event order is not absolute when firing siblings
693 // animationcancel on same elapsed time.
694 // Force to flush style for the element so that the transition on the element
695 // iscancelled and corresponding cancel event is queued respectively.
696 getComputedStyle(div).display;
698 advance_clock(
10 *
1000);
700 checkEventOrder([ divs[
1], 'animationstart' ],
701 [ divs[
0], 'animationstart' ],
702 [ divs[
0], 'animationcancel' ],
703 [ divs[
1], 'animationcancel' ],
704 'Simultaneous animationcancel on siblings');
706 SpecialPowers.DOMWindowUtils.restoreNormalRefresh();