Bug 1896762 - Make animation timing match the spec. r=smaug,firefox-animation-reviewers,boris

Differential Revision: https://phabricator.services.mozilla.com/D210658
This commit is contained in:
Emilio Cobos Álvarez 2024-05-17 16:33:06 +00:00
parent a944f46809
commit 9116e6b532
9 changed files with 100 additions and 235 deletions

View file

@ -19,7 +19,6 @@ namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(DocumentTimeline)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocumentTimeline,
AnimationTimeline)
tmp->UnregisterFromRefreshDriver();
if (tmp->isInList()) {
tmp->remove();
}
@ -45,7 +44,6 @@ DocumentTimeline::DocumentTimeline(Document* aDocument,
: AnimationTimeline(aDocument->GetParentObject(),
aDocument->GetScopeObject()->GetRTPCallerType()),
mDocument(aDocument),
mIsObservingRefreshDriver(false),
mOriginTime(aOriginTime) {
if (mDocument) {
mDocument->Timelines().insertBack(this);
@ -55,9 +53,6 @@ DocumentTimeline::DocumentTimeline(Document* aDocument,
}
DocumentTimeline::~DocumentTimeline() {
MOZ_RELEASE_ASSERT(!mIsObservingRefreshDriver,
"Timeline should have disassociated"
" from the refresh driver before being destroyed");
if (isInList()) {
remove();
}
@ -105,11 +100,8 @@ TimeStamp DocumentTimeline::GetCurrentTimeStamp() const {
: mLastRefreshDriverTime;
}
void DocumentTimeline::UpdateLastRefreshDriverTime(TimeStamp aKnownTime) {
void DocumentTimeline::UpdateLastRefreshDriverTime() {
TimeStamp result = [&] {
if (!aKnownTime.IsNull()) {
return aKnownTime;
}
if (auto* rd = GetRefreshDriver()) {
return rd->MostRecentRefresh();
};
@ -157,110 +149,53 @@ Nullable<TimeDuration> DocumentTimeline::ToTimelineTime(
void DocumentTimeline::NotifyAnimationUpdated(Animation& aAnimation) {
AnimationTimeline::NotifyAnimationUpdated(aAnimation);
if (!mIsObservingRefreshDriver && !mAnimationOrder.isEmpty()) {
nsRefreshDriver* refreshDriver = GetRefreshDriver();
if (refreshDriver) {
if (!mAnimationOrder.isEmpty()) {
if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
MOZ_ASSERT(isInList(),
"We should not register with the refresh driver if we are not"
" in the document's list of timelines");
ObserveRefreshDriver(refreshDriver);
refreshDriver->EnsureAnimationUpdate();
}
}
}
void DocumentTimeline::MostRecentRefreshTimeUpdated() {
MOZ_ASSERT(mIsObservingRefreshDriver);
MOZ_ASSERT(GetRefreshDriver(),
"Should be able to reach refresh driver from within WillRefresh");
nsAutoAnimationMutationBatch mb(mDocument);
TickState state;
bool ticked = Tick(state);
if (!ticked) {
// We already assert that GetRefreshDriver() is non-null at the beginning
// of this function but we check it again here to be sure that ticking
// animations does not have any side effects that cause us to lose the
// connection with the refresh driver, such as triggering the destruction
// of mDocument's PresShell.
MOZ_ASSERT(GetRefreshDriver(),
"Refresh driver should still be valid at end of WillRefresh");
UnregisterFromRefreshDriver();
}
}
void DocumentTimeline::TriggerAllPendingAnimationsNow() {
for (Animation* animation : mAnimationOrder) {
animation->TryTriggerNow();
}
}
void DocumentTimeline::WillRefresh(TimeStamp aTime) {
UpdateLastRefreshDriverTime();
MostRecentRefreshTimeUpdated();
}
void DocumentTimeline::NotifyTimerAdjusted(TimeStamp aTime) {
MostRecentRefreshTimeUpdated();
}
void DocumentTimeline::ObserveRefreshDriver(nsRefreshDriver* aDriver) {
MOZ_RELEASE_ASSERT(!mIsObservingRefreshDriver,
"shouldn't register as an observer more than once");
// Set the mIsObservingRefreshDriver flag before calling AddRefreshObserver
// since it might end up calling NotifyTimerAdjusted which calls
// MostRecentRefreshTimeUpdated which has an assertion for
// mIsObserveingRefreshDriver check.
mIsObservingRefreshDriver = true;
aDriver->AddRefreshObserver(this, FlushType::Style,
"DocumentTimeline animations");
aDriver->AddTimerAdjustmentObserver(this);
}
void DocumentTimeline::NotifyRefreshDriverCreated(nsRefreshDriver* aDriver) {
MOZ_RELEASE_ASSERT(
!mIsObservingRefreshDriver,
"Timeline should not be observing the refresh driver before"
" it is created");
if (!mAnimationOrder.isEmpty()) {
MOZ_ASSERT(isInList(),
"We should not register with the refresh driver if we are not"
" in the document's list of timelines");
ObserveRefreshDriver(aDriver);
// Although we have started observing the refresh driver, it's possible we
// could perform a paint before the first refresh driver tick happens. To
// ensure we're in a consistent state in that case we run the first tick
// manually.
MostRecentRefreshTimeUpdated();
}
}
void DocumentTimeline::DisconnectRefreshDriver(nsRefreshDriver* aDriver) {
MOZ_ASSERT(mIsObservingRefreshDriver);
aDriver->RemoveRefreshObserver(this, FlushType::Style);
aDriver->RemoveTimerAdjustmentObserver(this);
mIsObservingRefreshDriver = false;
}
void DocumentTimeline::NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver) {
if (!mIsObservingRefreshDriver) {
void DocumentTimeline::WillRefresh() {
if (!mDocument->GetPresShell()) {
// If we're not displayed, don't tick animations.
return;
}
UpdateLastRefreshDriverTime();
if (mAnimationOrder.isEmpty()) {
return;
}
nsAutoAnimationMutationBatch mb(mDocument);
DisconnectRefreshDriver(aDriver);
TickState state;
bool ticked = Tick(state);
if (!ticked) {
return;
}
// We already assert that GetRefreshDriver() is non-null at the beginning
// of this function but we check it again here to be sure that ticking
// animations does not have any side effects that cause us to lose the
// connection with the refresh driver, such as triggering the destruction
// of mDocument's PresShell.
if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
refreshDriver->EnsureAnimationUpdate();
} else {
MOZ_ASSERT_UNREACHABLE(
"Refresh driver should still be valid at end of WillRefresh");
}
}
void DocumentTimeline::RemoveAnimation(Animation* aAnimation) {
AnimationTimeline::RemoveAnimation(aAnimation);
if (!mIsObservingRefreshDriver || !mAnimationOrder.isEmpty()) {
return;
}
UnregisterFromRefreshDriver();
}
void DocumentTimeline::NotifyAnimationContentVisibilityChanged(
@ -268,19 +203,11 @@ void DocumentTimeline::NotifyAnimationContentVisibilityChanged(
AnimationTimeline::NotifyAnimationContentVisibilityChanged(aAnimation,
aIsVisible);
if (mIsObservingRefreshDriver && mAnimationOrder.isEmpty()) {
UnregisterFromRefreshDriver();
}
if (!mIsObservingRefreshDriver && !mAnimationOrder.isEmpty()) {
nsRefreshDriver* refreshDriver = GetRefreshDriver();
if (refreshDriver) {
if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
MOZ_ASSERT(isInList(),
"We should not register with the refresh driver if we are not"
" in the document's list of timelines");
ObserveRefreshDriver(refreshDriver);
}
refreshDriver->EnsureAnimationUpdate();
}
}
@ -302,20 +229,7 @@ nsRefreshDriver* DocumentTimeline::GetRefreshDriver() const {
if (MOZ_UNLIKELY(!presContext)) {
return nullptr;
}
return presContext->RefreshDriver();
}
void DocumentTimeline::UnregisterFromRefreshDriver() {
if (!mIsObservingRefreshDriver) {
return;
}
nsRefreshDriver* refreshDriver = GetRefreshDriver();
if (!refreshDriver) {
return;
}
DisconnectRefreshDriver(refreshDriver);
}
} // namespace mozilla::dom

View file

@ -14,15 +14,12 @@
#include "AnimationTimeline.h"
#include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
#include "nsRefreshDriver.h"
#include "nsRefreshObservers.h"
struct JSContext;
namespace mozilla::dom {
class DocumentTimeline final : public AnimationTimeline,
public nsARefreshObserver,
public nsATimerAdjustmentObserver,
public LinkedListElement<DocumentTimeline> {
public:
DocumentTimeline(Document* aDocument, const TimeDuration& aOriginTime);
@ -35,8 +32,7 @@ class DocumentTimeline final : public AnimationTimeline,
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(DocumentTimeline,
AnimationTimeline)
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<DocumentTimeline> Constructor(
const GlobalObject& aGlobal, const DocumentTimelineOptions& aOptions,
@ -46,7 +42,7 @@ class DocumentTimeline final : public AnimationTimeline,
// This is deliberately _not_ called GetCurrentTime since that would clash
// with a macro defined in winbase.h
virtual Nullable<TimeDuration> GetCurrentTimeAsDuration() const override;
Nullable<TimeDuration> GetCurrentTimeAsDuration() const override;
bool TracksWallclockTime() const override;
Nullable<TimeDuration> ToTimelineTime(
@ -61,27 +57,17 @@ class DocumentTimeline final : public AnimationTimeline,
void TriggerAllPendingAnimationsNow();
// nsARefreshObserver methods
void WillRefresh(TimeStamp aTime) override;
// nsATimerAdjustmentObserver methods
void NotifyTimerAdjusted(TimeStamp aTime) override;
void NotifyRefreshDriverCreated(nsRefreshDriver* aDriver);
void NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver);
void WillRefresh();
Document* GetDocument() const override { return mDocument; }
void UpdateLastRefreshDriverTime(TimeStamp aKnownTime = {});
void UpdateLastRefreshDriverTime();
bool IsMonotonicallyIncreasing() const override { return true; }
protected:
TimeStamp GetCurrentTimeStamp() const;
nsRefreshDriver* GetRefreshDriver() const;
void UnregisterFromRefreshDriver();
void MostRecentRefreshTimeUpdated();
void ObserveRefreshDriver(nsRefreshDriver* aDriver);
void DisconnectRefreshDriver(nsRefreshDriver* aDriver);
RefPtr<Document> mDocument;
@ -89,8 +75,6 @@ class DocumentTimeline final : public AnimationTimeline,
// we don't have a refresh driver (e.g. because we are in a display:none
// iframe).
TimeStamp mLastRefreshDriverTime;
bool mIsObservingRefreshDriver;
TimeDuration mOriginTime;
};

View file

@ -88,11 +88,12 @@ promise_test(async t => {
div.style.animationPlayState = 'paused';
await animation.ready;
await waitForPaints();
assert_animation_is_not_running_on_compositor(animation,
'Animation reports that it is NOT running on the compositor'
+ ' when paused');
}, '');
}, 'Basic test');
promise_test(async t => {
var div = addDiv(t, { style: 'animation: z-index 100s' });
@ -127,6 +128,8 @@ promise_test(async t => {
animation.pause();
await animation.ready;
await waitForPaints();
assert_animation_is_not_running_on_compositor(animation,
'Animation reports that it is NOT running on the compositor'
+ ' when animation.pause() is called');

View file

@ -994,8 +994,8 @@ void PresShell::Init(nsPresContext* aPresContext, nsViewManager* aViewManager) {
animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
}
for (DocumentTimeline* timeline : mDocument->Timelines()) {
timeline->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
for (DocumentTimeline* timelines : mDocument->Timelines()) {
timelines->UpdateLastRefreshDriverTime();
}
// Get our activeness from the docShell.
@ -1338,9 +1338,6 @@ void PresShell::Destroy() {
if (mDocument->HasAnimationController()) {
mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
}
for (DocumentTimeline* timeline : mDocument->Timelines()) {
timeline->NotifyRefreshDriverDestroying(rd);
}
}
if (mPresContext) {

View file

@ -50,6 +50,7 @@
#include "nsComponentManagerUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentTimeline.h"
#include "mozilla/dom/DocumentInlines.h"
#include "nsIXULRuntime.h"
#include "jsapi.h"
@ -1364,6 +1365,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
mNotifyDOMContentFlushed(false),
mNeedToUpdateIntersectionObservations(false),
mNeedToUpdateResizeObservers(false),
mNeedToUpdateAnimations(false),
mMightNeedMediaQueryListenerUpdate(false),
mNeedToUpdateContentRelevancy(false),
mInNormalTick(false),
@ -1489,18 +1491,6 @@ bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
return true;
}
void nsRefreshDriver::AddTimerAdjustmentObserver(
nsATimerAdjustmentObserver* aObserver) {
MOZ_ASSERT(!mTimerAdjustmentObservers.Contains(aObserver));
mTimerAdjustmentObservers.AppendElement(aObserver);
}
void nsRefreshDriver::RemoveTimerAdjustmentObserver(
nsATimerAdjustmentObserver* aObserver) {
MOZ_ASSERT(mTimerAdjustmentObservers.Contains(aObserver));
mTimerAdjustmentObservers.RemoveElement(aObserver);
}
void nsRefreshDriver::PostVisualViewportResizeEvent(
VVPResizeEvent* aResizeEvent) {
mVisualViewportResizeEvents.AppendElement(aResizeEvent);
@ -1885,11 +1875,6 @@ void nsRefreshDriver::EnsureTimerStarted(EnsureTimerStartedFlags aFlags) {
if (mMostRecentRefresh != mActiveTimer->MostRecentRefresh()) {
mMostRecentRefresh = mActiveTimer->MostRecentRefresh();
for (nsATimerAdjustmentObserver* obs :
mTimerAdjustmentObservers.EndLimitedRange()) {
obs->NotifyTimerAdjusted(mMostRecentRefresh);
}
}
}
@ -1919,7 +1904,6 @@ uint32_t nsRefreshDriver::ObserverCount() const {
sum += mThrottledFrameRequestCallbackDocs.Length();
sum += mViewManagerFlushIsPending;
sum += mEarlyRunners.Length();
sum += mTimerAdjustmentObservers.Length();
sum += mAutoFocusFlushDocuments.Length();
return sum;
}
@ -2011,6 +1995,9 @@ auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons {
if (mNeedToUpdateResizeObservers) {
reasons |= TickReasons::eNeedsToNotifyResizeObservers;
}
if (mNeedToUpdateAnimations) {
reasons |= TickReasons::eNeedsToUpdateAnimations;
}
if (mNeedToUpdateIntersectionObservations) {
reasons |= TickReasons::eNeedsToUpdateIntersectionObservations;
}
@ -2054,6 +2041,9 @@ void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons,
if (aReasons & TickReasons::eNeedsToNotifyResizeObservers) {
aStr.AppendLiteral(" NeedsToNotifyResizeObservers");
}
if (aReasons & TickReasons::eNeedsToUpdateAnimations) {
aStr.AppendLiteral(" NeedsToUpdateAnimations");
}
if (aReasons & TickReasons::eNeedsToUpdateIntersectionObservations) {
aStr.AppendLiteral(" NeedsToUpdateIntersectionObservations");
}
@ -2344,11 +2334,41 @@ void nsRefreshDriver::DetermineProximityToViewportAndNotifyResizeObservers() {
}
}
void nsRefreshDriver::DispatchAnimationEvents() {
static CallState UpdateAndReduceAnimations(Document& aDocument) {
for (DocumentTimeline* timeline : aDocument.Timelines()) {
timeline->WillRefresh();
}
if (nsPresContext* pc = aDocument.GetPresContext()) {
if (pc->EffectCompositor()->NeedsReducing()) {
pc->EffectCompositor()->ReduceAnimations();
}
}
aDocument.EnumerateSubDocuments(UpdateAndReduceAnimations);
return CallState::Continue;
}
void nsRefreshDriver::UpdateAnimationsAndSendEvents() {
// TODO(emilio): Can we early-return here if mNeedToUpdateAnimations is
// already false?
mNeedToUpdateAnimations = false;
if (!mPresContext) {
return;
}
{
// Animation updates may queue Promise resolution microtasks. We shouldn't
// run these, however, until we have fully updated the animation state. As
// per the "update animations and send events" procedure[1], we should
// remove replaced animations and then run these microtasks before
// dispatching the corresponding animation events.
//
// [1]:
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
nsAutoMicroTask mt;
UpdateAndReduceAnimations(*mPresContext->Document());
}
// Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
// a RefPtr<> array since each AnimationEventDispatcher might be destroyed
// during processing the previous dispatcher.
@ -2495,16 +2515,6 @@ void nsRefreshDriver::CancelIdleTask(Task* aTask) {
}
}
static CallState ReduceAnimations(Document& aDocument) {
if (nsPresContext* pc = aDocument.GetPresContext()) {
if (pc->EffectCompositor()->NeedsReducing()) {
pc->EffectCompositor()->ReduceAnimations();
}
}
aDocument.EnumerateSubDocuments(ReduceAnimations);
return CallState::Continue;
}
bool nsRefreshDriver::TickObserverArray(uint32_t aIdx, TimeStamp aNowTime) {
MOZ_ASSERT(aIdx < ArrayLength(mObservers));
for (RefPtr<nsARefreshObserver> obs : mObservers[aIdx].EndLimitedRange()) {
@ -2667,28 +2677,6 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
return StopTimer();
}
// Any animation timelines updated above (animation timelines are style flush
// observers) may cause animations to queue Promise resolution microtasks. We
// shouldn't run these, however, until we have fully updated the animation
// state.
//
// As per the "update animations and send events" procedure[1], we should
// remove replaced animations and then run these microtasks before
// dispatching the corresponding animation events.
//
// FIXME(emilio, bug 1896762): This comment doesn't make much sense to me.
// We're running micro-tasks in the block below, but we don't "Update
// animations and send events" until we hit DispatchAnimationEvents() below.
// We should probably refactor the setup so that animation timelines are
// ticked as part of step 11 below or so, and do this only then.
//
// [1]:
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
{
nsAutoMicroTask mt;
ReduceAnimations(*mPresContext->Document());
}
// Check if running the microtask checkpoint above caused the pres context to
// be destroyed.
if (!mPresContext || !mPresContext->GetPresShell()) {
@ -2713,7 +2701,7 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
EvaluateMediaQueriesAndReportChanges();
// Step 11. For each doc of docs, update animations and send events for doc.
DispatchAnimationEvents();
UpdateAnimationsAndSendEvents();
// Step 12. For each doc of docs, run the fullscreen steps for doc.
RunFullscreenSteps();

View file

@ -103,12 +103,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
const char* aObserverDescription);
bool RemoveRefreshObserver(nsARefreshObserver* aObserver,
mozilla::FlushType aFlushType);
/**
* Add / remove an observer wants to know the time when the refresh driver
* updated the most recent refresh time due to its active timer changes.
*/
void AddTimerAdjustmentObserver(nsATimerAdjustmentObserver* aObserver);
void RemoveTimerAdjustmentObserver(nsATimerAdjustmentObserver* aObserver);
void PostVisualViewportResizeEvent(VVPResizeEvent* aResizeEvent);
void DispatchVisualViewportResizeEvents();
@ -417,6 +411,11 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
mNeedToUpdateResizeObservers = true;
}
void EnsureAnimationUpdate() {
EnsureTimerStarted();
mNeedToUpdateAnimations = true;
}
void ScheduleMediaQueryListenerUpdate() {
EnsureTimerStarted();
mMightNeedMediaQueryListenerUpdate = true;
@ -446,6 +445,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
eHasPendingMediaQueryListeners = 1 << 7,
eNeedsToNotifyResizeObservers = 1 << 8,
eRootNeedsMoreTicksForUserInput = 1 << 9,
eNeedsToUpdateAnimations = 1 << 10,
};
void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext);
@ -487,7 +487,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
MOZ_CAN_RUN_SCRIPT
void FlushAutoFocusDocuments();
void RunFullscreenSteps();
void DispatchAnimationEvents();
void UpdateAnimationsAndSendEvents();
MOZ_CAN_RUN_SCRIPT
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime);
@ -643,6 +643,9 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
// all our documents.
bool mNeedToUpdateResizeObservers : 1;
// True if we need to update animations.
bool mNeedToUpdateAnimations : 1;
// True if we might need to report media query changes in any of our
// documents.
bool mMightNeedMediaQueryListenerUpdate : 1;
@ -673,12 +676,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
// separate arrays for each flush type we support
ObserverArray mObservers[3];
// These observers should NOT be included in HasObservers() since that method
// is used to determine whether or not to stop the timer, or restore it when
// thawing the refresh driver. On the other hand these observers are intended
// to be called when the timer is re-started and should not influence its
// starting or stopping.
nsTObserverArray<nsATimerAdjustmentObserver*> mTimerAdjustmentObservers;
nsTArray<mozilla::layers::CompositionPayload> mCompositionPayloads;
RequestTable mRequests;
ImageStartTable mStartTable;

View file

@ -60,17 +60,6 @@ class nsARefreshObserver {
#endif // DEBUG
};
/**
* An abstract base class to be implemented by callers wanting to be notified
* when the observing refresh driver updated mMostRecentRefresh due to active
* timer changes. Callers must ensure an observer is removed before it is
* destroyed.
*/
class nsATimerAdjustmentObserver {
public:
virtual void NotifyTimerAdjusted(mozilla::TimeStamp aTime) = 0;
};
/**
* An abstract base class to be implemented by callers wanting to be notified
* that a refresh has occurred. Callers must ensure an observer is removed

View file

@ -20,6 +20,7 @@
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/CustomEvent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DocumentTimeline.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/IntegerRange.h"
@ -1414,6 +1415,10 @@ nsresult nsPrintJob::ReflowPrintObject(const UniquePtr<nsPrintObject>& aPO) {
aPO->mPresContext->SetPageSize(pageSize);
}
}
// Make sure animations are active.
for (DocumentTimeline* tl : aPO->mDocument->Timelines()) {
tl->TriggerAllPendingAnimationsNow();
}
// Process the reflow event Initialize posted
presShell->FlushPendingNotifications(FlushType::Layout);

View file

@ -1,16 +1,4 @@
[css-transition-cross-document.html]
expected:
if (os == "linux") and debug and fission: [OK, TIMEOUT]
if (os == "linux") and debug and not fission: [OK, TIMEOUT]
if (os == "win") and not debug: TIMEOUT
if (os == "linux") and not debug: TIMEOUT
if os == "android": OK
[TIMEOUT, OK]
expected: [TIMEOUT, OK]
[Moving a transition across documents should reset its state]
expected:
if (os == "linux") and debug and fission: [FAIL, TIMEOUT]
if (os == "linux") and debug and not fission: [FAIL, TIMEOUT]
if (os == "win") and not debug: TIMEOUT
if (os == "linux") and not debug: TIMEOUT
if os == "android": FAIL
[TIMEOUT, FAIL]
expected: [TIMEOUT, FAIL]