commit 155893d1b9c4c986dd23bbd5cbc2c657addbd64e parent c3ab936d8de288eb1e055054a4477262b32bbfff Author: Boris Chiou <boris.chiou@gmail.com> Date: Thu, 18 Dec 2025 01:22:20 +0000 Bug 1817051 - Part 4: Sample scroll timelines when trying to trigger the pending animations. r=layout-reviewers,hiro We trigger the pending scroll-driven animations after we flush the layout, and so we need to do an extra sampling of the scroll timelines to make sure those animations can get the correct current time (otherwise we cannot finish its pending state). Therefore, We have to sample the scroll timelines when the pending animation tracker is trying to trigger the animations. Without this patch, we may get an error: "Unhandled rejection with value: object "TypeError: can't access property "ready", animation is undefined"" in content-visibility-animation-with-scroll-timeline-in-hidden-subtree.html. Similar error message in scroll-timeline-resize.html because there is no animation returned by `getAnimations()`, so it is timeout without this patch. Besides, we have to update the OMTA test in file_animations_omta_scroll.html beacause we may clear the OMTA style in the HTML event loop (i.e. when advancing the timing). This may be slightly better than our previous version because using the base style for delay phase to override the out-of-date OMTA style is because the main thread doesn't update the OMTA in time. Differential Revision: https://phabricator.services.mozilla.com/D269667 Diffstat:
7 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/dom/animation/AnimationTimelinesController.cpp b/dom/animation/AnimationTimelinesController.cpp @@ -27,8 +27,6 @@ void AnimationTimelinesController::WillRefresh() { tl->WillRefresh(); } - // TODO: Switch to sample scroll timelines in HTML event loop in the following - // patches. for (ScrollTimeline* tl : ToTArray<AutoTArray<RefPtr<ScrollTimeline>, 32>>(mScrollTimelines)) { tl->WillRefresh(); @@ -61,4 +59,10 @@ void AnimationTimelinesController::UpdateHiddenByContentVisibility() { } } +void AnimationTimelinesController::TrySampleScrollTimelines() { + for (ScrollTimeline* timeline : mScrollTimelines) { + timeline->UpdateCachedCurrentTime(); + } +} + } // namespace mozilla::dom diff --git a/dom/animation/AnimationTimelinesController.h b/dom/animation/AnimationTimelinesController.h @@ -34,6 +34,7 @@ class AnimationTimelinesController final { void UpdateLastRefreshDriverTime(); void TriggerAllPendingAnimationsNow(); void UpdateHiddenByContentVisibility(); + void TrySampleScrollTimelines(); private: LinkedList<DocumentTimeline> mDocumentTimelines; diff --git a/dom/animation/ScrollTimeline.h b/dom/animation/ScrollTimeline.h @@ -189,6 +189,8 @@ class ScrollTimeline : public AnimationTimeline, void NotifyAnimationContentVisibilityChanged(Animation* aAnimation, bool aIsVisible) override; + void UpdateCachedCurrentTime(); + protected: virtual ~ScrollTimeline(); ScrollTimeline() = delete; @@ -203,8 +205,6 @@ class ScrollTimeline : public AnimationTimeline, const ScrollContainerFrame* aScrollFrame, layers::ScrollDirection aOrientation) const; - void UpdateCachedCurrentTime(); - // Note: This function is required to be idempotent, as it can be called from // both cycleCollection::Unlink() and ~ScrollTimeline(). When modifying this // function, be sure to preserve this property. diff --git a/dom/animation/ScrollTimelineAnimationTracker.cpp b/dom/animation/ScrollTimelineAnimationTracker.cpp @@ -13,6 +13,13 @@ namespace mozilla { NS_IMPL_CYCLE_COLLECTION(ScrollTimelineAnimationTracker, mPendingSet, mDocument) void ScrollTimelineAnimationTracker::TriggerPendingAnimations() { + // Sample scroll timelines now to make sure its offsets are up-to-date. + // The scroll container may not be ready when creating the scroll timelines, + // and we trigger the pending animations after we flush the layout, so we have + // to do this here to make sure the current time is up-to-date when triggering + // animations. + mDocument->TimelinesController().TrySampleScrollTimelines(); + for (RefPtr<dom::Animation>& animation : ToTArray<AutoTArray<RefPtr<dom::Animation>, 32>>(mPendingSet)) { MOZ_ASSERT(animation->GetTimeline() && diff --git a/layout/style/test/file_animations_omta_scroll.html b/layout/style/test/file_animations_omta_scroll.html @@ -186,17 +186,22 @@ addAsyncAnimTest(async function() { // sure Bug 1776077 is reproducible. let utils = SpecialPowers.wrap(window).windowUtils; utils.setAsyncScrollOffset(scroller, 0, -50); - utils.advanceTimeAndRefresh(16); - utils.restoreNormalRefresh(); - await waitForPaints(); // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the // OMTA style directly. let compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform"); ok(compositorStr === "matrix(1, 0, 0, 1, 25, 0)", - "scroll animations is in delay phase before calling main thread style " + - "udpate"); + "scroll animations is in delay phase before advancing time to next tick"); + + utils.advanceTimeAndRefresh(16); + utils.restoreNormalRefresh(); + await waitForPaints(); + + // We update the OMTA in the following tick, so the OMTA style gets cleared. + compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform"); + ok(compositorStr === "", + "scroll animation in delay phase clears its OMTA style"); scroller.scrollTop = 25; await waitForPaintsFlushed(); @@ -235,17 +240,22 @@ addAsyncAnimTest(async function() { // sure Bug 1776077 is reproducible. let utils = SpecialPowers.wrap(window).windowUtils; utils.setAsyncScrollOffset(scroller, 0, -50); - utils.advanceTimeAndRefresh(16); - utils.restoreNormalRefresh(); - await waitForPaints(); // NOTE: setAsyncScrollOffset() doesn't update main thread, so we check the // OMTA style directly. let compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform"); ok(compositorStr === "matrix(1, 0, 0, 1, 0, 0)", - "scroll animations is in delay phase before calling main thread style " + - "udpate"); + "scroll animations is in delay phase before advancing time to next tick"); + + utils.advanceTimeAndRefresh(16); + utils.restoreNormalRefresh(); + await waitForPaints(); + + // We update the OMTA in the following tick, so the OMTA style gets cleared. + compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "transform"); + ok(compositorStr === "", + "scroll animation in delay phase clears its OMTA style"); done_div(); done_scroller(); diff --git a/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-animation-with-scroll-timeline-in-hidden-subtree.html.ini b/testing/web-platform/meta/css/css-contain/content-visibility/content-visibility-animation-with-scroll-timeline-in-hidden-subtree.html.ini @@ -1,4 +1,2 @@ [content-visibility-animation-with-scroll-timeline-in-hidden-subtree.html] prefs: [layout.css.scroll-driven-animations.enabled:true] - [Animation with scroll-timeline should be affected c-v] - expected: FAIL diff --git a/testing/web-platform/meta/scroll-animations/crashtests/scroll-timeline-resize.html.ini b/testing/web-platform/meta/scroll-animations/crashtests/scroll-timeline-resize.html.ini @@ -1,2 +0,0 @@ -[scroll-timeline-resize.html] - expected: TIMEOUT