mirror of
https://github.com/servo/servo.git
synced 2025-06-28 02:53:48 +01:00
259 lines
8 KiB
HTML
259 lines
8 KiB
HTML
<!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="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="support/testcommon.js"></script>
|
|
<style>
|
|
@keyframes anim {
|
|
from { margin-left: 0px; }
|
|
to { margin-left: 100px; }
|
|
}
|
|
@keyframes color-anim {
|
|
from { color: red; }
|
|
to { color: green; }
|
|
}
|
|
</style>
|
|
<div id="log"></div>
|
|
<script type='text/javascript'>
|
|
'use strict';
|
|
|
|
/**
|
|
* Asserts that the set of actual and received events match.
|
|
* @param actualEvents An array of the received AnimationEvent objects.
|
|
* @param expectedEvents A series of array objects representing the expected
|
|
* events, each having the form:
|
|
* [ event type, target element, [pseudo type], elapsed time ]
|
|
*/
|
|
const checkEvents = (actualEvents, ...expectedEvents) => {
|
|
const actualTypeSummary = actualEvents.map(event => event.type).join(', ');
|
|
const expectedTypeSummary = expectedEvents.map(event => event[0]).join(', ');
|
|
|
|
assert_equals(
|
|
actualEvents.length,
|
|
expectedEvents.length,
|
|
`Number of events received (${actualEvents.length}) \
|
|
should match expected number (${expectedEvents.length}) \
|
|
(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})`
|
|
);
|
|
|
|
for (const [index, actualEvent] of actualEvents.entries()) {
|
|
const expectedEvent = expectedEvents[index];
|
|
const [type, target] = expectedEvent;
|
|
const pseudoElement = expectedEvent.length === 4 ? expectedEvent[2] : '';
|
|
const elapsedTime = expectedEvent[expectedEvent.length - 1];
|
|
|
|
assert_equals(
|
|
actualEvent.type,
|
|
type,
|
|
`Event #${index + 1} types should match \
|
|
(expected: ${expectedTypeSummary}, actual: ${actualTypeSummary})`
|
|
);
|
|
assert_equals(
|
|
actualEvent.target,
|
|
target,
|
|
`Event #${index + 1} targets should match`
|
|
);
|
|
assert_equals(
|
|
actualEvent.pseudoElement,
|
|
pseudoElement,
|
|
`Event #${index + 1} pseudoElements should match`
|
|
);
|
|
assert_equals(
|
|
actualEvent.elapsedTime,
|
|
elapsedTime,
|
|
`Event #${index + 1} elapsedTimes should match`
|
|
);
|
|
}
|
|
};
|
|
|
|
const setupAnimation = (t, animationStyle, receiveEvents) => {
|
|
const div = addDiv(t, { style: 'animation: ' + animationStyle });
|
|
|
|
for (const name of ['start', 'iteration', 'end']) {
|
|
div['onanimation' + name] = evt => {
|
|
receiveEvents.push({
|
|
type: evt.type,
|
|
target: evt.target,
|
|
pseudoElement: evt.pseudoElement,
|
|
elapsedTime: evt.elapsedTime,
|
|
});
|
|
};
|
|
}
|
|
|
|
const watcher = new EventWatcher(t, div, [
|
|
'animationstart',
|
|
'animationiteration',
|
|
'animationend',
|
|
]);
|
|
|
|
const animation = div.getAnimations()[0];
|
|
|
|
return [animation, watcher, div];
|
|
};
|
|
|
|
promise_test(async t => {
|
|
let events = [];
|
|
const [animation1, watcher1, div1] =
|
|
setupAnimation(t, 'anim 100s 2 paused', events);
|
|
const [animation2, watcher2, div2] =
|
|
setupAnimation(t, 'anim 100s 2 paused', events);
|
|
|
|
await Promise.all([ watcher1.wait_for('animationstart'),
|
|
watcher2.wait_for('animationstart') ]);
|
|
|
|
checkEvents(events, ['animationstart', div1, 0],
|
|
['animationstart', div2, 0]);
|
|
|
|
events.length = 0; // Clear received event array
|
|
|
|
animation1.currentTime = 100 * MS_PER_SEC;
|
|
animation2.currentTime = 100 * MS_PER_SEC;
|
|
|
|
await Promise.all([ watcher1.wait_for('animationiteration'),
|
|
watcher2.wait_for('animationiteration') ]);
|
|
|
|
checkEvents(events, ['animationiteration', div1, 100],
|
|
['animationiteration', div2, 100]);
|
|
|
|
events.length = 0; // Clear received event array
|
|
|
|
animation1.finish();
|
|
animation2.finish();
|
|
|
|
await Promise.all([ watcher1.wait_for('animationend'),
|
|
watcher2.wait_for('animationend') ]);
|
|
|
|
checkEvents(events, ['animationend', div1, 200],
|
|
['animationend', div2, 200]);
|
|
}, 'Same events are ordered by elements');
|
|
|
|
function pseudoTest(description, testMarkerPseudos) {
|
|
promise_test(async t => {
|
|
// Setup a hierarchy as follows:
|
|
//
|
|
// parent
|
|
// |
|
|
// (::marker, ::before, ::after) // ::marker optional
|
|
// |
|
|
// child
|
|
const parentDiv = addDiv(t, { style: 'animation: anim 100s' });
|
|
|
|
parentDiv.id = 'parent-div';
|
|
addStyle(t, {
|
|
'#parent-div::after': "content: ''; animation: anim 100s",
|
|
'#parent-div::before': "content: ''; animation: anim 100s",
|
|
});
|
|
|
|
if (testMarkerPseudos) {
|
|
parentDiv.style.display = 'list-item';
|
|
addStyle(t, {
|
|
'#parent-div::marker': "content: ''; animation: color-anim 100s",
|
|
});
|
|
}
|
|
|
|
const childDiv = addDiv(t, { style: 'animation: anim 100s' });
|
|
parentDiv.append(childDiv);
|
|
|
|
// Setup event handlers
|
|
let events = [];
|
|
for (const name of ['start', 'iteration', 'end', 'cancel']) {
|
|
parentDiv['onanimation' + name] = evt => {
|
|
events.push({
|
|
type: evt.type,
|
|
target: evt.target,
|
|
pseudoElement: evt.pseudoElement,
|
|
elapsedTime: evt.elapsedTime,
|
|
});
|
|
};
|
|
}
|
|
|
|
// Wait a couple of frames for the events to be dispatched
|
|
await waitForFrame();
|
|
await waitForFrame();
|
|
|
|
const expectedEvents = [
|
|
['animationstart', parentDiv, 0],
|
|
['animationstart', parentDiv, '::marker', 0],
|
|
['animationstart', parentDiv, '::before', 0],
|
|
['animationstart', parentDiv, '::after', 0],
|
|
['animationstart', childDiv, 0],
|
|
];
|
|
if (!testMarkerPseudos) {
|
|
expectedEvents.splice(1, 1);
|
|
}
|
|
|
|
checkEvents(events, ...expectedEvents);
|
|
}, description);
|
|
}
|
|
|
|
pseudoTest('Same events on pseudo-elements follow the prescribed order', false);
|
|
pseudoTest('Same events on pseudo-elements follow the prescribed order ' +
|
|
'(::marker)', true);
|
|
|
|
promise_test(async t => {
|
|
let events = [];
|
|
const [animation1, watcher1, div1] =
|
|
setupAnimation(t, 'anim 200s 400s', events);
|
|
const [animation2, watcher2, div2] =
|
|
setupAnimation(t, 'anim 300s 2', events);
|
|
|
|
await watcher2.wait_for('animationstart');
|
|
|
|
animation1.currentTime = 400 * MS_PER_SEC;
|
|
animation2.currentTime = 400 * MS_PER_SEC;
|
|
|
|
events.length = 0; // Clear received event array
|
|
|
|
await Promise.all([ watcher1.wait_for('animationstart'),
|
|
watcher2.wait_for('animationiteration') ]);
|
|
|
|
checkEvents(events, ['animationiteration', div2, 300],
|
|
['animationstart', div1, 0]);
|
|
}, 'Start and iteration events are ordered by time');
|
|
|
|
promise_test(async t => {
|
|
let events = [];
|
|
const [animation1, watcher1, div1] =
|
|
setupAnimation(t, 'anim 150s', events);
|
|
const [animation2, watcher2, div2] =
|
|
setupAnimation(t, 'anim 100s 2', events);
|
|
|
|
await Promise.all([ watcher1.wait_for('animationstart'),
|
|
watcher2.wait_for('animationstart') ]);
|
|
|
|
animation1.currentTime = 150 * MS_PER_SEC;
|
|
animation2.currentTime = 150 * MS_PER_SEC;
|
|
|
|
events.length = 0; // Clear received event array
|
|
|
|
await Promise.all([ watcher1.wait_for('animationend'),
|
|
watcher2.wait_for('animationiteration') ]);
|
|
|
|
checkEvents(events, ['animationiteration', div2, 100],
|
|
['animationend', div1, 150]);
|
|
}, 'Iteration and end events are ordered by time');
|
|
|
|
promise_test(async t => {
|
|
let events = [];
|
|
const [animation1, watcher1, div1] =
|
|
setupAnimation(t, 'anim 100s 100s', events);
|
|
const [animation2, watcher2, div2] =
|
|
setupAnimation(t, 'anim 100s 2', events);
|
|
|
|
animation1.finish();
|
|
animation2.finish();
|
|
|
|
await Promise.all([ watcher1.wait_for([ 'animationstart',
|
|
'animationend' ]),
|
|
watcher2.wait_for([ 'animationstart',
|
|
'animationend' ]) ]);
|
|
|
|
checkEvents(events, ['animationstart', div2, 0],
|
|
['animationstart', div1, 0],
|
|
['animationend', div1, 100],
|
|
['animationend', div2, 200]);
|
|
}, 'Start and end events are sorted correctly when fired simultaneously');
|
|
|
|
</script>
|