Bug 577607 part 2. Teach the refresh driver to flush restyles and reflow on more than one presshell. r=roc
☠☠ backed out by 01a46df32143 ☠ ☠
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 11 Aug 2010 17:05:27 -0400
changeset 49571 0c4519d63a982f1e5943dcbd1320b516dd613957
parent 49570 d07f35b7a38f0bbc102915a2ebcb608ecd82e979
child 49572 b7900afb72e53a4ebeea30e03931999b2a531be4
child 49583 01a46df32143c4b5f02e96bd3907e70a50fb792f
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs577607
milestone2.0b4pre
Bug 577607 part 2. Teach the refresh driver to flush restyles and reflow on more than one presshell. r=roc
layout/base/RestyleTracker.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -166,16 +166,18 @@ RestyleTracker::ProcessOneRestyle(Elemen
 
 void
 RestyleTracker::ProcessRestyles()
 {
   // Make sure to not rebuild quote or counter lists while we're
   // processing restyles
   mFrameConstructor->BeginUpdate();
 
+  mFrameConstructor->mInStyleRefresh = PR_TRUE;
+
   // loop so that we process any restyle events generated by processing
   while (mPendingRestyles.Count()) {
     if (mHaveLaterSiblingRestyles) {
       // Convert them to individual restyles on all the later siblings
       nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
       LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
       mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
       for (PRUint32 i = 0; i < laterSiblingArr.Length(); ++i) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1369,19 +1369,16 @@ MoveChildrenTo(nsPresContext* aPresConte
     aNewParent->SetInitialChildList(nsnull, aFrameList);
   } else {
     aNewParent->AppendFrames(nsnull, aFrameList);
   }
 }
 
 //----------------------------------------------------------------------
 
-NS_IMPL_ADDREF(nsCSSFrameConstructor)
-NS_IMPL_RELEASE(nsCSSFrameConstructor)
-
 nsCSSFrameConstructor::nsCSSFrameConstructor(nsIDocument *aDocument,
                                              nsIPresShell *aPresShell)
   : mDocument(aDocument)
   , mPresShell(aPresShell)
   , mRootElementFrame(nsnull)
   , mRootElementStyleFrame(nsnull)
   , mFixedContainingBlock(nsnull)
   , mDocElementContainingBlock(nsnull)
@@ -1390,17 +1387,16 @@ nsCSSFrameConstructor::nsCSSFrameConstru
   , mUpdateCount(0)
   , mQuotesDirty(PR_FALSE)
   , mCountersDirty(PR_FALSE)
   , mIsDestroyingFrameTree(PR_FALSE)
   , mRebuildAllStyleData(PR_FALSE)
   , mHasRootAbsPosContainingBlock(PR_FALSE)
   , mObservingRefreshDriver(PR_FALSE)
   , mInStyleRefresh(PR_FALSE)
-  , mInLazyFCRefresh(PR_FALSE)
   , mHoverGeneration(0)
   , mRebuildAllExtraHint(nsChangeHint(0))
   , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
                      ELEMENT_IS_POTENTIAL_RESTYLE_ROOT, this)
   , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
                               ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT, this)
 {
   // XXXbz this should be in Init() or something!
@@ -6355,18 +6351,16 @@ nsCSSFrameConstructor::CreateNeededFrame
   }
 }
 
 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");
   if (rootElement && rootElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
     BeginUpdate();
     CreateNeededFrames(rootElement);
     EndUpdate();
   }
@@ -8279,22 +8273,21 @@ nsCSSFrameConstructor::WillDestroyFrameT
 #endif
 
   mIsDestroyingFrameTree = PR_TRUE;
 
   // Prevent frame tree destruction from being O(N^2)
   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
 
 // XXXbz I'd really like this method to go away. Once we have inline-block and
 // I can just use that for sized broken images, that can happen, maybe.
 void nsCSSFrameConstructor::GetAlternateTextFor(nsIContent*    aContent,
                                                 nsIAtom*       aTag,  // content object's tag
@@ -11668,34 +11661,21 @@ nsCSSFrameConstructor::PostRestyleEventC
 }
     
 void
 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);
-  }
-}
-
-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;
+    mObservingRefreshDriver = mPresShell->GetPresContext()->RefreshDriver()->
+      AddStyleFlushObserver(mPresShell);
+  }
 }
 
 void
 nsCSSFrameConstructor::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
 {
   NS_ASSERTION(!(aExtraHint & nsChangeHint_ReconstructFrame),
                "Should not reconstruct the root of the frame tree.  "
                "Use ReconstructDocElementHierarchy instead.");
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -47,17 +47,16 @@
 #include "nsILayoutHistoryState.h"
 #include "nsIXBLService.h"
 #include "nsQuoteList.h"
 #include "nsCounterManager.h"
 #include "nsHashKeys.h"
 #include "nsThreadUtils.h"
 #include "nsPageContentFrame.h"
 #include "nsCSSPseudoElements.h"
-#include "nsRefreshDriver.h"
 #include "RestyleTracker.h"
 
 class nsIDocument;
 struct nsFrameItems;
 struct nsAbsoluteItems;
 class nsStyleContext;
 struct nsStyleContent;
 struct nsStyleDisplay;
@@ -67,43 +66,37 @@ class nsIDOMHTMLSelectElement;
 class nsPresContext;
 class nsStyleChangeList;
 class nsIFrame;
 struct nsGenConInitializer;
 class ChildIterator;
 class nsICSSAnonBoxPseudo;
 class nsPageContentFrame;
 struct PendingBinding;
+class nsRefreshDriver;
 
 typedef void (nsLazyFrameConstructionCallback)
              (nsIContent* aContent, nsIFrame* aFrame, void* aArg);
 
 class nsFrameConstructorState;
 class nsFrameConstructorSaveState;
 
-class nsCSSFrameConstructor : public nsARefreshObserver
+class nsCSSFrameConstructor
 {
+  friend class nsRefreshDriver;
+
 public:
   typedef mozilla::dom::Element Element;
   typedef mozilla::css::RestyleTracker RestyleTracker;
 
   nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell);
   ~nsCSSFrameConstructor(void) {
     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;
 
   // Maintain global objects - gXBLService
   static nsIXBLService * GetXBLService();
   static void ReleaseGlobals() { NS_IF_RELEASE(gXBLService); }
 
   // get the alternate text for a content node
@@ -346,19 +339,16 @@ public:
 
   // See PostRestyleEventCommon below.
   void PostAnimationRestyleEvent(Element* aElement,
                                  nsRestyleHint aRestyleHint,
                                  nsChangeHint aMinChangeHint)
   {
     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
    * style recomputed.
    * @param aElement: The element to be restyled.
    * @param aRestyleHint: Which nodes need to have selector matching run
    *                      on them.
    * @param aMinChangeHint: A minimum change hint for aContent and its
@@ -1866,19 +1856,16 @@ private:
   PRPackedBool        mIsDestroyingFrameTree : 1;
   PRPackedBool        mRebuildAllStyleData : 1;
   // This is true if mDocElementContainingBlock supports absolute positioning
   PRPackedBool        mHasRootAbsPosContainingBlock : 1;
   // True if we're already waiting for a refresh notification
   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;
 
   nsCOMPtr<nsILayoutHistoryState> mTempFrameTreeState;
 
   RestyleTracker mPendingRestyles;
   RestyleTracker mPendingAnimationRestyles;
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1088,26 +1088,28 @@ public:
 
   /**
    * Initialize and shut down static variables.
    */
   static void InitializeStatics();
   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).
 
   // these are the same Document and PresContext owned by the DocViewer.
   // we must share ownership.
   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]
   nsWeakPtr                 mForwardingContainer;
 
 #ifdef NS_DEBUG
   nsIFrame*                 mDrawEventTargetFrame;
 #endif
@@ -1135,16 +1137,23 @@ protected:
 #endif
 
   // Set to true when the accessibility service is being used to mirror
   // the dom/layout trees
   PRPackedBool              mIsAccessibilityActive;
 
   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;
 
   // Most recent canvas background color.
   nscolor                   mCanvasBackgroundColor;
 
   // Live pres shells, for memory and other tracking
   typedef nsPtrHashKey<nsIPresShell> PresShellPtrKey;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -668,26 +668,25 @@ struct nsCallbackEventRequest
   nsIReflowCallback* callback;
   nsCallbackEventRequest* next;
 };
 
 // ----------------------------------------------------------------------------
 #define ASSERT_REFLOW_SCHEDULED_STATE()                                       \
   NS_ASSERTION(mReflowScheduled ==                                            \
                  GetPresContext()->RefreshDriver()->                          \
-                   IsRefreshObserver(this, Flush_Layout), "Unexpected state")
+                   IsLayoutFlushObserver(this), "Unexpected state")
 
 class nsPresShellEventCB;
 class nsAutoCauseReflowNotifier;
 
 class PresShell : public nsIPresShell, public nsIViewObserver,
                   public nsStubDocumentObserver,
                   public nsISelectionController, public nsIObserver,
-                  public nsSupportsWeakReference,
-                  public nsARefreshObserver
+                  public nsSupportsWeakReference
 {
 public:
   PresShell();
 
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
   // nsISupports
   NS_DECL_ISUPPORTS
@@ -908,19 +907,16 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   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);
   virtual NS_HIDDEN_(void) PaintCount(const char * aName,
                                       nsIRenderingContext* aRenderingContext,
                                       nsPresContext* aPresContext,
                                       nsIFrame * aFrame,
                                       PRUint32 aColor);
@@ -1212,26 +1208,21 @@ protected:
 
   PRPackedBool                         mNoDelayedMouseEvents;
   PRPackedBool                         mNoDelayedKeyEvents;
   nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents;
 
   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.
 #define PAINTLOCK_EVENT_DELAY 250 // 250ms.  This is actually
                                   // pref-controlled, but we use this
                                   // value if we fail to get the pref
                                   // for any reason.
 
@@ -1640,17 +1631,17 @@ PresShell::~PresShell()
                "post-reflow queues not empty.  This means we're leaking");
 
 #ifdef DEBUG
   NS_ASSERTION(mPresArenaAllocCount == 0,
                "Some pres arena objects were not freed");
 #endif
 
   delete mStyleSet;
-  NS_IF_RELEASE(mFrameConstructor);
+  delete mFrameConstructor;
 
   mCurrentEventContent = nsnull;
 
   NS_IF_RELEASE(mPresContext);
   NS_IF_RELEASE(mDocument);
   NS_IF_RELEASE(mSelection);
 }
 
@@ -1689,17 +1680,16 @@ PresShell::Init(nsIDocument* aDocument,
 
   mDocument = aDocument;
   NS_ADDREF(mDocument);
   mViewManager = aViewManager;
 
   // 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);
 
   // Bind the context to the presentation shell.
   mPresContext = aPresContext;
   NS_ADDREF(mPresContext);
   aPresContext->SetShell(this);
@@ -1937,17 +1927,17 @@ PresShell::Destroy()
       mDocument->GetAnimationController()->StopSampling(rd);
     }
 #endif // MOZ_SMIL
   }
 
   // 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();
     mAsyncResizeTimerIsActive = PR_FALSE;
   }
 
   CancelAllPendingReflows();
   CancelPostedReflowCallbacks();
@@ -3578,17 +3568,17 @@ nsIPresShell::GetFrameToScrollAsScrollab
 }
 
 void
 PresShell::CancelAllPendingReflows()
 {
   mDirtyRoots.Clear();
 
   if (mReflowScheduled) {
-    GetPresContext()->RefreshDriver()->RemoveRefreshObserver(this, Flush_Layout);
+    GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
     mReflowScheduled = PR_FALSE;
   }
 
   ASSERT_REFLOW_SCHEDULED_STATE();
 }
 
 #ifdef ACCESSIBILITY
 void nsIPresShell::InvalidateAccessibleSubtree(nsIContent *aContent)
@@ -7341,30 +7331,16 @@ PresShell::Thaw()
 
   UnsuppressPainting();
 
   // Get the activeness of our presshell, as this might have changed
   // while we were in the bfcache
   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
 //--------------------------------------------------------
 
 void
 PresShell::MaybeScheduleReflow()
 {
   ASSERT_REFLOW_SCHEDULED_STATE();
@@ -7380,17 +7356,17 @@ PresShell::MaybeScheduleReflow()
 }
 
 void
 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;
   }
 
   ASSERT_REFLOW_SCHEDULED_STATE();
 }
 
 nsresult
 PresShell::DidCauseReflow()
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -41,16 +41,17 @@
  * refresh rate.  (Perhaps temporary, until replaced by compositor.)
  */
 
 #include "nsRefreshDriver.h"
 #include "nsPresContext.h"
 #include "nsComponentManagerUtils.h"
 #include "prlog.h"
 #include "nsAutoPtr.h"
+#include "nsCSSFrameConstructor.h"
 
 /*
  * TODO:
  * Once this is hooked in to suppressing updates when the presentation
  * is not visible, we need to hook it up to FlushPendingNotifications so
  * that we flush when necessary.
  */
 
@@ -135,16 +136,18 @@ nsRefreshDriver::StopTimer()
 
 PRUint32
 nsRefreshDriver::ObserverCount() const
 {
   PRUint32 sum = 0;
   for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mObservers); ++i) {
     sum += mObservers[i].Length();
   }
+  sum += mStyleFlushObservers.Length();
+  sum += mLayoutFlushObservers.Length();
   return sum;
 }
 
 void
 nsRefreshDriver::UpdateMostRecentRefresh()
 {
   mMostRecentRefresh = TimeStamp::Now();
 }
@@ -210,23 +213,35 @@ nsRefreshDriver::Notify(nsITimer * /* un
       
       if (!mPresContext || !mPresContext->GetPresShell()) {
         StopTimer();
         return NS_OK;
       }
     }
     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);
+      }
     }
   }
 
   return NS_OK;
 }
 
 void
 nsRefreshDriver::Freeze()
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -43,18 +43,20 @@
 #ifndef nsRefreshDriver_h_
 #define nsRefreshDriver_h_
 
 #include "mozilla/TimeStamp.h"
 #include "mozFlushType.h"
 #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
  * notified at refresh times.  When nothing needs to be painted, callers
  * may not be notified.
  */
 class nsARefreshObserver {
 public:
@@ -107,16 +109,43 @@ public:
    * they must remove themselves before they are destroyed.
    */
   PRBool AddRefreshObserver(nsARefreshObserver *aObserver,
                             mozFlushType aFlushType);
   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
    * be called from within a refresh.
    */
   void Disconnect() {
     StopTimer();
     mPresContext = nsnull;
   }
@@ -157,11 +186,13 @@ private:
 
   nsPresContext *mPresContext; // weak; pres context passed in constructor
                                // and unset in Disconnect
 
   PRBool mFrozen;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
+  nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
+  nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
 };
 
 #endif /* !defined(nsRefreshDriver_h_) */