Bug 577607 part 2. Teach the refresh driver to flush restyles and reflow on more than one presshell. r=roc

This commit is contained in:
Boris Zbarsky 2010-08-11 17:05:27 -04:00
parent d9188b16ef
commit 657e764de8
7 changed files with 80 additions and 80 deletions

View File

@ -171,6 +171,8 @@ RestyleTracker::ProcessRestyles()
// processing restyles
mFrameConstructor->BeginUpdate();
mFrameConstructor->mInStyleRefresh = PR_TRUE;
// loop so that we process any restyle events generated by processing
while (mPendingRestyles.Count()) {
if (mHaveLaterSiblingRestyles) {

View File

@ -1374,9 +1374,6 @@ MoveChildrenTo(nsPresContext* aPresContext,
//----------------------------------------------------------------------
NS_IMPL_ADDREF(nsCSSFrameConstructor)
NS_IMPL_RELEASE(nsCSSFrameConstructor)
nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
nsIPresShell *aPresShell)
: mDocument(aDocument)
@ -1395,7 +1392,6 @@ nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
, mHasRootAbsPosContainingBlock(PR_FALSE)
, mObservingRefreshDriver(PR_FALSE)
, mInStyleRefresh(PR_FALSE)
, mInLazyFCRefresh(PR_FALSE)
, mHoverGeneration(0)
, mRebuildAllExtraHint(nsChangeHint(0))
, mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
@ -6360,8 +6356,6 @@ void nsCSSFrameConstructor::CreateNeededFrames()
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"Someone forgot a script blocker");
mInLazyFCRefresh = PR_FALSE;
Element* rootElement = mDocument->GetRootElement();
NS_ASSERTION(!rootElement || !rootElement->HasFlag(NODE_NEEDS_FRAME),
"root element should not have frame created lazily");
@ -8284,12 +8278,11 @@ nsCSSFrameConstructor::WillDestroyFrameTree()
mQuoteList.Clear();
mCounterManager.Clear();
// Remove ourselves as a refresh observer, so the refresh driver
// won't assert about us. But leave mObservingRefreshDriver true so
// we don't readd to it even if someone tries to post restyle events
// on us from this point on for some reason.
// Remove our presshell as a style flush observer. But leave
// mObservingRefreshDriver true so we don't readd to it even if someone tries
// to post restyle events on us from this point on for some reason.
mPresShell->GetPresContext()->RefreshDriver()->
RemoveRefreshObserver(this, Flush_Style);
RemoveStyleFlushObserver(mPresShell);
}
//STATIC
@ -11673,26 +11666,13 @@ nsCSSFrameConstructor::PostRestyleEventInternal(PRBool aForLazyConstruction)
// Make sure we're not in a style refresh; if we are, we still have
// a call to ProcessPendingRestyles coming and there's no need to
// add ourselves as a refresh observer until then.
PRBool inRefresh = aForLazyConstruction ? mInLazyFCRefresh : mInStyleRefresh;
PRBool inRefresh = !aForLazyConstruction && mInStyleRefresh;
if (!mObservingRefreshDriver && !inRefresh) {
mObservingRefreshDriver = mPresShell->AddRefreshObserver(this, Flush_Style);
mObservingRefreshDriver = mPresShell->GetPresContext()->RefreshDriver()->
AddStyleFlushObserver(mPresShell);
}
}
void
nsCSSFrameConstructor::WillRefresh(mozilla::TimeStamp aTime)
{
NS_ASSERTION(mObservingRefreshDriver, "How did we get here?");
// Stop observing the refresh driver and flag ourselves as being in
// a refresh so we don't restart due to animation-triggered
// restyles. The actual work of processing our restyles will get
// done when the refresh driver flushes styles.
mPresShell->RemoveRefreshObserver(this, Flush_Style);
mObservingRefreshDriver = PR_FALSE;
mInLazyFCRefresh = PR_TRUE;
mInStyleRefresh = PR_TRUE;
}
void
nsCSSFrameConstructor::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
{

View File

@ -52,7 +52,6 @@
#include "nsThreadUtils.h"
#include "nsPageContentFrame.h"
#include "nsCSSPseudoElements.h"
#include "nsRefreshDriver.h"
#include "RestyleTracker.h"
class nsIDocument;
@ -72,6 +71,7 @@ class ChildIterator;
class nsICSSAnonBoxPseudo;
class nsPageContentFrame;
struct PendingBinding;
class nsRefreshDriver;
typedef void (nsLazyFrameConstructionCallback)
(nsIContent* aContent, nsIFrame* aFrame, void* aArg);
@ -79,8 +79,10 @@ typedef void (nsLazyFrameConstructionCallback)
class nsFrameConstructorState;
class nsFrameConstructorSaveState;
class nsCSSFrameConstructor : public nsARefreshObserver
class nsCSSFrameConstructor
{
friend class nsRefreshDriver;
public:
typedef mozilla::dom::Element Element;
typedef mozilla::css::RestyleTracker RestyleTracker;
@ -90,15 +92,6 @@ public:
NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
}
// Matches signature on nsARefreshObserver. Just like
// NS_DECL_ISUPPORTS, but without the QI part.
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
protected:
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
public:
struct RestyleData;
friend struct RestyleData;
@ -351,9 +344,6 @@ public:
{
PostRestyleEventCommon(aElement, aRestyleHint, aMinChangeHint, PR_TRUE);
}
// nsARefreshObserver
virtual void WillRefresh(mozilla::TimeStamp aTime);
private:
/**
* Notify the frame constructor that an element needs to have its
@ -1871,9 +1861,6 @@ private:
PRPackedBool mObservingRefreshDriver : 1;
// True if we're in the middle of a nsRefreshDriver refresh
PRPackedBool mInStyleRefresh : 1;
// True if we're in the middle of a nsRefreshDriver refresh and haven't yet
// called CreateNeededFrames
PRPackedBool mInLazyFCRefresh : 1;
PRUint32 mHoverGeneration;
nsChangeHint mRebuildAllExtraHint;

View File

@ -1093,6 +1093,8 @@ public:
static void ReleaseStatics();
protected:
friend class nsRefreshDriver;
// IMPORTANT: The ownership implicit in the following member variables
// has been explicitly checked. If you add any members to this class,
// please make the ownership explicit (pinkerton, scc).
@ -1102,7 +1104,7 @@ protected:
nsIDocument* mDocument; // [STRONG]
nsPresContext* mPresContext; // [STRONG]
nsStyleSet* mStyleSet; // [OWNS]
nsCSSFrameConstructor* mFrameConstructor; // [STRONG]
nsCSSFrameConstructor* mFrameConstructor; // [OWNS]
nsIViewManager* mViewManager; // [WEAK] docViewer owns it so I don't have to
nsFrameSelection* mSelection;
nsFrameManagerBase mFrameManager; // [OWNS]
@ -1140,6 +1142,13 @@ protected:
PRPackedBool mObservesMutationsForPrint;
PRPackedBool mReflowScheduled; // If true, we have a reflow
// scheduled. Guaranteed to be
// false if mReflowContinueTimer
// is non-null.
PRPackedBool mSuppressInterruptibleReflows;
// A list of weak frames. This is a pointer to the last item in the list.
nsWeakFrame* mWeakFrames;

View File

@ -673,7 +673,7 @@ struct nsCallbackEventRequest
#define ASSERT_REFLOW_SCHEDULED_STATE() \
NS_ASSERTION(mReflowScheduled == \
GetPresContext()->RefreshDriver()-> \
IsRefreshObserver(this, Flush_Layout), "Unexpected state")
IsLayoutFlushObserver(this), "Unexpected state")
class nsPresShellEventCB;
class nsAutoCauseReflowNotifier;
@ -681,8 +681,7 @@ class nsAutoCauseReflowNotifier;
class PresShell : public nsIPresShell, public nsIViewObserver,
public nsStubDocumentObserver,
public nsISelectionController, public nsIObserver,
public nsSupportsWeakReference,
public nsARefreshObserver
public nsSupportsWeakReference
{
public:
PresShell();
@ -913,9 +912,6 @@ public:
NS_DECL_NSIOBSERVER
// nsARefreshObserver
virtual void WillRefresh(mozilla::TimeStamp aTime);
#ifdef MOZ_REFLOW_PERF
virtual NS_HIDDEN_(void) DumpReflows();
virtual NS_HIDDEN_(void) CountReflows(const char * aName, nsIFrame * aFrame);
@ -1217,16 +1213,11 @@ protected:
nsCallbackEventRequest* mFirstCallbackEventRequest;
nsCallbackEventRequest* mLastCallbackEventRequest;
PRPackedBool mSuppressInterruptibleReflows;
PRPackedBool mIsDocumentGone; // We've been disconnected from the document.
// We will refuse to paint the document until either
// (a) our timer fires or (b) all frames are constructed.
PRPackedBool mShouldUnsuppressPainting; // Indicates that it is safe to unlock painting once all pending
// reflows have been processed.
PRPackedBool mReflowScheduled; // If true, we have a reflow scheduled.
// Guaranteed to be false if
// mReflowContinueTimer is non-null.
nsCOMPtr<nsITimer> mPaintSuppressionTimer; // This timer controls painting suppression. Until it fires
// or all frames are constructed, we won't paint anything but
// our <body> background and scrollbars.
@ -1645,7 +1636,7 @@ PresShell::~PresShell()
#endif
delete mStyleSet;
NS_IF_RELEASE(mFrameConstructor);
delete mFrameConstructor;
mCurrentEventContent = nsnull;
@ -1694,7 +1685,6 @@ PresShell::Init(nsIDocument* aDocument,
// Create our frame constructor.
mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
NS_ENSURE_TRUE(mFrameConstructor, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(mFrameConstructor);
// The document viewer owns both view manager and pres shell.
mViewManager->SetViewObserver(this);
@ -1942,7 +1932,7 @@ PresShell::Destroy()
// Revoke any pending events. We need to do this and cancel pending reflows
// before we destroy the frame manager, since apparently frame destruction
// sometimes spins the event queue when plug-ins are involved(!).
rd->RemoveRefreshObserver(this, Flush_Layout);
rd->RemoveLayoutFlushObserver(this);
mResizeEvent.Revoke();
if (mAsyncResizeTimerIsActive) {
mAsyncResizeEventTimer->Cancel();
@ -3583,7 +3573,7 @@ PresShell::CancelAllPendingReflows()
mDirtyRoots.Clear();
if (mReflowScheduled) {
GetPresContext()->RefreshDriver()->RemoveRefreshObserver(this, Flush_Layout);
GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
mReflowScheduled = PR_FALSE;
}
@ -7346,20 +7336,6 @@ PresShell::Thaw()
QueryIsActive();
}
void
PresShell::WillRefresh(mozilla::TimeStamp aTime)
{
// Remove ourselves as a refresh observer; we'll readd during the
// flush if needed.
GetPresContext()->RefreshDriver()->RemoveRefreshObserver(this, Flush_Layout);
mReflowScheduled = PR_FALSE;
// Allow interruptible reflows now, since that's what the refresh
// driver will issue.
mSuppressInterruptibleReflows = PR_FALSE;
ASSERT_REFLOW_SCHEDULED_STATE();
}
//--------------------------------------------------------
// Start of protected and private methods on the PresShell
//--------------------------------------------------------
@ -7385,7 +7361,7 @@ PresShell::ScheduleReflow()
NS_PRECONDITION(!mReflowScheduled, "Why are we trying to schedule a reflow?");
ASSERT_REFLOW_SCHEDULED_STATE();
if (GetPresContext()->RefreshDriver()->AddRefreshObserver(this, Flush_Layout)) {
if (GetPresContext()->RefreshDriver()->AddLayoutFlushObserver(this)) {
mReflowScheduled = PR_TRUE;
}

View File

@ -46,6 +46,7 @@
#include "nsComponentManagerUtils.h"
#include "prlog.h"
#include "nsAutoPtr.h"
#include "nsCSSFrameConstructor.h"
/*
* TODO:
@ -140,6 +141,8 @@ nsRefreshDriver::ObserverCount() const
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mObservers); ++i) {
sum += mObservers[i].Length();
}
sum += mStyleFlushObservers.Length();
sum += mLayoutFlushObservers.Length();
return sum;
}
@ -215,13 +218,25 @@ nsRefreshDriver::Notify(nsITimer * /* unused */)
}
if (i == 0) {
// This is the Flush_Style case.
// FIXME: Maybe we should only flush if the WillRefresh calls did
// something? It's probably ok as-is, though, especially as we
// hook up more things here (or to the replacement of this class).
presShell->FlushPendingNotifications(Flush_Style);
while (!mStyleFlushObservers.IsEmpty() &&
mPresContext && mPresContext->GetPresShell()) {
PRUint32 idx = mStyleFlushObservers.Length() - 1;
nsCOMPtr<nsIPresShell> shell = mStyleFlushObservers[idx];
mStyleFlushObservers.RemoveElementAt(idx);
shell->FrameConstructor()->mObservingRefreshDriver = PR_FALSE;
shell->FlushPendingNotifications(Flush_Style);
}
} else if (i == 1) {
// This is the Flush_Layout case.
presShell->FlushPendingNotifications(Flush_InterruptibleLayout);
while (!mLayoutFlushObservers.IsEmpty() &&
mPresContext && mPresContext->GetPresShell()) {
PRUint32 idx = mLayoutFlushObservers.Length() - 1;
nsCOMPtr<nsIPresShell> shell = mLayoutFlushObservers[idx];
mLayoutFlushObservers.RemoveElementAt(idx);
shell->mReflowScheduled = PR_FALSE;
shell->mSuppressInterruptibleReflows = PR_FALSE;
shell->FlushPendingNotifications(Flush_InterruptibleLayout);
}
}
}

View File

@ -48,8 +48,10 @@
#include "nsITimer.h"
#include "nsCOMPtr.h"
#include "nsTObserverArray.h"
#include "nsTArray.h"
class nsPresContext;
class nsIPresShell;
/**
* An abstract base class to be implemented by callers wanting to be
@ -111,6 +113,33 @@ public:
PRBool RemoveRefreshObserver(nsARefreshObserver *aObserver,
mozFlushType aFlushType);
/**
* Add / remove presshells that we should flush style and layout on
*/
PRBool AddStyleFlushObserver(nsIPresShell* aShell) {
NS_ASSERTION(!mStyleFlushObservers.Contains(aShell),
"Double-adding style flush observer");
PRBool appended = mStyleFlushObservers.AppendElement(aShell) != nsnull;
EnsureTimerStarted();
return appended;
}
void RemoveStyleFlushObserver(nsIPresShell* aShell) {
mStyleFlushObservers.RemoveElement(aShell);
}
PRBool AddLayoutFlushObserver(nsIPresShell* aShell) {
NS_ASSERTION(!IsLayoutFlushObserver(aShell),
"Double-adding layout flush observer");
PRBool appended = mLayoutFlushObservers.AppendElement(aShell) != nsnull;
EnsureTimerStarted();
return appended;
}
void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
mLayoutFlushObservers.RemoveElement(aShell);
}
PRBool IsLayoutFlushObserver(nsIPresShell* aShell) {
return mLayoutFlushObservers.Contains(aShell);
}
/**
* Tell the refresh driver that it is done driving refreshes and
* should stop its timer and forget about its pres context. This may
@ -162,6 +191,8 @@ private:
// separate arrays for each flush type we support
ObserverArray mObservers[3];
nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
};
#endif /* !defined(nsRefreshDriver_h_) */