Display-frequency animations in ScrollController should be externally driven master
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 May 2021 02:45:51 +0000 (02:45 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 May 2021 02:45:51 +0000 (02:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=225663

Reviewed by Tim Horton.

Display-frequency animations (i.e. those driven by 1/60_s timers) should ultimately
be driven by Page::updateRendering(), since their output should be synchronized with
rendering updates.

As the first step, remove m_snapRubberbandTimer and m_scrollSnapTimer from
ScrollController and have it expose functions that tell its client to start and stop
the animation, and a per-frame callback. Give ScrollAnimator a 1/60_s timer
to drive this, temporarily.

* page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.h:
* page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm:
(WebCore::ScrollingTreeScrollingNodeDelegateMac::startAnimationCallback):
(WebCore::ScrollingTreeScrollingNodeDelegateMac::stopAnimationCallback):
(WebCore::ScrollingTreeScrollingNodeDelegateMac::scrollControllerAnimationTimerFired):
* platform/ScrollAnimator.cpp:
(WebCore::ScrollAnimator::ScrollAnimator):
(WebCore::ScrollAnimator::startAnimationCallback):
(WebCore::ScrollAnimator::stopAnimationCallback):
(WebCore::ScrollAnimator::scrollControllerAnimationTimerFired):
* platform/ScrollAnimator.h:
* platform/ScrollController.cpp:
(WebCore::ScrollController::animationCallback):
(WebCore::ScrollController::startOrStopAnimationCallbacks):
(WebCore::ScrollController::setIsAnimatingRubberBand):
(WebCore::ScrollController::setIsAnimatingScrollSnap):
(WebCore::ScrollController::updateScrollSnapAnimatingState):
(WebCore::ScrollController::updateRubberBandAnimatingState):
* platform/ScrollController.h:
* platform/ScrollSnapAnimatorState.cpp:
(WebCore::ScrollSnapAnimatorState::currentAnimatedScrollOffset const):
* platform/ScrollSnapAnimatorState.h:
* platform/mac/ScrollController.mm:
(WebCore::ScrollController::stopAllTimers):
(WebCore::ScrollController::handleWheelEvent):
(WebCore::ScrollController::updateRubberBandAnimatingState):
(WebCore::ScrollController::isScrollSnapInProgress const):
(WebCore::ScrollController::stopRubberbanding):
(WebCore::ScrollController::startRubberbandAnimation):
(WebCore::ScrollController::stopSnapRubberbandAnimation):
(WebCore::ScrollController::snapRubberBand):
(WebCore::ScrollController::isRubberBandInProgressInternal const):
(WebCore::ScrollController::scheduleStatelessScrollSnap):
(WebCore::ScrollController::statelessSnapTransitionTimerFired):
(WebCore::ScrollController::processWheelEventForScrollSnap):
(WebCore::ScrollController::startScrollSnapAnimation):
(WebCore::ScrollController::stopScrollSnapAnimation):
(WebCore::ScrollController::updateScrollSnapAnimatingState):
(WebCore::ScrollController::snapRubberBandTimerFired): Deleted.
(WebCore::ScrollController::startSnapRubberbandTimer): Deleted.
(WebCore::ScrollController::stopSnapRubberbandTimer): Deleted.
(WebCore::ScrollController::startScrollSnapTimer): Deleted.
(WebCore::ScrollController::stopScrollSnapTimer): Deleted.
(WebCore::ScrollController::scrollSnapTimerFired): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@277350 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.h
Source/WebCore/page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm
Source/WebCore/platform/ScrollAnimator.cpp
Source/WebCore/platform/ScrollAnimator.h
Source/WebCore/platform/ScrollController.cpp
Source/WebCore/platform/ScrollController.h
Source/WebCore/platform/ScrollSnapAnimatorState.cpp
Source/WebCore/platform/ScrollSnapAnimatorState.h
Source/WebCore/platform/mac/ScrollController.mm

index 6a32110..df59360 100644 (file)
@@ -1,3 +1,64 @@
+2021-05-11  Simon Fraser  <simon.fraser@apple.com>
+
+        Display-frequency animations in ScrollController should be externally driven
+        https://bugs.webkit.org/show_bug.cgi?id=225663
+
+        Reviewed by Tim Horton.
+
+        Display-frequency animations (i.e. those driven by 1/60_s timers) should ultimately
+        be driven by Page::updateRendering(), since their output should be synchronized with
+        rendering updates.
+
+        As the first step, remove m_snapRubberbandTimer and m_scrollSnapTimer from
+        ScrollController and have it expose functions that tell its client to start and stop
+        the animation, and a per-frame callback. Give ScrollAnimator a 1/60_s timer
+        to drive this, temporarily.
+
+        * page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.h:
+        * page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm:
+        (WebCore::ScrollingTreeScrollingNodeDelegateMac::startAnimationCallback):
+        (WebCore::ScrollingTreeScrollingNodeDelegateMac::stopAnimationCallback):
+        (WebCore::ScrollingTreeScrollingNodeDelegateMac::scrollControllerAnimationTimerFired):
+        * platform/ScrollAnimator.cpp:
+        (WebCore::ScrollAnimator::ScrollAnimator):
+        (WebCore::ScrollAnimator::startAnimationCallback):
+        (WebCore::ScrollAnimator::stopAnimationCallback):
+        (WebCore::ScrollAnimator::scrollControllerAnimationTimerFired):
+        * platform/ScrollAnimator.h:
+        * platform/ScrollController.cpp:
+        (WebCore::ScrollController::animationCallback):
+        (WebCore::ScrollController::startOrStopAnimationCallbacks):
+        (WebCore::ScrollController::setIsAnimatingRubberBand):
+        (WebCore::ScrollController::setIsAnimatingScrollSnap):
+        (WebCore::ScrollController::updateScrollSnapAnimatingState):
+        (WebCore::ScrollController::updateRubberBandAnimatingState):
+        * platform/ScrollController.h:
+        * platform/ScrollSnapAnimatorState.cpp:
+        (WebCore::ScrollSnapAnimatorState::currentAnimatedScrollOffset const):
+        * platform/ScrollSnapAnimatorState.h:
+        * platform/mac/ScrollController.mm:
+        (WebCore::ScrollController::stopAllTimers):
+        (WebCore::ScrollController::handleWheelEvent):
+        (WebCore::ScrollController::updateRubberBandAnimatingState):
+        (WebCore::ScrollController::isScrollSnapInProgress const):
+        (WebCore::ScrollController::stopRubberbanding):
+        (WebCore::ScrollController::startRubberbandAnimation):
+        (WebCore::ScrollController::stopSnapRubberbandAnimation):
+        (WebCore::ScrollController::snapRubberBand):
+        (WebCore::ScrollController::isRubberBandInProgressInternal const):
+        (WebCore::ScrollController::scheduleStatelessScrollSnap):
+        (WebCore::ScrollController::statelessSnapTransitionTimerFired):
+        (WebCore::ScrollController::processWheelEventForScrollSnap):
+        (WebCore::ScrollController::startScrollSnapAnimation):
+        (WebCore::ScrollController::stopScrollSnapAnimation):
+        (WebCore::ScrollController::updateScrollSnapAnimatingState):
+        (WebCore::ScrollController::snapRubberBandTimerFired): Deleted.
+        (WebCore::ScrollController::startSnapRubberbandTimer): Deleted.
+        (WebCore::ScrollController::stopSnapRubberbandTimer): Deleted.
+        (WebCore::ScrollController::startScrollSnapTimer): Deleted.
+        (WebCore::ScrollController::stopScrollSnapTimer): Deleted.
+        (WebCore::ScrollController::scrollSnapTimerFired): Deleted.
+
 2021-05-11  Geoffrey Garen  <ggaren@apple.com>
 
         Function.prototype.toString triggers page demand on Speedometer
index 9a11b6a..c1a0676 100644 (file)
@@ -30,6 +30,7 @@
 #if ENABLE(ASYNC_SCROLLING) && PLATFORM(MAC)
 
 #include "ScrollController.h"
+#include <wtf/RunLoop.h>
 
 OBJC_CLASS NSScrollerImp;
 
@@ -73,6 +74,9 @@ private:
 
     // ScrollControllerClient.
     std::unique_ptr<ScrollControllerTimer> createTimer(Function<void()>&&) final;
+    void startAnimationCallback(ScrollController&) final;
+    void stopAnimationCallback(ScrollController&) final;
+
     bool allowsHorizontalStretching(const PlatformWheelEvent&) const final;
     bool allowsVerticalStretching(const PlatformWheelEvent&) const final;
     IntSize stretchAmount() const final;
@@ -88,6 +92,7 @@ private:
     void adjustScrollPositionToBoundsIfNecessary() final;
 
     bool scrollPositionIsNotRubberbandingEdge(const FloatPoint&) const;
+    void scrollControllerAnimationTimerFired();
 
 #if ENABLE(CSS_SCROLL_SNAP)
     FloatPoint scrollOffset() const override;
@@ -102,11 +107,13 @@ private:
     void releaseReferencesToScrollerImpsOnTheMainThread();
 
     ScrollController m_scrollController;
-    
-    bool m_inMomentumPhase { false };
 
     RetainPtr<NSScrollerImp> m_verticalScrollerImp;
     RetainPtr<NSScrollerImp> m_horizontalScrollerImp;
+
+    std::unique_ptr<RunLoop::Timer<ScrollingTreeScrollingNodeDelegateMac>> m_scrollControllerAnimationTimer;
+
+    bool m_inMomentumPhase { false };
 };
 
 } // namespace WebCore
index c04e51b..4fd7591 100644 (file)
@@ -225,6 +225,28 @@ std::unique_ptr<ScrollControllerTimer> ScrollingTreeScrollingNodeDelegateMac::cr
     });
 }
 
+void ScrollingTreeScrollingNodeDelegateMac::startAnimationCallback(ScrollController&)
+{
+    if (!m_scrollControllerAnimationTimer)
+        m_scrollControllerAnimationTimer = WTF::makeUnique<RunLoop::Timer<ScrollingTreeScrollingNodeDelegateMac>>(RunLoop::current(), this, &ScrollingTreeScrollingNodeDelegateMac::scrollControllerAnimationTimerFired);
+
+    if (m_scrollControllerAnimationTimer->isActive())
+        return;
+
+    m_scrollControllerAnimationTimer->startRepeating(1_s / 60.);
+}
+
+void ScrollingTreeScrollingNodeDelegateMac::stopAnimationCallback(ScrollController&)
+{
+    if (m_scrollControllerAnimationTimer)
+        m_scrollControllerAnimationTimer->stop();
+}
+
+void ScrollingTreeScrollingNodeDelegateMac::scrollControllerAnimationTimerFired()
+{
+    m_scrollController.animationCallback(MonotonicTime::now());
+}
+
 bool ScrollingTreeScrollingNodeDelegateMac::allowsHorizontalStretching(const PlatformWheelEvent& wheelEvent) const
 {
     switch (horizontalScrollElasticity()) {
index 76de729..8d62e02 100644 (file)
@@ -56,6 +56,7 @@ ScrollAnimator::ScrollAnimator(ScrollableArea& scrollableArea)
     : m_scrollableArea(scrollableArea)
 #if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
     , m_scrollController(*this)
+    , m_scrollControllerAnimationTimer(*this, &ScrollAnimator::scrollControllerAnimationTimerFired)
 #endif
     , m_scrollAnimation(makeUnique<ScrollAnimationSmooth>(
         [this]() -> ScrollExtents {
@@ -333,6 +334,24 @@ std::unique_ptr<ScrollControllerTimer> ScrollAnimator::createTimer(Function<void
         function();
     });
 }
+
+void ScrollAnimator::startAnimationCallback(ScrollController&)
+{
+    if (m_scrollControllerAnimationTimer.isActive())
+        return;
+        
+    m_scrollControllerAnimationTimer.startRepeating(1_s / 60.);
+}
+
+void ScrollAnimator::stopAnimationCallback(ScrollController&)
+{
+    m_scrollControllerAnimationTimer.stop();
+}
+
+void ScrollAnimator::scrollControllerAnimationTimerFired()
+{
+    m_scrollController.animationCallback(MonotonicTime::now());
+}
 #endif
 
 #if (ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)) && PLATFORM(MAC)
index dcb4b48..b6163a1 100644 (file)
@@ -34,6 +34,7 @@
 #include "FloatPoint.h"
 #include "PlatformWheelEvent.h"
 #include "ScrollTypes.h"
+#include "Timer.h"
 #include "WheelEventTestMonitor.h"
 #include <wtf/FastMalloc.h>
 #include <wtf/Forward.h>
@@ -146,6 +147,11 @@ public:
 
 #if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
     std::unique_ptr<ScrollControllerTimer> createTimer(Function<void()>&&) final;
+
+    void startAnimationCallback(ScrollController&) final;
+    void stopAnimationCallback(ScrollController&) final;
+
+    void scrollControllerAnimationTimerFired();
 #endif
 
 #if ENABLE(CSS_SCROLL_SNAP)
@@ -180,6 +186,7 @@ protected:
     RefPtr<WheelEventTestMonitor> m_wheelEventTestMonitor;
 #if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
     ScrollController m_scrollController;
+    Timer m_scrollControllerAnimationTimer;
 #endif
     FloatPoint m_currentPosition;
 
index f0ade73..b9d6bbd 100644 (file)
@@ -30,6 +30,7 @@
 #include "Logging.h"
 #include "PlatformWheelEvent.h"
 #include "WheelEventTestMonitor.h"
+#include <wtf/text/TextStream.h>
 
 #if ENABLE(CSS_SCROLL_SNAP)
 #include "ScrollSnapAnimatorState.h"
@@ -45,6 +46,48 @@ ScrollController::ScrollController(ScrollControllerClient& client)
 {
 }
 
+void ScrollController::animationCallback(MonotonicTime currentTime)
+{
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollController " << this << " animationCallback: isAnimatingRubberBand " << m_isAnimatingRubberBand << " isAnimatingScrollSnap " << m_isAnimatingScrollSnap);
+
+    updateScrollSnapAnimatingState(currentTime);
+    updateRubberBandAnimatingState(currentTime);
+}
+
+void ScrollController::startOrStopAnimationCallbacks()
+{
+    bool needsCallbacks = m_isAnimatingRubberBand || m_isAnimatingScrollSnap;
+    if (needsCallbacks == m_isRunningAnimatingCallback)
+        return;
+
+    if (needsCallbacks) {
+        m_client.startAnimationCallback(*this);
+        m_isRunningAnimatingCallback = true;
+        return;
+    }
+
+    m_client.stopAnimationCallback(*this);
+    m_isRunningAnimatingCallback = false;
+}
+
+void ScrollController::setIsAnimatingRubberBand(bool isAnimatingRubberBand)
+{
+    if (isAnimatingRubberBand == m_isAnimatingRubberBand)
+        return;
+        
+    m_isAnimatingRubberBand = isAnimatingRubberBand;
+    startOrStopAnimationCallbacks();
+}
+
+void ScrollController::setIsAnimatingScrollSnap(bool isAnimatingScrollSnap)
+{
+    if (isAnimatingScrollSnap == m_isAnimatingScrollSnap)
+        return;
+        
+    m_isAnimatingScrollSnap = isAnimatingScrollSnap;
+    startOrStopAnimationCallbacks();
+}
+
 bool ScrollController::usesScrollSnap() const
 {
 #if ENABLE(CSS_SCROLL_SNAP)
@@ -169,6 +212,16 @@ void ScrollController::scrollPositionChanged()
 {
 }
 
+void ScrollController::updateScrollSnapAnimatingState(MonotonicTime)
+{
+
+}
+
+void ScrollController::updateRubberBandAnimatingState(MonotonicTime)
+{
+
+}
+
 #endif // PLATFORM(MAC)
 
 } // namespace WebCore
index dd53ada..93af58b 100644 (file)
@@ -70,8 +70,12 @@ protected:
     virtual ~ScrollControllerClient() = default;
 
 public:
+    // Only used for non-animation timers.
     virtual std::unique_ptr<ScrollControllerTimer> createTimer(Function<void()>&&) = 0;
 
+    virtual void startAnimationCallback(ScrollController&) = 0;
+    virtual void stopAnimationCallback(ScrollController&) = 0;
+
 #if ENABLE(RUBBER_BANDING)
     virtual bool allowsHorizontalStretching(const PlatformWheelEvent&) const = 0;
     virtual bool allowsVerticalStretching(const PlatformWheelEvent&) const = 0;
@@ -130,6 +134,9 @@ public:
     bool usesScrollSnap() const;
     void stopAllTimers();
     void scrollPositionChanged();
+    
+    // Should be called periodically by the client. Started by startAnimationCallback(), stopped by stopAnimationCallback().
+    void animationCallback(MonotonicTime);
 
 #if ENABLE(CSS_SCROLL_SNAP)
     void updateScrollSnapPoints(const LayoutScrollSnapOffsetsInfo&);
@@ -171,11 +178,17 @@ private:
     void setNearestScrollSnapIndexForAxisAndOffset(ScrollEventAxis, int);
 #endif
 
+    void updateScrollSnapAnimatingState(MonotonicTime);
+    void updateRubberBandAnimatingState(MonotonicTime);
+    
+    void setIsAnimatingRubberBand(bool);
+    void setIsAnimatingScrollSnap(bool);
+
 #if PLATFORM(MAC)
 #if ENABLE(CSS_SCROLL_SNAP)
-    void scrollSnapTimerFired();
-    void startScrollSnapTimer();
-    void stopScrollSnapTimer();
+    void startScrollSnapAnimation();
+    void stopScrollSnapAnimation();
+
     bool shouldOverrideMomentumScrolling() const;
     void statelessSnapTransitionTimerFired();
     void scheduleStatelessScrollSnap();
@@ -184,10 +197,10 @@ private:
 #endif
 
 #if ENABLE(RUBBER_BANDING)
-    void startSnapRubberbandTimer();
-    void stopSnapRubberbandTimer();
+    void startRubberbandAnimation();
+    void stopSnapRubberbandAnimation();
+
     void snapRubberBand();
-    void snapRubberBandTimerFired();
     bool shouldRubberBandInHorizontalDirection(const PlatformWheelEvent&) const;
     bool shouldRubberBandInDirection(ScrollDirection) const;
     bool isRubberBandInProgressInternal() const;
@@ -196,12 +209,18 @@ private:
 #endif
 #endif
 
+    void startOrStopAnimationCallbacks();
+
     ScrollControllerClient& m_client;
 #if ENABLE(CSS_SCROLL_SNAP)
     std::unique_ptr<ScrollSnapAnimatorState> m_scrollSnapState;
     bool m_activeScrollSnapIndexDidChange { false };
 #endif
 
+    bool m_isRunningAnimatingCallback { false };
+    bool m_isAnimatingRubberBand { false };
+    bool m_isAnimatingScrollSnap { false };
+
 #if PLATFORM(MAC)
     WallTime m_lastMomentumScrollTimestamp;
     FloatSize m_overflowScrollDelta;
@@ -216,7 +235,6 @@ private:
 #if ENABLE(CSS_SCROLL_SNAP)
     FloatSize m_dragEndedScrollingVelocity;
     std::unique_ptr<ScrollControllerTimer> m_statelessSnapTransitionTimer;
-    std::unique_ptr<ScrollControllerTimer> m_scrollSnapTimer;
 #endif
 
 #if ENABLE(RUBBER_BANDING)
@@ -225,7 +243,6 @@ private:
     FloatSize m_startStretch;
     FloatSize m_origVelocity;
     RectEdges<bool> m_rubberBandingEdges;
-    std::unique_ptr<ScrollControllerTimer> m_snapRubberbandTimer;
 #endif
 
 #if ASSERT_ENABLED
index 4185213..5a06d88 100644 (file)
@@ -79,14 +79,14 @@ void ScrollSnapAnimatorState::teardownAnimationForState(ScrollSnapState state)
     m_currentState = state;
 }
 
-FloatPoint ScrollSnapAnimatorState::currentAnimatedScrollOffset(bool& isAnimationComplete) const
+FloatPoint ScrollSnapAnimatorState::currentAnimatedScrollOffset(MonotonicTime currentTime, bool& isAnimationComplete) const
 {
     if (!m_momentumCalculator) {
         isAnimationComplete = true;
         return { };
     }
 
-    Seconds elapsedTime = MonotonicTime::now() - m_startTime;
+    Seconds elapsedTime = currentTime - m_startTime;
     isAnimationComplete = elapsedTime >= m_momentumCalculator->animationDuration();
     return m_momentumCalculator->scrollOffsetAfterElapsedTime(elapsedTime);
 }
index 6b6d227..e620ce5 100644 (file)
@@ -75,7 +75,7 @@ public:
             m_activeSnapIndexY = index;
     }
 
-    FloatPoint currentAnimatedScrollOffset(bool& isAnimationComplete) const;
+    FloatPoint currentAnimatedScrollOffset(MonotonicTime, bool& isAnimationComplete) const;
 
     // State transition helpers.
     void transitionToSnapAnimationState(const FloatSize& contentSize, const FloatSize& viewportSize, float pageScale, const FloatPoint& initialOffset);
index 0de81e0..1c6686f 100644 (file)
@@ -77,17 +77,9 @@ ScrollController::~ScrollController()
 
 void ScrollController::stopAllTimers()
 {
-#if ENABLE(RUBBER_BANDING)
-    if (m_snapRubberbandTimer)
-        m_snapRubberbandTimer->stop();
-#endif
-
 #if ENABLE(CSS_SCROLL_SNAP)
     if (m_statelessSnapTransitionTimer)
         m_statelessSnapTransitionTimer->stop();
-
-    if (m_scrollSnapTimer)
-        m_scrollSnapTimer->stop();
 #endif
 
 #if ASSERT_ENABLED
@@ -122,21 +114,22 @@ bool ScrollController::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
         IntSize stretchAmount = m_client.stretchAmount();
         m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width()));
         m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height()));
-        m_overflowScrollDelta = FloatSize();
+        m_overflowScrollDelta = { };
 
-        stopSnapRubberbandTimer();
+        stopSnapRubberbandAnimation();
         updateRubberBandingState();
         return true;
     }
 
     if (wheelEvent.phase() == PlatformWheelEventPhase::Ended) {
+        // FIXME: This triggers the rubberband timer even when we don't start rubberbanding.
         snapRubberBand();
         updateRubberBandingState();
         return true;
     }
 
     bool isMomentumScrollEvent = (wheelEvent.momentumPhase() != PlatformWheelEventPhase::None);
-    if (m_ignoreMomentumScrolls && (isMomentumScrollEvent || m_snapRubberbandTimer)) {
+    if (m_ignoreMomentumScrolls && (isMomentumScrollEvent || m_isAnimatingRubberBand)) {
         if (wheelEvent.momentumPhase() == PlatformWheelEventPhase::Ended) {
             m_ignoreMomentumScrolls = false;
             return true;
@@ -152,7 +145,7 @@ bool ScrollController::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
     float deltaY = m_overflowScrollDelta.height();
 
     // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
-    m_overflowScrollDelta = FloatSize();
+    m_overflowScrollDelta = { };
 
     IntSize stretchAmount = m_client.stretchAmount();
     bool isVerticallyStretched = stretchAmount.height();
@@ -387,15 +380,18 @@ static inline float roundToDevicePixelTowardZero(float num)
     return roundTowardZero(num);
 }
 
-void ScrollController::snapRubberBandTimerFired()
+void ScrollController::updateRubberBandAnimatingState(MonotonicTime currentTime)
 {
+    if (!m_isAnimatingRubberBand)
+        return;
+
     if (isScrollSnapInProgress())
         return;
     
-    LOG_WITH_STREAM(Scrolling, stream << "ScrollController::snapRubberBandTimerFired() - main thread " << isMainThread());
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollController::updateRubberBandAnimatingState() - main thread " << isMainThread());
 
     if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) {
-        auto timeDelta = MonotonicTime::now() - m_startTime;
+        auto timeDelta = currentTime - m_startTime;
 
         if (m_startStretch.isZero()) {
             m_startStretch = m_client.stretchAmount();
@@ -434,10 +430,10 @@ void ScrollController::snapRubberBandTimerFired()
             stopRubberbanding();
         }
     } else {
-        m_startTime = MonotonicTime::now();
+        m_startTime = currentTime;
         m_startStretch = { };
         if (!isRubberBandInProgressInternal())
-            stopSnapRubberbandTimer();
+            stopSnapRubberbandAnimation();
     }
 
     updateRubberBandingState();
@@ -471,7 +467,7 @@ bool ScrollController::isScrollSnapInProgress() const
         return false;
 
 #if ENABLE(CSS_SCROLL_SNAP)
-    if (m_inScrollGesture || m_momentumScrollInProgress || m_scrollSnapTimer)
+    if (m_inScrollGesture || m_momentumScrollInProgress || m_isAnimatingScrollSnap)
         return true;
 #endif
     return false;
@@ -480,7 +476,7 @@ bool ScrollController::isScrollSnapInProgress() const
 void ScrollController::stopRubberbanding()
 {
 #if ENABLE(RUBBER_BANDING)
-    stopSnapRubberbandTimer();
+    stopSnapRubberbandAnimation();
     m_stretchScrollForce = { };
     m_startTime = { };
     m_startStretch = { };
@@ -490,27 +486,20 @@ void ScrollController::stopRubberbanding()
 }
 
 #if ENABLE(RUBBER_BANDING)
-void ScrollController::startSnapRubberbandTimer()
+void ScrollController::startRubberbandAnimation()
 {
     m_client.willStartRubberBandSnapAnimation();
 
-    // Make a new one each time to ensure it fires on the current RunLoop.
-    m_snapRubberbandTimer = m_client.createTimer([this] {
-        snapRubberBandTimerFired();
-    });
-    m_snapRubberbandTimer->startRepeating(1_s / 60.);
+    setIsAnimatingRubberBand(true);
 
     m_client.deferWheelEventTestCompletionForReason(reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(this), WheelEventTestMonitor::RubberbandInProgress);
 }
 
-void ScrollController::stopSnapRubberbandTimer()
+void ScrollController::stopSnapRubberbandAnimation()
 {
     m_client.didStopRubberbandSnapAnimation();
 
-    if (m_snapRubberbandTimer) {
-        m_snapRubberbandTimer->stop();
-        m_snapRubberbandTimer = nullptr;
-    }
+    setIsAnimatingRubberBand(false);
 
     m_client.removeWheelEventTestCompletionDeferralForReason(reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(this), WheelEventTestMonitor::RubberbandInProgress);
 }
@@ -521,14 +510,14 @@ void ScrollController::snapRubberBand()
     if (m_lastMomentumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
         m_momentumVelocity = { };
 
-    if (m_snapRubberbandTimer)
+    if (m_isAnimatingRubberBand)
         return;
 
     m_startTime = MonotonicTime::now();
     m_startStretch = { };
     m_origVelocity = { };
 
-    startSnapRubberbandTimer();
+    startRubberbandAnimation();
 }
 
 bool ScrollController::shouldRubberBandInHorizontalDirection(const PlatformWheelEvent& wheelEvent) const
@@ -547,7 +536,7 @@ bool ScrollController::shouldRubberBandInDirection(ScrollDirection direction) co
 
 bool ScrollController::isRubberBandInProgressInternal() const
 {
-    if (!m_inScrollGesture && !m_momentumScrollInProgress && !m_snapRubberbandTimer)
+    if (!m_inScrollGesture && !m_momentumScrollInProgress && !m_isAnimatingRubberBand)
         return false;
 
     return !m_client.stretchAmount().isZero();
@@ -659,7 +648,7 @@ bool ScrollController::shouldOverrideMomentumScrolling() const
 
 void ScrollController::scheduleStatelessScrollSnap()
 {
-    stopScrollSnapTimer();
+    stopScrollSnapAnimation();
     if (m_statelessSnapTransitionTimer) {
         m_statelessSnapTransitionTimer->stop();
         m_statelessSnapTransitionTimer = nullptr;
@@ -683,7 +672,7 @@ void ScrollController::statelessSnapTransitionTimerFired()
         return;
 
     m_scrollSnapState->transitionToSnapAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset());
-    startScrollSnapTimer();
+    startScrollSnapAnimation();
 }
 
 void ScrollController::startDeferringWheelEventTestCompletionDueToScrollSnapping()
@@ -712,13 +701,13 @@ bool ScrollController::processWheelEventForScrollSnap(const PlatformWheelEvent&
     switch (status) {
     case WheelEventStatus::UserScrollBegin:
     case WheelEventStatus::UserScrolling:
-        stopScrollSnapTimer();
+        stopScrollSnapAnimation();
         m_scrollSnapState->transitionToUserInteractionState();
         m_dragEndedScrollingVelocity = -wheelEvent.scrollingVelocity();
         break;
     case WheelEventStatus::UserScrollEnd:
         m_scrollSnapState->transitionToSnapAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset());
-        startScrollSnapTimer();
+        startScrollSnapAnimation();
         break;
     case WheelEventStatus::MomentumScrollBegin:
         m_scrollSnapState->transitionToGlideAnimationState(m_client.scrollExtent(), m_client.viewportSize(), m_client.pageScaleFactor(), m_client.scrollOffset(), m_dragEndedScrollingVelocity, FloatSize(-wheelEvent.deltaX(), -wheelEvent.deltaY()));
@@ -751,52 +740,51 @@ void ScrollController::updateGestureInProgressState(const PlatformWheelEvent& wh
     updateRubberBandingState();
 }
 
-void ScrollController::startScrollSnapTimer()
+void ScrollController::startScrollSnapAnimation()
 {
-    if (m_scrollSnapTimer)
+    if (m_isAnimatingScrollSnap)
         return;
 
-    LOG_WITH_STREAM(ScrollSnap, stream << "ScrollController " << this << " startScrollSnapTimer (main thread " << isMainThread() << ")");
+    LOG_WITH_STREAM(ScrollSnap, stream << "ScrollController " << this << " startScrollSnapAnimation (main thread " << isMainThread() << ")");
 
     startDeferringWheelEventTestCompletionDueToScrollSnapping();
     m_client.willStartScrollSnapAnimation();
-    m_scrollSnapTimer = m_client.createTimer([this] {
-        scrollSnapTimerFired();
-    });
-    m_scrollSnapTimer->startRepeating(1_s / 60.);
+    setIsAnimatingScrollSnap(true);
 }
 
-void ScrollController::stopScrollSnapTimer()
+void ScrollController::stopScrollSnapAnimation()
 {
-    if (!m_scrollSnapTimer)
+    if (!m_isAnimatingScrollSnap)
         return;
 
-    LOG_WITH_STREAM(ScrollSnap, stream << "ScrollController " << this << " stopScrollSnapTimer (main thread " << isMainThread() << ")");
+    LOG_WITH_STREAM(ScrollSnap, stream << "ScrollController " << this << " stopScrollSnapAnimation (main thread " << isMainThread() << ")");
 
     stopDeferringWheelEventTestCompletionDueToScrollSnapping();
     m_client.didStopScrollSnapAnimation();
 
-    m_scrollSnapTimer->stop();
-    m_scrollSnapTimer = nullptr;
+    setIsAnimatingScrollSnap(false);
 }
 
-void ScrollController::scrollSnapTimerFired()
+void ScrollController::updateScrollSnapAnimatingState(MonotonicTime currentTime)
 {
+    if (!m_isAnimatingScrollSnap)
+        return;
+
     if (!usesScrollSnap()) {
         ASSERT_NOT_REACHED();
         return;
     }
 
     bool isAnimationComplete;
-    auto animationOffset = m_scrollSnapState->currentAnimatedScrollOffset(isAnimationComplete);
+    auto animationOffset = m_scrollSnapState->currentAnimatedScrollOffset(currentTime, isAnimationComplete);
     auto currentOffset = m_client.scrollOffset();
 
-    LOG_WITH_STREAM(ScrollSnap, stream << "ScrollController " << this << " scrollSnapTimerFired - isAnimationComplete " << isAnimationComplete << " currentOffset " << currentOffset << " (main thread " << isMainThread() << ")");
+    LOG_WITH_STREAM(ScrollSnap, stream << "ScrollController " << this << " updateScrollSnapAnimatingState - isAnimationComplete " << isAnimationComplete << " currentOffset " << currentOffset << " (main thread " << isMainThread() << ")");
 
     m_client.immediateScrollByWithoutContentEdgeConstraints(FloatSize(animationOffset.x() - currentOffset.x(), animationOffset.y() - currentOffset.y()));
     if (isAnimationComplete) {
         m_scrollSnapState->transitionToDestinationReachedState();
-        stopScrollSnapTimer();
+        stopScrollSnapAnimation();
     }
 }
 #endif // ENABLE(CSS_SCROLL_SNAP)