Bug 564991. Part 22: Mark scrolled elements as inactive after a timeout. r=mats

This commit is contained in:
Robert O'Callahan 2010-07-16 09:08:05 +12:00
Родитель 2eeaed4ebb
Коммит 06e139fe2b
3 изменённых файлов: 86 добавлений и 11 удалений

Просмотреть файл

@ -1272,6 +1272,26 @@ IsSmoothScrollingEnabled()
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)
@ -1307,6 +1327,14 @@ nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter,
nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
{
if (mActivityExpirationState.IsTracked()) {
gScrollFrameActivityTracker->RemoveObject(this);
}
if (gScrollFrameActivityTracker &&
gScrollFrameActivityTracker->IsEmpty()) {
delete gScrollFrameActivityTracker;
gScrollFrameActivityTracker = nsnull;
}
delete mAsyncScroll;
}
@ -1562,6 +1590,41 @@ InvalidateFixedBackgroundFrames(nsIFrame* aRootFrame,
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 =
@ -1595,10 +1658,10 @@ void nsGfxScrollFrameInner::ScrollVisual(nsIntPoint aPixDelta)
// 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) {

Просмотреть файл

@ -54,6 +54,7 @@
#ifdef MOZ_SVG
#include "nsSVGIntegrationUtils.h"
#endif
#include "nsExpirationTracker.h"
class nsPresContext;
class nsIPresShell;
@ -213,7 +214,7 @@ public:
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.
@ -227,6 +228,10 @@ public:
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;
@ -253,6 +258,8 @@ public:
nsPoint mRestorePos;
nsPoint mLastPos;
nsExpirationState mActivityExpirationState;
PRPackedBool mNeverHasVerticalScrollbar:1;
PRPackedBool mNeverHasHorizontalScrollbar:1;
PRPackedBool mHasVerticalScrollbar:1;

Просмотреть файл

@ -259,12 +259,20 @@ template <class T, PRUint32 K> class nsExpirationTracker {
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
@ -298,13 +306,10 @@ template <class T, PRUint32 K> class nsExpirationTracker {
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() {