products/Sources/formale Sprachen/C/Firefox/layout/style/test/test_animations_event_order.html
<!
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 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:
100 px } }
@keyframes animA { to { margin-left:
100 px } }
@keyframes animB { to { margin-left:
100 px } }
@keyframes animC { to { margin-left:
100 px } }
</
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' ;
/* eslint-disable no-shadow */
// 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' ,
'animationcancel' ,
'transitionrun' ,
'transitionstart' ,
'transitionend' ,
'transitioncancel' ]
.forEach(event =>
gDisplay.addEventListener(event,
event => gEventsReceived.push(event)));
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
//
1 a. 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 = [];
//
1 b. 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 = [];
//
1 c. 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 );
sheet.insertRule(
'#div0::after, #div0::before { ' +
' content: " " }' ,
2 );
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
//
2 a. 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 = [];
//
2 b. Test different events within the one element
var div = document.createElement(
'div' );
gDisplay.appendChild(
div );
div .
style .animation =
'anim 4s 2, ' + // Repeat at t=
4 s
'anim 10s 5s, ' + // Start at t=
5 s
'anim 3s' ; // End at t=
3 s
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;
//
2 c. 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' ; //
0 s delay
divs[
2 ].
style .animation =
'anim 20s -5s' ; // Negative delay, sorts same as
//
0 s 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
//
3 a. 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;
//
3 b. 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
//
4 a. 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 = [];
//
4 b. 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 = [];
//
4 c. 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;
//
4 d. 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 = [];
//
4 e. 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 = [];
//
4 f. 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;
//
4 g. 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 = [];
//
4 h. 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;
//
4 i. 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 = [];
//
4 j. 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 = [];
//
4 k. Test sorting animations with cancel
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 .animation =
'anim 10s 5s' ;
divs[
1 ].
style .animation =
'anim 10s' ;
getComputedStyle(divs[
0 ]).animation; // flush
advance_clock(
0 ); // divs[
1 ]
's animation start
advance_clock(
5 *
1000 ); // divs[
0 ]
's animation start
divs.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 >
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland