--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -84,16 +84,26 @@ function waitForWheelEvent(aTarget) {
sendWheelAndPaintNoFlush(aTarget, centerX, centerY,
{ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaY: targetRect.height },
resolve);
});
}
+// Returns true if |aAnimation| begins at the current timeline time. We
+// sometimes need to detect this case because if we started an animation
+// asynchronously (e.g. using play()) and then ended up running the next frame
+// at precisely the time the animation started (due to aligning with vsync
+// refresh rate) then we won't end up restyling in that frame.
+function startsRightNow(aAnimation) {
+ return aAnimation.startTime === aAnimation.timeline.currentTime &&
+ aAnimation.currentTime === 0;
+}
+
var omtaEnabled = isOMTAEnabled();
var isAndroid = !!navigator.userAgent.includes("Android");
var isServo = isStyledByServo();
var offscreenThrottlingEnabled =
SpecialPowers.getBoolPref('dom.animations.offscreen-throttling');
var hasMicroTaskCheckpointForAnimation;
@@ -142,18 +152,36 @@ waitForAllPaints(() => {
add_task(async function restyling_for_main_thread_animations() {
var div = addDiv(null, { style: 'animation: background-color 100s' });
var animation = div.getAnimations()[0];
await animation.ready;
ok(!SpecialPowers.wrap(animation).isRunningOnCompositor);
+ // Normally we expect one restyling for each requestAnimationFrame (as
+ // called by observeRestyling) PLUS one for the last frame becasue of bug
+ // 1193394. However, we won't observe that initial restyling unless BOTH of
+ // the following two conditions hold:
+ //
+ // 1. We are running *before* restyling happens. This only happens if we
+ // perform a micro task checkpoint after resolving the 'ready' promise
+ // above (bug 1416966).
+ // 2. The animation actually needs a restyle because it started prior to
+ // this frame. Even if (1) is true, in some cases due to aligning with
+ // the refresh driver, the animation fame in which the ready promise is
+ // resolved happens to coincide perfectly with the start time of the
+ // animation. In this case no restyling is needed so we won't observe
+ // an additional restyle.
+ const expectedRestyleCount =
+ hasMicroTaskCheckpointForAnimation && !startsRightNow(animation)
+ ? 6
+ : 5;
var markers = await observeStyling(5);
- is(markers.length, 5,
+ is(markers.length, expectedRestyleCount,
'CSS animations running on the main-thread should update style ' +
'on the main thread');
await ensureElementRemoval(div);
});
add_task_if_omta_enabled(async function no_restyling_for_compositor_animations() {
var div = addDiv(null, { style: 'animation: opacity 100s' });
var animation = div.getAnimations()[0];
@@ -900,19 +928,23 @@ waitForAllPaints(() => {
}
var div = addDiv(null, { style: 'transform: translateY(-400px);' });
var animation =
div.animate([{ visibility: 'visible' }], 100 * MS_PER_SEC);
await animation.ready;
+ const expectedRestyleCount =
+ hasMicroTaskCheckpointForAnimation && !startsRightNow(animation)
+ ? 6
+ : 5;
var markers = await observeStyling(5);
- is(markers.length, 5,
+ is(markers.length, expectedRestyleCount,
'Discrete animation has has no keyframe whose offset is 0 or 1 in an ' +
'out-of-view element should not be throttled');
await ensureElementRemoval(div);
});
// Counter part of the above test.
add_task(async function no_restyling_discrete_animations_out_of_view_element() {
if (!offscreenThrottlingEnabled ||
@@ -967,18 +999,22 @@ waitForAllPaints(() => {
var rect = addSVGElement(svg, 'rect', { x: '-10',
y: '-10',
width: '10',
height: '10',
fill: 'red' });
var animation = rect.animate({ fill: ['blue', 'lime'] }, 100 * MS_PER_SEC);
await animation.ready;
+ const expectedRestyleCount =
+ hasMicroTaskCheckpointForAnimation && !startsRightNow(animation)
+ ? 6
+ : 5;
var markers = await observeStyling(5);
- is(markers.length, 5,
+ is(markers.length, expectedRestyleCount,
'CSS animations on an in-view svg element with post-transform should ' +
'not be throttled.');
await ensureElementRemoval(div);
});
add_task(async function throttling_animations_out_of_view_svg() {
if (!offscreenThrottlingEnabled) {
@@ -1027,18 +1063,22 @@ waitForAllPaints(() => {
var targetDiv = addDiv(null,
{ style: 'animation: background-color 100s;' +
'transform: translate(-50px, -50px);' });
scrollDiv.appendChild(targetDiv);
var animation = targetDiv.getAnimations()[0];
await animation.ready;
+ const expectedRestyleCount =
+ hasMicroTaskCheckpointForAnimation && !startsRightNow(animation)
+ ? 6
+ : 5;
var markers = await observeStyling(5);
- is(markers.length, 5,
+ is(markers.length, expectedRestyleCount,
'CSS animation on an in-view element with pre-transform should not ' +
'be throttled.');
await ensureElementRemoval(scrollDiv);
});
add_task(async function throttling_animations_out_of_view_css_transform() {
if (!offscreenThrottlingEnabled) {