author | Robert O'Callahan <robert@ocallahan.org> |
Fri, 16 Jul 2010 09:08:05 +1200 | |
changeset 47750 | 9cc11c490ec2d9a9faf58dc003208077e509fafb |
parent 47749 | 1fa38a238c90bf57bfe87720575f63ccd9a61cfe |
child 47751 | e724a4858e54fd79beafdb3195f55db206b859ea |
push id | 14413 |
push user | rocallahan@mozilla.com |
push date | Thu, 15 Jul 2010 21:12:02 +0000 |
treeherder | autoland@e1d7fd5255fd [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mats |
bugs | 564991 |
milestone | 2.0b2pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1267,16 +1267,36 @@ IsSmoothScrollingEnabled() nsresult rv = prefs->GetBoolPref(SMOOTH_SCROLL_PREF_NAME, &enabled); if (NS_SUCCEEDED(rv)) { return enabled; } } return PR_FALSE; } +class ScrollFrameActivityTracker : public nsExpirationTracker<nsGfxScrollFrameInner,4> { +public: + // Wait for 75-100ms between scrolls before we switch the appearance back to + // subpixel AA. That's 4 generations of 25ms each. + enum { TIMEOUT_MS = 25 }; + ScrollFrameActivityTracker() + : nsExpirationTracker<nsGfxScrollFrameInner,4>(TIMEOUT_MS) {} + ~ScrollFrameActivityTracker() { + AgeAllGenerations(); + } + + virtual void NotifyExpired(nsGfxScrollFrameInner *aObject) { + RemoveObject(aObject); + aObject->mScrollingActive = PR_FALSE; + aObject->mOuter->InvalidateOverflowRect(); + } +}; + +static ScrollFrameActivityTracker *gScrollFrameActivityTracker = nsnull; + nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter, PRBool aIsRoot, PRBool aIsXUL) : mHScrollbarBox(nsnull), mVScrollbarBox(nsnull), mScrolledFrame(nsnull), mScrollCornerBox(nsnull), mOuter(aOuter), @@ -1302,16 +1322,24 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI mMayHaveDirtyFixedChildren(PR_FALSE), mUpdateScrollbarAttributes(PR_FALSE), mScrollingActive(PR_FALSE) { } nsGfxScrollFrameInner::~nsGfxScrollFrameInner() { + if (mActivityExpirationState.IsTracked()) { + gScrollFrameActivityTracker->RemoveObject(this); + } + if (gScrollFrameActivityTracker && + gScrollFrameActivityTracker->IsEmpty()) { + delete gScrollFrameActivityTracker; + gScrollFrameActivityTracker = nsnull; + } delete mAsyncScroll; } static nscoord Clamp(nscoord aLower, nscoord aVal, nscoord aUpper) { if (aVal < aLower) return aLower; @@ -1557,16 +1585,51 @@ InvalidateFixedBackgroundFrames(nsIFrame return; nsRegion visibleRegion(aUpdateRect); list.ComputeVisibility(&builder, &visibleRegion, nsnull); InvalidateFixedBackgroundFramesFromList(&builder, list); } +PRBool nsGfxScrollFrameInner::IsAlwaysActive() const +{ + // The root scrollframe for a non-chrome document which is the direct + // child of a chrome document is always treated as "active". + if (!mIsRoot) + return PR_FALSE; + nsPresContext* presContext = mOuter->PresContext(); + if (presContext->IsChrome()) + return PR_FALSE; + nsIFrame* rootFrame = mOuter->PresContext()->PresShell()->GetRootFrame(); + nsIFrame* rootParent = nsLayoutUtils::GetCrossDocParentFrame(rootFrame); + return !rootParent || rootParent->PresContext()->IsChrome(); +} + +PRBool nsGfxScrollFrameInner::IsScrollingActive() const +{ + return mScrollingActive || IsAlwaysActive(); +} + +void nsGfxScrollFrameInner::MarkActive() +{ + if (IsAlwaysActive()) + return; + + mScrollingActive = PR_TRUE; + if (mActivityExpirationState.IsTracked()) { + gScrollFrameActivityTracker->MarkUsed(this); + } else { + if (!gScrollFrameActivityTracker) { + gScrollFrameActivityTracker = new ScrollFrameActivityTracker(); + } + gScrollFrameActivityTracker->AddObject(this); + } +} + void nsGfxScrollFrameInner::ScrollVisual(nsIntPoint aPixDelta) { nsRootPresContext* rootPresContext = mOuter->PresContext()->GetRootPresContext(); if (!rootPresContext) { return; } @@ -1590,20 +1653,20 @@ void nsGfxScrollFrameInner::ScrollVisual if (nearestWidget) { nearestWidget->ConfigureChildren(configurations); } AdjustViewsAndWidgets(mScrolledFrame, PR_FALSE); // We need to call this after fixing up the widget and view positions // to be consistent with the view and frame hierarchy. PRUint32 flags = nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT; nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(mOuter); - if (CanScrollWithBlitting(mOuter, displayRoot) && mScrollingActive) { + if (IsScrollingActive() && CanScrollWithBlitting(mOuter, displayRoot)) { flags |= nsIFrame::INVALIDATE_NO_THEBES_LAYERS; } - mScrollingActive = PR_TRUE; + MarkActive(); mOuter->InvalidateWithFlags(mScrollPort, flags); if (flags & nsIFrame::INVALIDATE_NO_THEBES_LAYERS) { // XXX fix this to transform rectangle properly InvalidateFixedBackgroundFrames(displayRoot, mScrolledFrame, mScrolledFrame->GetRect() + mOuter->GetOffsetTo(displayRoot)); } }
--- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -49,16 +49,17 @@ #include "nsThreadUtils.h" #include "nsIReflowCallback.h" #include "nsBoxLayoutState.h" #include "nsQueryFrame.h" #include "nsCOMArray.h" #ifdef MOZ_SVG #include "nsSVGIntegrationUtils.h" #endif +#include "nsExpirationTracker.h" class nsPresContext; class nsIPresShell; class nsIContent; class nsIAtom; class nsIDocument; class nsIScrollFrameInternal; class nsPresState; @@ -208,30 +209,34 @@ public: PRUint32 GetScrollbarVisibility() const { return (mHasVerticalScrollbar ? nsIScrollableFrame::VERTICAL : 0) | (mHasHorizontalScrollbar ? nsIScrollableFrame::HORIZONTAL : 0); } nsMargin GetActualScrollbarSizes() const; nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState); PRBool IsLTR() const; PRBool IsScrollbarOnRight() const; - PRBool IsScrollingActive() { return mScrollingActive; } + PRBool IsScrollingActive() const; // adjust the scrollbar rectangle aRect to account for any visible resizer. // aHasResizer specifies if there is a content resizer, however this method // will also check if a widget resizer is present as well. void AdjustScrollbarRectForResizer(nsIFrame* aFrame, nsPresContext* aPresContext, nsRect& aRect, PRBool aHasResizer, PRBool aVertical); // returns true if a resizer should be visible PRBool HasResizer() { return mScrollCornerContent && mScrollCornerContent->Tag() == nsGkAtoms::resizer; } void LayoutScrollbars(nsBoxLayoutState& aState, const nsRect& aContentArea, const nsRect& aOldScrollArea); + PRBool IsAlwaysActive() const; + void MarkActive(); + nsExpirationState* GetExpirationState() { return &mActivityExpirationState; } + // owning references to the nsIAnonymousContentCreator-built content nsCOMPtr<nsIContent> mHScrollbarContent; nsCOMPtr<nsIContent> mVScrollbarContent; nsCOMPtr<nsIContent> mScrollCornerContent; nsRevocableEventPtr<ScrollEvent> mScrollEvent; nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent; nsRevocableEventPtr<ScrolledAreaEvent> mScrolledAreaEvent; @@ -248,16 +253,18 @@ public: // just the current scroll position. ScrollBy will choose its // destination based on this value. nsPoint mDestination; nsPoint mScrollPosAtLastPaint; nsPoint mRestorePos; nsPoint mLastPos; + nsExpirationState mActivityExpirationState; + PRPackedBool mNeverHasVerticalScrollbar:1; PRPackedBool mNeverHasHorizontalScrollbar:1; PRPackedBool mHasVerticalScrollbar:1; PRPackedBool mHasHorizontalScrollbar:1; PRPackedBool mFrameIsUpdatingScrollbar:1; PRPackedBool mDidHistoryRestore:1; // Is this the scrollframe for the document's viewport? PRPackedBool mIsRoot:1;
--- a/xpcom/ds/nsExpirationTracker.h +++ b/xpcom/ds/nsExpirationTracker.h @@ -254,22 +254,30 @@ template <class T, PRUint32 K> class nsE mIndex = 0; } return nsnull; } }; friend class Iterator; + PRBool IsEmpty() { + for (PRUint32 i = 0; i < K; ++i) { + if (!mGenerations[i].IsEmpty()) + return PR_FALSE; + } + return PR_TRUE; + } + protected: /** * This must be overridden to catch notifications. It is called whenever * we detect that an object has not been used for at least (K-1)*mTimerPeriod - * seconds. If timer events are not delayed, it will be called within - * roughly K*mTimerPeriod seconds after the last use. (Unless AgeOneGeneration + * milliseconds. If timer events are not delayed, it will be called within + * roughly K*mTimerPeriod milliseconds after the last use. (Unless AgeOneGeneration * or AgeAllGenerations have been called to accelerate the aging process.) * * NOTE: These bounds ignore delays in timer firings due to actual work being * performed by the browser. We use a slack timer so there is always at least * mTimerPeriod milliseconds between firings, which gives us (K-1)*mTimerPeriod * as a pretty solid lower bound. The upper bound is rather loose, however. * If the maximum amount by which any given timer firing is delayed is D, then * the upper bound before NotifyExpired is called is K*(mTimerPeriod + D). @@ -293,23 +301,20 @@ template <class T, PRUint32 K> class nsE PRUint32 mTimerPeriod; PRUint32 mNewestGeneration; PRPackedBool mInAgeOneGeneration; static void TimerCallback(nsITimer* aTimer, void* aThis) { nsExpirationTracker* tracker = static_cast<nsExpirationTracker*>(aThis); tracker->AgeOneGeneration(); // Cancel the timer if we have no objects to track - PRUint32 i; - for (i = 0; i < K; ++i) { - if (!tracker->mGenerations[i].IsEmpty()) - return; + if (tracker->IsEmpty()) { + tracker->mTimer->Cancel(); + tracker->mTimer = nsnull; } - tracker->mTimer->Cancel(); - tracker->mTimer = nsnull; } nsresult CheckStartTimer() { if (mTimer || !mTimerPeriod) return NS_OK; mTimer = do_CreateInstance("@mozilla.org/timer;1"); if (!mTimer) return NS_ERROR_OUT_OF_MEMORY;