Bug 1416966 - Perform a micro task checkpoint in DocumentTimeline::WillRefresh. r?bevis,birtles
MozReview-Commit-ID: Kd1Il7COZZY
--- a/dom/animation/DocumentTimeline.cpp
+++ b/dom/animation/DocumentTimeline.cpp
@@ -1,16 +1,18 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocumentTimeline.h"
+#include "mozilla/ScopeExit.h"
#include "mozilla/dom/DocumentTimelineBinding.h"
+#include "mozilla/dom/Promise.h"
#include "AnimationUtils.h"
#include "nsContentUtils.h"
#include "nsDOMMutationObserver.h"
#include "nsDOMNavigationTiming.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsRefreshDriver.h"
@@ -151,16 +153,26 @@ DocumentTimeline::WillRefresh(mozilla::T
{
MOZ_ASSERT(mIsObservingRefreshDriver);
MOZ_ASSERT(GetRefreshDriver(),
"Should be able to reach refresh driver from within WillRefresh");
bool needsTicks = false;
nsTArray<Animation*> animationsToRemove(mAnimations.Count());
+ // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events,
+ // step2.
+ // FIXME: This needs to be replaced with nsAutoMicroTask.
+ // Note that this should be done before nsAutoAnimationMutationBatch. If
+ // PerformMicroTaskCheckpoint was called before nsAutoAnimationMutationBatch
+ // is destroyed, some mutation records might not be delivered in this
+ // checkpoint.
+ auto autoPerformMicrotaskCheckpoint = MakeScopeExit([] {
+ Promise::PerformMicroTaskCheckpoint();
+ });
nsAutoAnimationMutationBatch mb(mDocument);
for (Animation* animation = mAnimationOrder.getFirst(); animation;
animation = animation->getNext()) {
// Skip any animations that are longer need associated with this timeline.
if (animation->GetTimeline() != this) {
// If animation has some other timeline, it better not be also in the
// animation list of this timeline object!
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -234,23 +234,22 @@ waitForAllPaints(() => {
var animation = div.getAnimations()[0];
await animation.ready;
ok(SpecialPowers.wrap(animation).isRunningOnCompositor);
animation.finish();
var markers = await observeStyling(1);
- // FIXME: Once we perform a micro task checkpoint between animation tick
- // and requestAnimationFrame callback and we have the conformant Promise
- // handling, we will see two restyle markers in two different frames
- // for the finish() due to bug 1415457.
- is(markers.length, 1,
- 'Bug 1415457: Animations running on the compositor should only ' +
- 'update style once after finish() is called');
+ // FIXME: Once we have the conformant Promise handling, we will see two
+ // restyle markers in two different frames for the finish() due to bug
+ // 1415457.
+ todo_is(markers.length, 1,
+ 'Bug 1415457: Animations running on the compositor should only ' +
+ 'update style once after finish() is called');
markers = await observeStyling(5);
is(markers.length, 0,
'Finished animations should never update style after one ' +
'restyle happened for finish()');
await ensureElementRemoval(div);
});
@@ -260,19 +259,19 @@ waitForAllPaints(() => {
getComputedStyle(div).opacity;
div.style.opacity = 1;
var animation = div.getAnimations()[0];
var initialRect = div.getBoundingClientRect();
await animation.finished;
var markers = await observeStyling(1);
- todo_is(markers.length, 1,
- 'Finished transitions should restyle once after ' +
- 'Animation.finished was fulfilled');
+ is(markers.length, 1,
+ 'Finished transitions should restyle once after Animation.finished ' +
+ 'was fulfilled');
var mouseX = initialRect.left + initialRect.width / 2;
var mouseY = initialRect.top + initialRect.height / 2;
markers = await observeStyling(5, () => {
// We can't use synthesizeMouse here since synthesizeMouse causes
// layout flush.
synthesizeMouseAtPoint(mouseX++, mouseY++,
{ type: 'mousemove' }, window);
@@ -287,19 +286,19 @@ waitForAllPaints(() => {
add_task(async function no_restyling_mouse_movement_on_finished_animation() {
var div = addDiv(null, { style: 'animation: opacity 1ms' });
var animation = div.getAnimations()[0];
var initialRect = div.getBoundingClientRect();
await animation.finished;
var markers = await observeStyling(1);
- todo_is(markers.length, 1,
- 'Finished animations should restyle once after ' +
- 'Animation.finished was fulfilled');
+ is(markers.length, 1,
+ 'Finished animations should restyle once after Animation.finished ' +
+ 'was fulfilled');
var mouseX = initialRect.left + initialRect.width / 2;
var mouseY = initialRect.top + initialRect.height / 2;
markers = await observeStyling(5, () => {
// We can't use synthesizeMouse here since synthesizeMouse causes
// layout flush.
synthesizeMouseAtPoint(mouseX++, mouseY++,
{ type: 'mousemove' }, window);
@@ -623,19 +622,19 @@ waitForAllPaints(() => {
await animation.ready;
ok(SpecialPowers.wrap(animation).isRunningOnCompositor);
animation.pause();
await animation.ready;
var markers = await observeStyling(1);
- todo_is(markers.length, 1,
- 'Animations running on the compositor should restyle once after ' +
- 'Animation.pause() was called');
+ is(markers.length, 1,
+ 'Animations running on the compositor should restyle once after ' +
+ 'Animation.pause() was called');
markers = await observeStyling(5);
is(markers.length, 0,
'Paused animations running on the compositor should never cause ' +
'restyles');
await ensureElementRemoval(div);
});
@@ -645,19 +644,19 @@ waitForAllPaints(() => {
await animation.ready;
animation.pause();
await animation.ready;
var markers = await observeStyling(1);
- todo_is(markers.length, 1,
- 'Animations running on the main-thread should restyle once after ' +
- 'Animation.pause() was called');
+ is(markers.length, 1,
+ 'Animations running on the main-thread should restyle once after ' +
+ 'Animation.pause() was called');
markers = await observeStyling(5);
is(markers.length, 0,
'Paused animations running on the main-thread should never cause ' +
'restyles');
await ensureElementRemoval(div);
});
@@ -892,19 +891,19 @@ waitForAllPaints(() => {
// layer restyle (bug 1388557) before fixing important_rules_change in
// compute_style() so that it no longer dispatches a needless standard
// restyle (bug 1388560), we should add a test case that fails if we
// continue to unnecessarily request a standard restyle.
//
// Bug 1417354: There will be an additional superfluos restyle request
// whichi results when we detach an element from the document between
// the Animation tick and styling, and then re-attach it.
- todo_is(markers.length, 2,
- 'We should observe one restyle in the first frame right after ' +
- 're-attaching to the document');
+ is(markers.length, 2,
+ 'We should observe one restyle in the first frame right after ' +
+ 're-attaching to the document');
} else {
// Bug 1388557: We should call RequestRestyle(Layer) when an element which
// has running script animations is attached to a document.
todo_is(markers.length, 1,
'Bug 1388557 We should observe one restyle in the first frame ' +
'right after re-attaching to the document');
}
markers = await observeStyling(5);
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/timelines/timelines.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[timelines.html]
- [Performs a micro task checkpoint before requestAnimation callbacks]
- expected: FAIL
-