From 03341a571099fd3d28bb514376c2620ce5add9cb Mon Sep 17 00:00:00 2001 From: "Avi Halachmi (:avih)" Date: Sat, 31 Mar 2012 16:08:00 +0300 Subject: [PATCH] Bug 702463: Smooth scroll - Use refresh observer instead of a timer. r=roc --- layout/generic/nsGfxScrollFrame.cpp | 87 +++++++++++++++++++---------- layout/generic/nsGfxScrollFrame.h | 4 +- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 57310d47512..9ef69fd429e 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1312,17 +1312,19 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) const double kCurrentVelocityWeighting = 0.25; const double kStopDecelerationWeighting = 0.4; -class nsGfxScrollFrameInner::AsyncScroll { +// AsyncScroll has ref counting. +class nsGfxScrollFrameInner::AsyncScroll : public nsARefreshObserver { public: typedef mozilla::TimeStamp TimeStamp; typedef mozilla::TimeDuration TimeDuration; - AsyncScroll(): - mIsFirstIteration(true) - {} + AsyncScroll() + : mIsFirstIteration(true) + , mCallee(nsnull) + {} ~AsyncScroll() { - if (mScrollTimer) mScrollTimer->Cancel(); + RemoveObserver(); } nsPoint PositionAt(TimeStamp aTime); @@ -1336,7 +1338,6 @@ public: return aTime > mStartTime + mDuration; // XXX or if we've hit the wall } - nsCOMPtr mScrollTimer; TimeStamp mStartTime; // mPrevStartTime holds previous 3 timestamps for intervals averaging (to @@ -1382,6 +1383,51 @@ protected: nscoord aDestination); void InitDuration(nsIAtom *aOrigin); + +// The next section is observer/callback management +// Bodies of WillRefresh and RefreshDriver contain nsGfxScrollFrameInner specific code. +public: + NS_INLINE_DECL_REFCOUNTING(AsyncScroll) + + /* + * Set a refresh observer for smooth scroll iterations (and start observing). + * Should be used at most once during the lifetime of this object. + * Return value: true on success, false otherwise. + */ + bool SetRefreshObserver(nsGfxScrollFrameInner *aCallee) { + NS_ASSERTION(aCallee && !mCallee, "AsyncScroll::SetRefreshObserver - Invalid usage."); + + if (!RefreshDriver(aCallee)->AddRefreshObserver(this, Flush_Display)) { + return false; + } + + mCallee = aCallee; + return true; + } + + virtual void WillRefresh(mozilla::TimeStamp aTime) { + // The callback may release "this". + // We don't access members after returning, so no need for KungFuDeathGrip. + nsGfxScrollFrameInner::AsyncScrollCallback(mCallee, aTime); + } + +private: + nsGfxScrollFrameInner *mCallee; + + nsRefreshDriver* RefreshDriver(nsGfxScrollFrameInner* aCallee) { + return aCallee->mOuter->PresContext()->RefreshDriver(); + } + + /* + * The refresh driver doesn't hold a reference to its observers, + * so releasing this object can (and is) used to remove the observer on DTOR. + * Currently, this object is released once the scrolling ends. + */ + void RemoveObserver() { + if (mCallee) { + RefreshDriver(mCallee)->RemoveRefreshObserver(this, Flush_Display); + } + } }; nsPoint @@ -1595,7 +1641,6 @@ nsGfxScrollFrameInner::~nsGfxScrollFrameInner() delete gScrollFrameActivityTracker; gScrollFrameActivityTracker = nsnull; } - delete mAsyncScroll; if (mScrollActivityTimer) { mScrollActivityTimer->Cancel(); @@ -1622,28 +1667,23 @@ nsGfxScrollFrameInner::ClampScrollPosition(const nsPoint& aPt) const } /* - * Callback function from timer used in nsGfxScrollFrameInner::ScrollTo + * Callback function from AsyncScroll, used in nsGfxScrollFrameInner::ScrollTo */ void -nsGfxScrollFrameInner::AsyncScrollCallback(nsITimer *aTimer, void* anInstance) +nsGfxScrollFrameInner::AsyncScrollCallback(void* anInstance, mozilla::TimeStamp aTime) { nsGfxScrollFrameInner* self = static_cast(anInstance); if (!self || !self->mAsyncScroll) return; if (self->mAsyncScroll->mIsSmoothScroll) { - TimeStamp now = TimeStamp::Now(); - nsPoint destination = self->mAsyncScroll->PositionAt(now); - if (self->mAsyncScroll->IsFinished(now)) { - delete self->mAsyncScroll; + nsPoint destination = self->mAsyncScroll->PositionAt(aTime); + if (self->mAsyncScroll->IsFinished(aTime)) { self->mAsyncScroll = nsnull; } - self->ScrollToImpl(destination); } else { - delete self->mAsyncScroll; self->mAsyncScroll = nsnull; - self->ScrollToImpl(self->mDestination); } } @@ -1666,7 +1706,6 @@ nsGfxScrollFrameInner::ScrollToWithOrigin(nsPoint aScrollPosition, if (aMode == nsIScrollableFrame::INSTANT) { // Asynchronous scrolling is not allowed, so we'll kill any existing // async-scrolling process and do an instant scroll - delete mAsyncScroll; mAsyncScroll = nsnull; ScrollToImpl(mDestination); return; @@ -1685,22 +1724,12 @@ nsGfxScrollFrameInner::ScrollToWithOrigin(nsPoint aScrollPosition, } } else { mAsyncScroll = new AsyncScroll; - mAsyncScroll->mScrollTimer = do_CreateInstance("@mozilla.org/timer;1"); - if (!mAsyncScroll->mScrollTimer) { - delete mAsyncScroll; + if (!mAsyncScroll->SetRefreshObserver(this)) { mAsyncScroll = nsnull; - // allocation failed. Scroll the normal way. + // Observer setup failed. Scroll the normal way. ScrollToImpl(mDestination); return; } - if (isSmoothScroll) { - mAsyncScroll->mScrollTimer->InitWithFuncCallback( - AsyncScrollCallback, this, 1000 / 60, - nsITimer::TYPE_REPEATING_SLACK); - } else { - mAsyncScroll->mScrollTimer->InitWithFuncCallback( - AsyncScrollCallback, this, 0, nsITimer::TYPE_ONE_SHOT); - } } mAsyncScroll->mIsSmoothScroll = isSmoothScroll; diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 75a551f0c02..ede907aadbb 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -179,7 +179,7 @@ public: nsPoint RestrictToDevPixels(const nsPoint& aPt, nsIntPoint* aPtDevPx, bool aShouldClamp) const; nsPoint ClampScrollPosition(const nsPoint& aPt) const; - static void AsyncScrollCallback(nsITimer *aTimer, void* anInstance); + static void AsyncScrollCallback(void* anInstance, mozilla::TimeStamp aTime); void ScrollTo(nsPoint aScrollPosition, nsIScrollableFrame::ScrollMode aMode) { ScrollToWithOrigin(aScrollPosition, aMode, nsGkAtoms::other); }; @@ -288,7 +288,7 @@ public: nsIBox* mScrollCornerBox; nsIBox* mResizerBox; nsContainerFrame* mOuter; - AsyncScroll* mAsyncScroll; + nsRefPtr mAsyncScroll; nsTArray mListeners; nsRect mScrollPort; // Where we're currently scrolling to, if we're scrolling asynchronously.