<!doctype html><html><!--https://bugzilla.mozilla.org/show_bug.cgi?id=1183461--><!-- This test is similar to those in test_animations.html with the exception that those tests interact with a single element at a time. The tests in this file are specifically concerned with testing the ordering of events between elements for which most of the utilities in animation_utils.js are not suited.--><head><metacharset=utf-8><title>Test for CSS Animation and Transition event ordering (Bug 1183461)</title><scriptsrc="/tests/SimpleTest/SimpleTest.js"></script><!-- We still need animation_utils.js for advance_clock --><scripttype="application/javascript"src="animation_utils.js"></script><linkrel="stylesheet"type="text/css"href="/tests/SimpleTest/test.css"/><style>@keyframesanim{to{margin-left:100px}}@keyframesanimA{to{margin-left:100px}}@keyframesanimB{to{margin-left:100px}}@keyframesanimC{to{margin-left:100px}}</style></head><body><atarget="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1183461">Mozilla Bug 1183461</a><divid="display"></div><preid="test"><scripttype="application/javascript">'use strict';/* eslint-disable no-shadow */// Take over the refresh driver right from the start.advance_clock(0);// Common test scaffoldingvargEventsReceived=[];vargDisplay=document.getElementById('display');['animationstart','animationiteration','animationend','animationcancel','transitionrun','transitionstart','transitionend','transitioncancel'].forEach(event=>gDisplay.addEventListener(event,event=>gEventsReceived.push(event)));functioncheckEventOrder(...args){// Argument format:// Arguments = ExpectedEvent*, desc// ExpectedEvent =// [ target|animationName|transitionProperty, (pseudo,) message ]varexpectedEvents=args.slice(0,-1);vardesc=args[args.length-1];varisTestingNameOrProperty=expectedEvents.length&&typeofexpectedEvents[0][0]=='string';varformatEvent=(target,nameOrProperty,pseudo,message)=>isTestingNameOrProperty?`${nameOrProperty}${pseudo}:${message}`:`${target.id}${pseudo}:${message}`;varactual=gEventsReceived.map(event=>formatEvent(event.target,event.animationName||event.propertyName,event.pseudoElement,event.type)).join(';');varexpected=expectedEvents.map(event=>event.length==3?formatEvent(event[0],event[0],event[1],event[2]):formatEvent(event[0],event[0],'',event[1])).join(';');is(actual,expected,desc);gEventsReceived=[];}// 1. TESTS FOR SORTING BY TREE ORDER// 1a. Test that simultaneous events are sorted by tree order (siblings)vardivs=[document.createElement('div'),document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.setAttribute('style','animation: anim 10s');div.setAttribute('id','div'+i);getComputedStyle(div).animationName;// trigger building of animation});advance_clock(0);checkEventOrder([divs[0],'animationstart'],[divs[1],'animationstart'],[divs[2],'animationstart'],'Simultaneous start on siblings');divs.forEach(div=>div.remove());divs=[];// 1b. Test that simultaneous events are sorted by tree order (children)divs=[document.createElement('div'),document.createElement('div'),document.createElement('div')];// Create the following arrangement://// display// / \// div[0] div[1]// /// div[2]gDisplay.appendChild(divs[0]);gDisplay.appendChild(divs[1]);divs[0].appendChild(divs[2]);divs.forEach((div,i)=>{div.setAttribute('style','animation: anim 10s');div.setAttribute('id','div'+i);getComputedStyle(div).animationName;// trigger building of animation});advance_clock(0);checkEventOrder([divs[0],'animationstart'],[divs[2],'animationstart'],[divs[1],'animationstart'],'Simultaneous start on children');divs.forEach(div=>div.remove());divs=[];// 1c. Test that simultaneous events are sorted by tree order (pseudos)divs=[document.createElement('div'),document.createElement('div')];// Create the following arrangement://// display// |// div[0]// ::before,// ::after// |// div[1]gDisplay.appendChild(divs[0]);divs[0].appendChild(divs[1]);divs.forEach((div,i)=>{div.setAttribute('style','animation: anim 10s');div.setAttribute('id','div'+i);});varextraStyle=document.createElement('style');document.head.appendChild(extraStyle);varsheet=extraStyle.sheet;sheet.insertRule('#div0::after { animation: anim 10s }',0);sheet.insertRule('#div0::before { animation: anim 10s }',1);sheet.insertRule('#div0::after, #div0::before { '+' content: " " }',2);getComputedStyle(divs[0]).animationName;// build animationgetComputedStyle(divs[1]).animationName;// build animationadvance_clock(0);checkEventOrder([divs[0],'animationstart'],[divs[0],'::before','animationstart'],[divs[0],'::after','animationstart'],[divs[1],'animationstart'],'Simultaneous start on pseudo-elements');divs.forEach(div=>div.remove());divs=[];sheet=undefined;extraStyle.remove();extraStyle=undefined;// 2. TESTS FOR SORTING BY TIME// 2a. Test that events are sorted by timedivs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.setAttribute('style','animation: anim 10s');div.setAttribute('id','div'+i);});divs[0].style.animationDelay='5s';advance_clock(0);advance_clock(20000);checkEventOrder([divs[1],'animationstart'],[divs[0],'animationstart'],[divs[1],'animationend'],[divs[0],'animationend'],'Sorting of start and end events by time');divs.forEach(div=>div.remove());divs=[];// 2b. Test different events within the one elementvardiv=document.createElement('div');gDisplay.appendChild(div);div.style.animation='anim 4s 2, '+// Repeat at t=4s'anim 10s 5s, '+// Start at t=5s'anim 3s';// End at t=3sdiv.setAttribute('id','div');getComputedStyle(div).animationName;// build animationadvance_clock(0);advance_clock(5000);checkEventOrder([div,'animationstart'],[div,'animationstart'],[div,'animationend'],[div,'animationiteration'],[div,'animationstart'],'Sorting of different events by time within an element');div.remove();div=undefined;// 2c. Test negative delay is sorted equal to zero delay but before// positive delaydivs=[document.createElement('div'),document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.setAttribute('id','div'+i);});divs[0].style.animation='anim 20s 5s';// Positive delay, sorts lastdivs[1].style.animation='anim 20s';// 0s delaydivs[2].style.animation='anim 20s -5s';// Negative delay, sorts same as// 0s delay, i.e. use document// positionadvance_clock(0);advance_clock(5000);checkEventOrder([divs[1],'animationstart'],[divs[2],'animationstart'],[divs[0],'animationstart'],'Sorting of events including negative delay');divs.forEach(div=>div.remove());divs=[];// 3. TESTS FOR SORTING BY animation-name POSITION// 3a. Test animation-name positiondiv=document.createElement('div');gDisplay.appendChild(div);div.style.animation='animA 10s, animB 5s, animC 5s 2';div.setAttribute('id','div');getComputedStyle(div).animationName;// build animationadvance_clock(0);checkEventOrder(['animA','animationstart'],['animB','animationstart'],['animC','animationstart'],'Sorting of simultaneous animationstart events by '+'animation-name');advance_clock(5000);checkEventOrder(['animB','animationend'],['animC','animationiteration'],'Sorting of different types of events by animation-name');div.remove();div=undefined;// 3b. Test time trumps animation-name positiondiv=document.createElement('div');gDisplay.appendChild(div);div.style.animation='animA 10s 2s, animB 10s 1s';div.setAttribute('id','div');advance_clock(0);advance_clock(3000);checkEventOrder(['animB','animationstart'],['animA','animationstart'],'Events are sorted by time first, before animation-position');div.remove();div=undefined;// 4. TESTS FOR TRANSITIONS// 4a. Test sorting transitions by document positiondivs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.style.transition='margin-left 10s';div.setAttribute('id','div'+i);});getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.style.marginLeft='100px');getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder([divs[0],'transitionrun'],[divs[0],'transitionstart'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionend'],[divs[1],'transitionend'],'Simultaneous transitionrun/start/end on siblings');divs.forEach(div=>div.remove());divs=[];// 4b. Test sorting transitions by document position (children)divs=[document.createElement('div'),document.createElement('div'),document.createElement('div')];// Create the following arrangement://// display// / \// div[0] div[1]// /// div[2]gDisplay.appendChild(divs[0]);gDisplay.appendChild(divs[1]);divs[0].appendChild(divs[2]);divs.forEach((div,i)=>{div.style.marginLeft='0px';div.style.transition='margin-left 10s';div.setAttribute('id','div'+i);});getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.style.marginLeft='100px');getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder([divs[0],'transitionrun'],[divs[0],'transitionstart'],[divs[2],'transitionrun'],[divs[2],'transitionstart'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionend'],[divs[2],'transitionend'],[divs[1],'transitionend'],'Simultaneous transitionrun/start/end on children');divs.forEach(div=>div.remove());divs=[];// 4c. Test sorting transitions by document position (pseudos)divs=[document.createElement('div'),document.createElement('div')];// Create the following arrangement://// display// |// div[0]// ::before,// ::after// |// div[1]gDisplay.appendChild(divs[0]);divs[0].appendChild(divs[1]);divs.forEach((div,i)=>{div.setAttribute('id','div'+i);});extraStyle=document.createElement('style');document.head.appendChild(extraStyle);sheet=extraStyle.sheet;sheet.insertRule('div, #div0::after, #div0::before { '+' transition: margin-left 10s; '+' margin-left: 0px }',0);sheet.insertRule('div.active, #div0.active::after, #div0.active::before { '+' margin-left: 100px }',1);sheet.insertRule('#div0::after, #div0::before { '+' content: " " }',2);getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.classList.add('active'));getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder([divs[0],'transitionrun'],[divs[0],'transitionstart'],[divs[0],'::before','transitionrun'],[divs[0],'::before','transitionstart'],[divs[0],'::after','transitionrun'],[divs[0],'::after','transitionstart'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionend'],[divs[0],'::before','transitionend'],[divs[0],'::after','transitionend'],[divs[1],'transitionend'],'Simultaneous transitionrun/start/end on pseudo-elements');divs.forEach(div=>div.remove());divs=[];sheet=undefined;extraStyle.remove();extraStyle=undefined;// 4d. Test sorting transitions by timedivs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.setAttribute('id','div'+i);});divs[0].style.transition='margin-left 10s';divs[1].style.transition='margin-left 5s';getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.style.marginLeft='100px');getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder([divs[0],'transitionrun'],[divs[0],'transitionstart'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[1],'transitionend'],[divs[0],'transitionend'],'Sorting of transitionrun/start/end events by time');divs.forEach(div=>div.remove());divs=[];// 4e. Test sorting transitions by time (with delay)divs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.setAttribute('id','div'+i);});divs[0].style.transition='margin-left 5s 5s';divs[1].style.transition='margin-left 5s';getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.style.marginLeft='100px');getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(10*1000);checkEventOrder([divs[0],'transitionrun'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionstart'],[divs[1],'transitionend'],[divs[0],'transitionend'],'Sorting of transitionrun/start/end events by time'+'(including delay)');divs.forEach(div=>div.remove());divs=[];// 4f. Test sorting transitions by transition-propertydiv=document.createElement('div');gDisplay.appendChild(div);div.style.opacity='0';div.style.marginLeft='0px';div.style.transition='all 5s';getComputedStyle(div).marginLeft;div.style.opacity='1';div.style.marginLeft='100px';getComputedStyle(div).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder(['margin-left','transitionrun'],['margin-left','transitionstart'],['opacity','transitionrun'],['opacity','transitionstart'],['margin-left','transitionend'],['opacity','transitionend'],'Sorting of transitionrun/start/end events by '+'transition-property')div.remove();div=undefined;// 4g. Test document position beats transition-propertydivs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.style.opacity='0';div.style.transition='all 10s';div.setAttribute('id','div'+i);});getComputedStyle(divs[0]).marginLeft;divs[0].style.opacity='1';divs[1].style.marginLeft='100px';getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder([divs[0],'transitionrun'],[divs[0],'transitionstart'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionend'],[divs[1],'transitionend'],'Transition events are sorted by document position first, '+'before transition-property');divs.forEach(div=>div.remove());divs=[];// 4h. Test time beats transition-propertydiv=document.createElement('div');gDisplay.appendChild(div);div.style.opacity='0';div.style.marginLeft='0px';div.style.transition='margin-left 10s, opacity 5s';getComputedStyle(div).marginLeft;div.style.opacity='1';div.style.marginLeft='100px';getComputedStyle(div).marginLeft;advance_clock(0);advance_clock(10000);checkEventOrder(['margin-left','transitionrun'],['margin-left','transitionstart'],['opacity','transitionrun'],['opacity','transitionstart'],['opacity','transitionend'],['margin-left','transitionend'],'Transition events are sorted by time first, before '+'transition-property');div.remove();div=undefined;// 4i. Test sorting transitions by document position (negative delay)divs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.setAttribute('id','div'+i);});divs[0].style.transition='margin-left 10s 5s';divs[1].style.transition='margin-left 10s';getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.style.marginLeft='100px');getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(15*1000);checkEventOrder([divs[0],'transitionrun'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionstart'],[divs[1],'transitionend'],[divs[0],'transitionend'],'Simultaneous transitionrun/start/end on siblings');divs.forEach(div=>div.remove());divs=[];// 4j. Test sorting transitions with cancel// The order of transitioncancel is based on StyleManager.divs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.setAttribute('id','div'+i);});divs[0].style.transition='margin-left 10s 5s';divs[1].style.transition='margin-left 10s';getComputedStyle(divs[0]).marginLeft;divs.forEach(div=>div.style.marginLeft='100px');getComputedStyle(divs[0]).marginLeft;advance_clock(0);advance_clock(5*1000);divs.forEach(div=>{div.style.display='none';// The transitioncancel event order is not absolute when firing siblings// transitioncancel on same elapsed time.// Force to flush style for the element so that the transition on the element// iscancelled and corresponding cancel event is queued respectively.getComputedStyle(div).display;});advance_clock(10*1000);checkEventOrder([divs[0],'transitionrun'],[divs[1],'transitionrun'],[divs[1],'transitionstart'],[divs[0],'transitionstart'],[divs[0],'transitioncancel'],[divs[1],'transitioncancel'],'Simultaneous transitionrun/start/cancel on siblings');divs.forEach(div=>div.remove());divs=[];// 4k. Test sorting animations with canceldivs=[document.createElement('div'),document.createElement('div')];divs.forEach((div,i)=>{gDisplay.appendChild(div);div.style.marginLeft='0px';div.setAttribute('id','div'+i);});divs[0].style.animation='anim 10s 5s';divs[1].style.animation='anim 10s';getComputedStyle(divs[0]).animation;// flushadvance_clock(0);// divs[1]'s animation startadvance_clock(5*1000);// divs[0]'s animation startdivs.forEach(div=>{div.style.display='none';// The animationcancel event order is not absolute when firing siblings// animationcancel on same elapsed time.// Force to flush style for the element so that the transition on the element// iscancelled and corresponding cancel event is queued respectively.getComputedStyle(div).display;});advance_clock(10*1000);checkEventOrder([divs[1],'animationstart'],[divs[0],'animationstart'],[divs[0],'animationcancel'],[divs[1],'animationcancel'],'Simultaneous animationcancel on siblings');SpecialPowers.DOMWindowUtils.restoreNormalRefresh();</script></body></html>