Bug 702463 - Smooth scrolling should use refresh observer instead of a timer. r=roc

This commit is contained in:
Avi Halachmi 2012-03-31 16:08:00 +03:00
parent dffd8d682c
commit 3ecca78724
2 changed files with 60 additions and 31 deletions

View File

@ -1333,17 +1333,19 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
const double kCurrentVelocityWeighting = 0.25; const double kCurrentVelocityWeighting = 0.25;
const double kStopDecelerationWeighting = 0.4; const double kStopDecelerationWeighting = 0.4;
class nsGfxScrollFrameInner::AsyncScroll { // AsyncScroll has ref counting.
class nsGfxScrollFrameInner::AsyncScroll : public nsARefreshObserver {
public: public:
typedef mozilla::TimeStamp TimeStamp; typedef mozilla::TimeStamp TimeStamp;
typedef mozilla::TimeDuration TimeDuration; typedef mozilla::TimeDuration TimeDuration;
AsyncScroll(): AsyncScroll()
mIsFirstIteration(true) : mIsFirstIteration(true)
{} , mCallee(nsnull)
{}
~AsyncScroll() { ~AsyncScroll() {
if (mScrollTimer) mScrollTimer->Cancel(); RemoveObserver();
} }
nsPoint PositionAt(TimeStamp aTime); nsPoint PositionAt(TimeStamp aTime);
@ -1357,7 +1359,6 @@ public:
return aTime > mStartTime + mDuration; // XXX or if we've hit the wall return aTime > mStartTime + mDuration; // XXX or if we've hit the wall
} }
nsCOMPtr<nsITimer> mScrollTimer;
TimeStamp mStartTime; TimeStamp mStartTime;
// mPrevStartTime holds previous 3 timestamps for intervals averaging (to // mPrevStartTime holds previous 3 timestamps for intervals averaging (to
@ -1403,6 +1404,51 @@ protected:
nscoord aDestination); nscoord aDestination);
void InitDuration(nsIAtom *aOrigin); 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 nsPoint
@ -1616,7 +1662,6 @@ nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
delete gScrollFrameActivityTracker; delete gScrollFrameActivityTracker;
gScrollFrameActivityTracker = nsnull; gScrollFrameActivityTracker = nsnull;
} }
delete mAsyncScroll;
if (mScrollActivityTimer) { if (mScrollActivityTimer) {
mScrollActivityTimer->Cancel(); mScrollActivityTimer->Cancel();
@ -1651,28 +1696,23 @@ nsGfxScrollFrameInner::ClampScrollPosition(const nsPoint& aPt) const
} }
/* /*
* Callback function from timer used in nsGfxScrollFrameInner::ScrollTo * Callback function from AsyncScroll, used in nsGfxScrollFrameInner::ScrollTo
*/ */
void void
nsGfxScrollFrameInner::AsyncScrollCallback(nsITimer *aTimer, void* anInstance) nsGfxScrollFrameInner::AsyncScrollCallback(void* anInstance, mozilla::TimeStamp aTime)
{ {
nsGfxScrollFrameInner* self = static_cast<nsGfxScrollFrameInner*>(anInstance); nsGfxScrollFrameInner* self = static_cast<nsGfxScrollFrameInner*>(anInstance);
if (!self || !self->mAsyncScroll) if (!self || !self->mAsyncScroll)
return; return;
if (self->mAsyncScroll->mIsSmoothScroll) { if (self->mAsyncScroll->mIsSmoothScroll) {
TimeStamp now = TimeStamp::Now(); nsPoint destination = self->mAsyncScroll->PositionAt(aTime);
nsPoint destination = self->mAsyncScroll->PositionAt(now); if (self->mAsyncScroll->IsFinished(aTime)) {
if (self->mAsyncScroll->IsFinished(now)) {
delete self->mAsyncScroll;
self->mAsyncScroll = nsnull; self->mAsyncScroll = nsnull;
} }
self->ScrollToImpl(destination); self->ScrollToImpl(destination);
} else { } else {
delete self->mAsyncScroll;
self->mAsyncScroll = nsnull; self->mAsyncScroll = nsnull;
self->ScrollToImpl(self->mDestination); self->ScrollToImpl(self->mDestination);
} }
} }
@ -1695,7 +1735,6 @@ nsGfxScrollFrameInner::ScrollToWithOrigin(nsPoint aScrollPosition,
if (aMode == nsIScrollableFrame::INSTANT) { if (aMode == nsIScrollableFrame::INSTANT) {
// Asynchronous scrolling is not allowed, so we'll kill any existing // Asynchronous scrolling is not allowed, so we'll kill any existing
// async-scrolling process and do an instant scroll // async-scrolling process and do an instant scroll
delete mAsyncScroll;
mAsyncScroll = nsnull; mAsyncScroll = nsnull;
ScrollToImpl(mDestination); ScrollToImpl(mDestination);
return; return;
@ -1714,22 +1753,12 @@ nsGfxScrollFrameInner::ScrollToWithOrigin(nsPoint aScrollPosition,
} }
} else { } else {
mAsyncScroll = new AsyncScroll; mAsyncScroll = new AsyncScroll;
mAsyncScroll->mScrollTimer = do_CreateInstance("@mozilla.org/timer;1"); if (!mAsyncScroll->SetRefreshObserver(this)) {
if (!mAsyncScroll->mScrollTimer) {
delete mAsyncScroll;
mAsyncScroll = nsnull; mAsyncScroll = nsnull;
// allocation failed. Scroll the normal way. // Observer setup failed. Scroll the normal way.
ScrollToImpl(mDestination); ScrollToImpl(mDestination);
return; 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; mAsyncScroll->mIsSmoothScroll = isSmoothScroll;

View File

@ -186,7 +186,7 @@ protected:
public: public:
nsPoint RestrictToDevPixels(const nsPoint& aPt, nsIntPoint* aPtDevPx, bool aShouldClamp) const; nsPoint RestrictToDevPixels(const nsPoint& aPt, nsIntPoint* aPtDevPx, bool aShouldClamp) const;
nsPoint ClampScrollPosition(const nsPoint& aPt) 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) { void ScrollTo(nsPoint aScrollPosition, nsIScrollableFrame::ScrollMode aMode) {
ScrollToWithOrigin(aScrollPosition, aMode, nsGkAtoms::other); ScrollToWithOrigin(aScrollPosition, aMode, nsGkAtoms::other);
}; };
@ -295,7 +295,7 @@ public:
nsIBox* mScrollCornerBox; nsIBox* mScrollCornerBox;
nsIBox* mResizerBox; nsIBox* mResizerBox;
nsContainerFrame* mOuter; nsContainerFrame* mOuter;
AsyncScroll* mAsyncScroll; nsRefPtr<AsyncScroll> mAsyncScroll;
nsTArray<nsIScrollPositionListener*> mListeners; nsTArray<nsIScrollPositionListener*> mListeners;
nsRect mScrollPort; nsRect mScrollPort;
// Where we're currently scrolling to, if we're scrolling asynchronously. // Where we're currently scrolling to, if we're scrolling asynchronously.