Patch fetched from https://hg.mozilla.org/releases/mozilla-release/rev/d2a21d941ed5a73a37b3446caa4a49e74ffe854b # HG changeset patch # User Emilio Cobos Álvarez # Date 1728404712 0 # Node ID d2a21d941ed5a73a37b3446caa4a49e74ffe854b # Parent ca8fc4093e5d03c1a9e4482a95409d59cbafb017 Bug 1923344 - r=smaug, a=dsmith Differential Revision: https://phabricator.services.mozilla.com/D224958 diff --git a/dom/animation/AnimationTimeline.cpp b/dom/animation/AnimationTimeline.cpp --- a/dom/animation/AnimationTimeline.cpp +++ b/dom/animation/AnimationTimeline.cpp @@ -35,71 +35,64 @@ AnimationTimeline::AnimationTimeline(nsI MOZ_ASSERT(mWindow); } AnimationTimeline::~AnimationTimeline() { mAnimationOrder.clear(); } bool AnimationTimeline::Tick(TickState& aState) { bool needsTicks = false; - nsTArray animationsToRemove; - - for (Animation* animation = mAnimationOrder.getFirst(); animation; - animation = - static_cast*>(animation)->getNext()) { + AutoTArray, 32> animationsToTick; + for (Animation* animation : mAnimationOrder) { MOZ_ASSERT(mAnimations.Contains(animation), "The sampling order list should be a subset of the hashset"); MOZ_ASSERT(!animation->IsHiddenByContentVisibility(), "The sampling order list should not contain any animations " "that are hidden by content-visibility"); + animationsToTick.AppendElement(animation); + } + for (Animation* animation : animationsToTick) { // 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! - MOZ_ASSERT(!animation->GetTimeline()); - animationsToRemove.AppendElement(animation); + RemoveAnimation(animation); continue; } needsTicks |= animation->NeedsTicks(); - // Even if |animation| doesn't need future ticks, we should still - // Tick it this time around since it might just need a one-off tick in - // order to dispatch events. + // Even if |animation| doesn't need future ticks, we should still Tick it + // this time around since it might just need a one-off tick in order to + // queue events. animation->Tick(aState); - if (!animation->NeedsTicks()) { - animationsToRemove.AppendElement(animation); + RemoveAnimation(animation); } } - for (Animation* animation : animationsToRemove) { - RemoveAnimation(animation); - } - return needsTicks; } void AnimationTimeline::NotifyAnimationUpdated(Animation& aAnimation) { if (mAnimations.EnsureInserted(&aAnimation)) { if (aAnimation.GetTimeline() && aAnimation.GetTimeline() != this) { aAnimation.GetTimeline()->RemoveAnimation(&aAnimation); } if (!aAnimation.IsHiddenByContentVisibility()) { mAnimationOrder.insertBack(&aAnimation); } } } void AnimationTimeline::RemoveAnimation(Animation* aAnimation) { - MOZ_ASSERT(!aAnimation->GetTimeline() || aAnimation->GetTimeline() == this); - if (static_cast*>(aAnimation)->isInList()) { + if (static_cast*>(aAnimation)->isInList() && + MOZ_LIKELY(!aAnimation->GetTimeline() || + aAnimation->GetTimeline() == this)) { + static_cast*>(aAnimation)->remove(); MOZ_ASSERT(mAnimations.Contains(aAnimation), "The sampling order list should be a subset of the hashset"); - static_cast*>(aAnimation)->remove(); } mAnimations.Remove(aAnimation); } void AnimationTimeline::NotifyAnimationContentVisibilityChanged( Animation* aAnimation, bool aIsVisible) { bool inList = static_cast*>(aAnimation)->isInList(); diff --git a/dom/animation/DocumentTimeline.cpp b/dom/animation/DocumentTimeline.cpp --- a/dom/animation/DocumentTimeline.cpp +++ b/dom/animation/DocumentTimeline.cpp @@ -155,17 +155,22 @@ void DocumentTimeline::NotifyAnimationUp "We should not register with the refresh driver if we are not" " in the document's list of timelines"); refreshDriver->EnsureAnimationUpdate(); } } } void DocumentTimeline::TriggerAllPendingAnimationsNow() { + AutoTArray, 32> animationsToTrigger; for (Animation* animation : mAnimationOrder) { + animationsToTrigger.AppendElement(animation); + } + + for (Animation* animation : animationsToTrigger) { animation->TryTriggerNow(); } } void DocumentTimeline::WillRefresh() { if (!mDocument->GetPresShell()) { // If we're not displayed, don't tick animations. return; @@ -183,19 +188,16 @@ void DocumentTimeline::WillRefresh() { } // 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); } void DocumentTimeline::NotifyAnimationContentVisibilityChanged( diff --git a/dom/animation/ScrollTimelineAnimationTracker.cpp b/dom/animation/ScrollTimelineAnimationTracker.cpp --- a/dom/animation/ScrollTimelineAnimationTracker.cpp +++ b/dom/animation/ScrollTimelineAnimationTracker.cpp @@ -8,23 +8,20 @@ #include "mozilla/dom/Document.h" namespace mozilla { NS_IMPL_CYCLE_COLLECTION(ScrollTimelineAnimationTracker, mPendingSet, mDocument) void ScrollTimelineAnimationTracker::TriggerPendingAnimations() { - for (auto iter = mPendingSet.begin(), end = mPendingSet.end(); iter != end; - ++iter) { - dom::Animation* animation = *iter; - + for (RefPtr& animation : + ToTArray, 32>>(mPendingSet)) { MOZ_ASSERT(animation->GetTimeline() && !animation->GetTimeline()->IsMonotonicallyIncreasing()); - // FIXME: Trigger now may not be correct because the spec says: // If a user agent determines that animation is immediately ready, it may // schedule the task (i.e. ResumeAt()) as a microtask such that it runs at // the next microtask checkpoint, but it must not perform the task // synchronously. // Note: So, for now, we put the animation into the tracker, and trigger // them immediately until the frames are ready. Using TriggerOnNextTick() // for scroll-driven animations may have issues because we don't tick if @@ -34,15 +31,13 @@ void ScrollTimelineAnimationTracker::Tri // inactive. It's pretty hard to tell its future status, for example, it's // possible that the scroll container is in display:none subtree but the // animating element isn't the subtree, then we need to keep tracking the // situation until the scroll container gets framed. so in general we make // this animation be pending (i.e. not ready) if its scroll-timeline is // inactive, and this also matches the current spec definition. continue; } - - // Note: Remove() is legitimately called once per entry during the loop. - mPendingSet.Remove(iter); + mPendingSet.Remove(animation); } } } // namespace mozilla diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -2290,18 +2290,25 @@ void nsRefreshDriver::DetermineProximity ShouldCollect); for (const RefPtr& doc : documents) { MOZ_KnownLive(doc)->DetermineProximityToViewportAndNotifyResizeObservers(); } } static CallState UpdateAndReduceAnimations(Document& aDocument) { - for (DocumentTimeline* timeline : aDocument.Timelines()) { - timeline->WillRefresh(); + { + AutoTArray, 32> timelinesToTick; + for (DocumentTimeline* timeline : aDocument.Timelines()) { + timelinesToTick.AppendElement(timeline); + } + + for (DocumentTimeline* tl : timelinesToTick) { + tl->WillRefresh(); + } } if (nsPresContext* pc = aDocument.GetPresContext()) { if (pc->EffectCompositor()->NeedsReducing()) { pc->EffectCompositor()->ReduceAnimations(); } } aDocument.EnumerateSubDocuments(UpdateAndReduceAnimations); @@ -2321,17 +2328,18 @@ void nsRefreshDriver::UpdateAnimationsAn // 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()); + RefPtr doc = mPresContext->Document(); + UpdateAndReduceAnimations(*doc); } // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as // a RefPtr<> array since each AnimationEventDispatcher might be destroyed // during processing the previous dispatcher. AutoTArray, 16> dispatchers; dispatchers.AppendElements(mAnimationEventFlushObservers); mAnimationEventFlushObservers.Clear();