123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 |
- <!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>
- <meta charset=utf-8>
- <title>Test for CSS Animation and Transition event ordering
- (Bug 1183461)</title>
- <script type="application/javascript"
- src="/tests/SimpleTest/SimpleTest.js"></script>
- <!-- We still need animation_utils.js for advance_clock -->
- <script type="application/javascript" src="animation_utils.js"></script>
- <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
- <style>
- @keyframes anim { to { margin-left: 100px } }
- @keyframes animA { to { margin-left: 100px } }
- @keyframes animB { to { margin-left: 100px } }
- @keyframes animC { to { margin-left: 100px } }
- </style>
- </head>
- <body>
- <a target="_blank"
- href="https://bugzilla.mozilla.org/show_bug.cgi?id=1183461">Mozilla Bug
- 1183461</a>
- <div id="display"></div>
- <pre id="test">
- <script type="application/javascript">
- 'use strict';
- // Take over the refresh driver right from the start.
- advance_clock(0);
- // Common test scaffolding
- var gEventsReceived = [];
- var gDisplay = document.getElementById('display');
- [ 'animationstart',
- 'animationiteration',
- 'animationend',
- 'transitionrun',
- 'transitionstart',
- 'transitionend',
- 'transitioncancel' ]
- .forEach(event =>
- gDisplay.addEventListener(event,
- event => gEventsReceived.push(event),
- false));
- function checkEventOrder(...args) {
- // Argument format:
- // Arguments = ExpectedEvent*, desc
- // ExpectedEvent =
- // [ target|animationName|transitionProperty, (pseudo,) message ]
- var expectedEvents = args.slice(0, -1);
- var desc = args[args.length - 1];
- var isTestingNameOrProperty = expectedEvents.length &&
- typeof expectedEvents[0][0] == 'string';
- var formatEvent = (target, nameOrProperty, pseudo, message) =>
- isTestingNameOrProperty ?
- `${nameOrProperty}${pseudo}:${message}` :
- `${target.id}${pseudo}:${message}`;
- var actual = gEventsReceived.map(
- event => formatEvent(event.target,
- event.animationName || event.propertyName,
- event.pseudoElement, event.type)
- ).join(';');
- var expected = 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)
- var divs = [ 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);
- });
- var extraStyle = document.createElement('style');
- document.head.appendChild(extraStyle);
- var sheet = extraStyle.sheet;
- sheet.insertRule('#div0::after { animation: anim 10s }', 0);
- sheet.insertRule('#div0::before { animation: anim 10s }', 1);
- getComputedStyle(divs[0]).animationName; // build animation
- getComputedStyle(divs[1]).animationName; // build animation
- advance_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 time
- divs = [ 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 element
- var div = 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=3s
- div.setAttribute('id', 'div');
- getComputedStyle(div).animationName; // build animation
- advance_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 delay
- divs = [ 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 last
- divs[1].style.animation = 'anim 20s'; // 0s delay
- divs[2].style.animation = 'anim 20s -5s'; // Negative delay, sorts same as
- // 0s delay, i.e. use document
- // position
- advance_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 position
- div = document.createElement('div');
- gDisplay.appendChild(div);
- div.style.animation = 'animA 10s, animB 5s, animC 5s 2';
- div.setAttribute('id', 'div');
- getComputedStyle(div).animationName; // build animation
- advance_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 position
- div = 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 position
- divs = [ 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 time
- 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';
- 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-property
- div = 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-property
- divs = [ 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-property
- div = 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.
- // So this test looks like wrong result at a glance. However
- // the gecko will cancel div1's transition before div2 in this case.
- 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' );
- getComputedStyle(divs[0]).display;
- advance_clock(10 * 1000);
- checkEventOrder([ divs[0], 'transitionrun' ],
- [ divs[1], 'transitionrun' ],
- [ divs[1], 'transitionstart' ],
- [ divs[0], 'transitionstart' ],
- [ divs[1], 'transitioncancel' ],
- [ divs[0], 'transitioncancel' ],
- 'Simultaneous transitionrun/start/cancel on siblings');
- divs.forEach(div => div.remove());
- divs = [];
- SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
- </script>
- </body>
- </html>
|