forked from mirrors/gecko-dev
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:
parent
a944f46809
commit
9116e6b532
9 changed files with 100 additions and 235 deletions
|
|
@ -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) {
|
||||
MOZ_ASSERT(isInList(),
|
||||
"We should not register with the refresh driver if we are not"
|
||||
" in the document's list of timelines");
|
||||
|
||||
ObserveRefreshDriver(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");
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Reference in a new issue