Make sure to process style updates before reflow, and both before painting. Bug 375436, r+sr=roc, a=dbaron
authorbzbarsky@mit.edu
Tue, 21 Aug 2007 19:57:06 -0700
changeset 4886 4f25f1f3f9bc513a5c16ac0b778e5d370f0acf9f
parent 4885 f2bb468eab2fe9e9f2bb0d9ee7e4156691922997
child 4887 46e7e3f789c32b2109c1de07f253a353cd75cd27
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs375436
milestone1.9a8pre
Make sure to process style updates before reflow, and both before painting. Bug 375436, r+sr=roc, a=dbaron
content/base/public/mozFlushType.h
content/base/src/nsDocument.cpp
content/html/document/src/nsHTMLContentSink.cpp
content/xml/document/src/nsXMLContentSink.cpp
layout/base/nsPresShell.cpp
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsListControlFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsSelection.cpp
layout/printing/nsPrintEngine.cpp
layout/style/nsInspectorCSSUtils.cpp
layout/xul/base/src/nsListBoxBodyFrame.cpp
layout/xul/base/src/nsListBoxBodyFrame.h
view/src/nsViewManager.cpp
--- a/content/base/public/mozFlushType.h
+++ b/content/base/public/mozFlushType.h
@@ -37,26 +37,19 @@
 #ifndef mozFlushType_h___
 #define mozFlushType_h___
 
 /**
  * This is the enum used by nsIDocument::FlushPendingNotifications to
  * decide what to flush.
  */
 enum mozFlushType {
-  Flush_Content           = 0x1,   /* flush the content model construction */
-  Flush_SinkNotifications = 0x2,   /* flush the frame model construction */
-  Flush_StyleReresolves   = 0x4,   /* flush style reresolution */
-  Flush_OnlyReflow        = 0x8,   /* flush reflows */
-  Flush_OnlyPaint         = 0x10,  /* flush painting */
-  Flush_ContentAndNotify  = (Flush_Content | Flush_SinkNotifications),
-  Flush_Frames            = (Flush_Content | Flush_SinkNotifications |
-                             Flush_StyleReresolves),
-  Flush_Style             = (Flush_Content | Flush_SinkNotifications |
-                             Flush_StyleReresolves),
-  Flush_Layout            = (Flush_Content | Flush_SinkNotifications |
-                             Flush_StyleReresolves | Flush_OnlyReflow),
-  Flush_Display           = (Flush_Content | Flush_SinkNotifications |
-                             Flush_StyleReresolves | Flush_OnlyReflow |
-                             Flush_OnlyPaint)
+  Flush_Content          = 1, /* flush the content model construction */
+  Flush_ContentAndNotify = 2, /* As above, plus flush the frame model
+                                 construction and other nsIMutationObserver
+                                 notifications. */
+  Flush_Style            = 3, /* As above, plus flush style reresolution */
+  Flush_Frames           = Flush_Style,
+  Flush_Layout           = 4, /* As above, plus flush reflow */
+  Flush_Display          = 5  /* As above, plus flush painting */
 };
 
 #endif /* mozFlushType_h___ */
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -4781,30 +4781,28 @@ nsDocument::CreateEventGroup(nsIDOMEvent
   return NS_OK;
 }
 
 void
 nsDocument::FlushPendingNotifications(mozFlushType aType)
 {
   // Determine if it is safe to flush the sink notifications
   // by determining if it safe to flush all the presshells.
-  if ((aType & Flush_Content) && mParser &&
-      (!(aType & Flush_SinkNotifications) || IsSafeToFlush())) {
+  if (mParser && (aType == Flush_Content || IsSafeToFlush())) {
     nsCOMPtr<nsIContentSink> sink = mParser->GetContentSink();
     if (sink) {
       sink->FlushPendingNotifications(aType);
     }
   }
 
   // Should we be flushing pending binding constructors in here?
 
   nsPIDOMWindow *window = GetWindow();
 
-  if (aType == (aType & (Flush_Content | Flush_SinkNotifications)) ||
-      !window) {
+  if (aType <= Flush_ContentAndNotify || !window) {
     // Nothing to do here
     return;
   }
 
   // We should be able to replace all this nsIDocShell* code with code
   // that uses mParentDocument, but mParentDocument is never set in
   // the current code!
 
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -3194,23 +3194,23 @@ HTMLContentSink::ProcessSTYLEEndTag(nsGe
 }
 
 void
 HTMLContentSink::FlushPendingNotifications(mozFlushType aType)
 {
   // Only flush tags if we're not doing the notification ourselves
   // (since we aren't reentrant)
   if (mCurrentContext && !mInNotification) {
-    if (aType & Flush_SinkNotifications) {
+    if (aType >= Flush_ContentAndNotify) {
       mCurrentContext->FlushTags();
     }
     else {
       mCurrentContext->FlushText();
     }
-    if (aType & Flush_OnlyReflow) {
+    if (aType >= Flush_Layout) {
       // Make sure that layout has started so that the reflow flush
       // will actually happen.
       StartLayout(PR_TRUE);
     }
   }
 }
 
 nsresult
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -1574,23 +1574,23 @@ nsXMLContentSink::AddText(const PRUnicha
 }
 
 void
 nsXMLContentSink::FlushPendingNotifications(mozFlushType aType)
 {
   // Only flush tags if we're not doing the notification ourselves
   // (since we aren't reentrant)
   if (!mInNotification) {
-    if (aType & Flush_SinkNotifications) {
+    if (aType >= Flush_ContentAndNotify) {
       FlushTags();
     }
     else {
       FlushText();
     }
-    if (aType & Flush_OnlyReflow) {
+    if (aType >= Flush_Layout) {
       // Make sure that layout has started so that the reflow flush
       // will actually happen.
       MaybeStartLayout(PR_TRUE);
     }
   }
 }
 
 /**
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1094,16 +1094,21 @@ protected:
   void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet);
 
   // Hide a view if it is a popup
   void HideViewIfPopup(nsIView* aView);
 
   // Utility method to restore the root scrollframe state
   void RestoreRootScrollPosition();
 
+  // Method to handle actually flushing.  This allows the caller to control
+  // whether the reflow flush (if any) should be interruptible.
+  nsresult DoFlushPendingNotifications(mozFlushType aType,
+                                       PRBool aInterruptibleReflow);
+
   nsICSSStyleSheet*         mPrefStyleSheet; // mStyleSet owns it but we maintain a ref, may be null
 #ifdef DEBUG
   PRUint32                  mUpdateCount;
 #endif
   // reflow roots that need to be reflowed, as both a queue and a hashtable
   nsVoidArray mDirtyRoots;
 
   PRPackedBool mDocumentLoading;
@@ -4364,19 +4369,24 @@ PresShell::IsSafeToFlush(PRBool& aIsSafe
   }
   return NS_OK;
 }
 
 
 NS_IMETHODIMP 
 PresShell::FlushPendingNotifications(mozFlushType aType)
 {
-  NS_ASSERTION(aType & (Flush_StyleReresolves | Flush_OnlyReflow |
-                        Flush_OnlyPaint),
-               "Why did we get called?");
+  return DoFlushPendingNotifications(aType, PR_FALSE);
+}
+
+NS_IMETHODIMP
+PresShell::DoFlushPendingNotifications(mozFlushType aType,
+                                       PRBool aInterruptibleReflow)
+{
+  NS_ASSERTION(aType >= Flush_Frames, "Why did we get called?");
   
   PRBool isSafeToFlush;
   IsSafeToFlush(isSafeToFlush);
 
   NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
   // Make sure the view manager stays alive while batching view updates.
   // XXX FIXME: If viewmanager hierarchy is modified while we're in update
   //            batch... We need to address that somehow.  See bug 369165.
@@ -4386,32 +4396,30 @@ PresShell::FlushPendingNotifications(moz
     // hold weak refs when calling FlushPendingNotifications().  :(
     nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
 
     // Style reresolves not in conjunction with reflows can't cause
     // painting or geometry changes, so don't bother with view update
     // batching if we only have style reresolve
     viewManager->BeginUpdateViewBatch();
 
-    if (aType & Flush_StyleReresolves) {
-      mFrameConstructor->ProcessPendingRestyles();
-    }
-
-    if (aType & Flush_OnlyReflow && !mIsDestroying) {
+    mFrameConstructor->ProcessPendingRestyles();
+
+    if (aType >= Flush_Layout && !mIsDestroying) {
       mFrameConstructor->RecalcQuotesAndCounters();
-      ProcessReflowCommands(PR_FALSE);
+      ProcessReflowCommands(aInterruptibleReflow);
     }
 
     PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
-    if (aType & Flush_OnlyPaint) {
+    if (aType >= Flush_Display) {
       // Flushing paints, so perform the invalidates and drawing
       // immediately
       updateFlags = NS_VMREFRESH_IMMEDIATE;
     }
-    else if (!(aType & Flush_OnlyReflow)) {
+    else if (aType < Flush_Layout) {
       // Not flushing reflows, so do deferred invalidates.  This will keep us
       // from possibly flushing out reflows due to invalidates being processed
       // at the end of this view batch.
       updateFlags = NS_VMREFRESH_DEFERRED;
     }
     viewManager->EndUpdateViewBatch(updateFlags);
   }
 
@@ -5781,22 +5789,17 @@ PresShell::WillPaint()
   if (mIsReflowing || mChangeNestCount || mPaintingSuppressed) {
     return;
   }
   
   // Process reflows, if we have them, to reduce flicker due to invalidates and
   // reflow being interspersed.  Note that we _do_ allow this to be
   // interruptible; if we can't do all the reflows it's better to flicker a bit
   // than to freeze up.
-  // XXXbz this update batch may not be strictly necessary, but it's good form.
-  // XXXbz should we be flushing out style changes here?  Probably not, I'd say.
-  NS_ASSERTION(mViewManager, "Something weird is going on");
-  mViewManager->BeginUpdateViewBatch();
-  ProcessReflowCommands(PR_TRUE);
-  mViewManager->EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
+  DoFlushPendingNotifications(Flush_Layout, PR_TRUE);
 }
 
 nsresult
 PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
 {
   aSheets.Clear();
   PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
 
@@ -5924,19 +5927,18 @@ PresShell::ReflowEvent::Run() {
        printf("\n*** Handling reflow event: PresShell=%p, event=%p\n", (void*)ps, (void*)this);
     }
 #endif
     // NOTE: the ReflowEvent class is a friend of the PresShell class
     ps->ClearReflowEventStatus();
     // Set a kung fu death grip on the view manager associated with the pres shell
     // before processing that pres shell's reflow commands.  Fixes bug 54868.
     nsCOMPtr<nsIViewManager> viewManager = ps->GetViewManager();
-    viewManager->BeginUpdateViewBatch();
-    ps->ProcessReflowCommands(PR_TRUE);
-    viewManager->EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
+
+    ps->DoFlushPendingNotifications(Flush_Layout, PR_TRUE);
 
     // Now, explicitly release the pres shell before the view manager
     ps = nsnull;
     viewManager = nsnull;
   }
   return NS_OK;
 }
 
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -413,20 +413,19 @@ nsComboboxControlFrame::ShowList(nsPresC
   mDroppedDown = aShowList;
   if (mDroppedDown) {
     // The listcontrol frame will call back to the nsComboboxControlFrame's
     // ListWasSelected which will stop the capture.
     mListControlFrame->AboutToDropDown();
     mListControlFrame->CaptureMouseEvents(PR_TRUE);
   }
 
-  // Don't flush anything but reflows lest it destroy us
-  shell->GetDocument()->FlushPendingNotifications(Flush_OnlyReflow);
+  // XXXbz so why do we need to flush here, exactly?
+  shell->GetDocument()->FlushPendingNotifications(Flush_Layout);
   if (!weakFrame.IsAlive()) {
-    NS_ERROR("Flush_OnlyReflow destroyed the frame");
     return PR_FALSE;
   }
 
   nsIFrame* listFrame;
   CallQueryInterface(mListControlFrame, &listFrame);
   if (listFrame) {
     nsIView* view = listFrame->GetView();
     NS_ASSERTION(view, "nsComboboxControlFrame view is null");
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -2733,25 +2733,16 @@ nsListControlFrame::KeyPress(nsIDOMEvent
       }
     }
 #ifdef ACCESSIBILITY
     if (charcode != ' ') {
       FireMenuItemActiveEvent();
     }
 #endif
 
-    // XXX - Are we cover up a problem here???
-    // Why aren't they getting flushed each time?
-    // because this isn't needed for Gfx
-    if (IsInDropDownMode()) {
-      // Don't flush anything but reflows lest it destroy us
-      PresContext()->PresShell()->
-        GetDocument()->FlushPendingNotifications(Flush_OnlyReflow);
-    }
-
     // Make sure the SelectArea frame gets painted
     Invalidate(nsRect(0,0,mRect.width,mRect.height), PR_TRUE);
 
   }
 
   return NS_OK;
 }
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1870,17 +1870,17 @@ nsGfxScrollFrameInner::PostScrollEvent()
   }
 }
 
 NS_IMETHODIMP
 nsGfxScrollFrameInner::AsyncScrollPortEvent::Run()
 {
   if (mInner) {
     mInner->mOuter->PresContext()->GetPresShell()->
-      FlushPendingNotifications(Flush_OnlyReflow);
+      FlushPendingNotifications(Flush_Layout);
   }
   return mInner ? mInner->FireScrollPortEvent() : NS_OK;
 }
 
 PRBool
 nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState,
                                          nsRect& aScrollAreaSize, PRBool aOnTop)
 {
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -207,17 +207,20 @@ public:
   nsresult      GetPresShell(nsIPresShell **aPresShell);
   nsresult      GetRootScrollableView(nsIScrollableView **aScrollableView);
   nsresult      GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aXOffset, nscoord *aYOffset);
   nsresult      GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint);
   nsresult      GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView);
   nsresult      ScrollRectIntoView(nsIScrollableView *aScrollableView, nsRect& aRect, PRIntn  aVPercent, PRIntn  aHPercent, PRBool aScrollParentViews);
 
   nsresult      PostScrollSelectionIntoViewEvent(SelectionRegion aRegion);
-  NS_IMETHOD    ScrollIntoView(SelectionRegion aRegion=nsISelectionController::SELECTION_FOCUS_REGION, PRBool aIsSynchronous=PR_TRUE);
+  // aDoFlush only matters if aIsSynchronous is true.  If not, we'll just flush
+  // when the scroll event fires so we make sure to scroll to the right place.
+  nsresult      ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous,
+                               PRBool aDoFlush);
   nsresult      AddItem(nsIDOMRange *aRange);
   nsresult      RemoveItem(nsIDOMRange *aRange);
   nsresult      Clear(nsPresContext* aPresContext);
 
   // methods for convenience. Note, these don't addref
   nsIDOMNode*  FetchAnchorNode();  //where did the selection begin
   PRInt32      FetchAnchorOffset();
 
@@ -1257,32 +1260,36 @@ nsFrameSelection::MoveCaret(PRUint32    
             weakNodeUsed = mDomSelections[index]->FetchFocusNode();
           }
           else {
             offsetused = mDomSelections[index]->FetchAnchorOffset();
             weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
           }
           result = mDomSelections[index]->Collapse(weakNodeUsed, offsetused);
           mHint = HINTRIGHT;
-          mDomSelections[index]->ScrollIntoView();
+          mDomSelections[index]->
+            ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
+                           PR_FALSE, PR_FALSE);
           return NS_OK;
 
       case nsIDOMKeyEvent::DOM_VK_RIGHT :
       case nsIDOMKeyEvent::DOM_VK_DOWN  :
           if (mDomSelections[index]->GetDirection() == eDirPrevious) { //f,a
             offsetused = mDomSelections[index]->FetchAnchorOffset();
             weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
           }
           else {
             offsetused = mDomSelections[index]->FetchFocusOffset();
             weakNodeUsed = mDomSelections[index]->FetchFocusNode();
           }
           result = mDomSelections[index]->Collapse(weakNodeUsed, offsetused);
           mHint = HINTLEFT;
-          mDomSelections[index]->ScrollIntoView();
+          mDomSelections[index]->
+            ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
+                           PR_FALSE, PR_FALSE);
           return NS_OK;
     }
   }
 
   PRBool visualMovement = 
     (aKeycode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE || 
      aKeycode == nsIDOMKeyEvent::DOM_VK_DELETE ||
      aKeycode == nsIDOMKeyEvent::DOM_VK_HOME || 
@@ -1421,17 +1428,19 @@ nsFrameSelection::MoveCaret(PRUint32    
     else {
       tHint = HINTLEFT; // 2: we're now at the end of the frame to the left
     }
     result = NS_OK;
   }
   if (NS_SUCCEEDED(result))
   {
     mHint = tHint; //save the hint parameter now for the next time
-    result = mDomSelections[index]->ScrollIntoView();
+    result = mDomSelections[index]->
+      ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION,
+                     PR_FALSE, PR_FALSE);
   }
 
   return result;
 }
 
 //END nsFrameSelection methods
 
 
@@ -2533,17 +2542,18 @@ nsFrameSelection::ScrollSelectionIntoVie
 {
   PRInt8 index = GetIndexFromSelectionType(aType);
   if (index < 0)
     return NS_ERROR_INVALID_ARG;
 
   if (!mDomSelections[index])
     return NS_ERROR_NULL_POINTER;
 
-  return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous);
+  return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous,
+                                               PR_FALSE);
 }
 
 nsresult
 nsFrameSelection::RepaintSelection(SelectionType aType)
 {
   PRInt8 index = GetIndexFromSelectionType(aType);
   if (index < 0)
     return NS_ERROR_INVALID_ARG;
@@ -5826,17 +5836,18 @@ nsTypedSelection::RemoveRange(nsIDOMRang
   // might appear to be moving randomly (bug 337871).
   if (mType != nsISelectionController::SELECTION_SPELLCHECK &&
       aRange == mAnchorFocusRange.get())
   {
     PRInt32 cnt = mRanges.Length();
     if (cnt > 0)
     {
       setAnchorFocusRange(cnt - 1);//reset anchor to LAST range.
-      ScrollIntoView();
+      ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE,
+                     PR_FALSE);
     }
   }
   if (!mFrameSelection)
     return NS_OK;//nothing to do
   return mFrameSelection->NotifySelectionListeners(GetType());
 }
 
 
@@ -7246,17 +7257,17 @@ nsTypedSelection::ScrollRectIntoView(nsI
 
 NS_IMETHODIMP
 nsTypedSelection::ScrollSelectionIntoViewEvent::Run()
 {
   if (!mTypedSelection)
     return NS_OK;  // event revoked
 
   mTypedSelection->mScrollEvent.Forget();
-  mTypedSelection->ScrollIntoView(mRegion, PR_TRUE);
+  mTypedSelection->ScrollIntoView(mRegion, PR_TRUE, PR_TRUE);
   return NS_OK;
 }
 
 nsresult
 nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion)
 {
   // If we've already posted an event, revoke it and place a new one at the
   // end of the queue to make sure that any new pending reflow events are
@@ -7268,18 +7279,19 @@ nsTypedSelection::PostScrollSelectionInt
       new ScrollSelectionIntoViewEvent(this, aRegion);
   nsresult rv = NS_DispatchToCurrentThread(ev);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mScrollEvent = ev;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous)
+nsresult
+nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
+                                 PRBool aIsSynchronous, PRBool aDoFlush)
 {
   nsresult result;
   if (!mFrameSelection)
     return NS_OK;//nothing to do
 
   if (mFrameSelection->GetBatching())
     return NS_OK;
 
@@ -7293,24 +7305,32 @@ nsTypedSelection::ScrollIntoView(Selecti
   nsCOMPtr<nsIPresShell> presShell;
   result = GetPresShell(getter_AddRefs(presShell));
   if (NS_FAILED(result) || !presShell)
     return result;
   nsCOMPtr<nsICaret> caret;
   presShell->GetCaret(getter_AddRefs(caret));
   if (caret)
   {
+    // Now that text frame character offsets are always valid (though not
+    // necessarily correct), the worst that will happen if we don't flush here
+    // is that some callers might scroll to the wrong place.  Those should
+    // either manually flush if they're in a safe position for it or use the
+    // async version of this method.
+    if (aDoFlush) {
+      presShell->FlushPendingNotifications(Flush_Layout);
+
+      // Reget the presshell, since it might have gone away.
+      result = GetPresShell(getter_AddRefs(presShell));
+      if (NS_FAILED(result) || !presShell)
+        return result;
+    }
+
     StCaretHider  caretHider(caret);      // stack-based class hides and shows the caret
 
-    // We are going to scroll to a character offset within a frame by
-    // using APIs on the scrollable view directly. So we need to
-    // flush out pending reflows to make sure that frames are up-to-date.
-    // We crash otherwise - bug 252970#c97
-    presShell->FlushPendingNotifications(Flush_OnlyReflow);
-
     //
     // Scroll the selection region into view.
     //
 
     nsRect rect;
     nsIScrollableView *scrollableView = 0;
 
     result = GetSelectionRegionRectAndScrollableView(aRegion, &rect, &scrollableView);
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -1913,17 +1913,17 @@ nsPrintEngine::ReflowPrintObject(nsPrint
   aPO->mPresContext->SetPrintPreviewScale(float(screenDPI) / float(printDPI));
 
   rv = aPO->mPresShell->InitialReflow(adjSize.width, adjSize.height);
 
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(aPO->mPresShell, "Presshell should still be here");
 
   // Process the reflow event InitialReflow posted
-  aPO->mPresShell->FlushPendingNotifications(Flush_OnlyReflow);
+  aPO->mPresShell->FlushPendingNotifications(Flush_Layout);
 
   nsCOMPtr<nsIPresShell> displayShell;
   aPO->mDocShell->GetPresShell(getter_AddRefs(displayShell));
   // Transfer Selection Ranges to the new Print PresShell
   nsCOMPtr<nsISelection> selection, selectionPS;
   // It's okay if there is no display shell, just skip copying the selection
   if (displayShell) {
     selection = displayShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
--- a/layout/style/nsInspectorCSSUtils.cpp
+++ b/layout/style/nsInspectorCSSUtils.cpp
@@ -117,17 +117,17 @@ nsInspectorCSSUtils::GetStyleContextForF
 
 /* static */
 already_AddRefed<nsStyleContext>
 nsInspectorCSSUtils::GetStyleContextForContent(nsIContent* aContent,
                                                nsIAtom* aPseudo,
                                                nsIPresShell* aPresShell)
 {
     if (!aPseudo) {
-        aPresShell->FlushPendingNotifications(Flush_StyleReresolves);
+        aPresShell->FlushPendingNotifications(Flush_Style);
         nsIFrame* frame = aPresShell->GetPrimaryFrameFor(aContent);
         if (frame) {
             nsStyleContext* result = GetStyleContextForFrame(frame);
             // this function returns an addrefed style context
             if (result)
                 result->AddRef();
             return result;
         }
--- a/layout/xul/base/src/nsListBoxBodyFrame.cpp
+++ b/layout/xul/base/src/nsListBoxBodyFrame.cpp
@@ -258,16 +258,21 @@ nsListBoxBodyFrame::Init(nsIContent*    
 
 void
 nsListBoxBodyFrame::Destroy()
 {
   // make sure we cancel any posted callbacks.
   if (mReflowCallbackPosted)
      PresContext()->PresShell()->CancelReflowCallback(this);
 
+  // Revoke any pending position changed events
+  for (PRUint32 i = 0; i < mPendingPositionChangeEvents.Length(); ++i) {
+    mPendingPositionChangeEvents[i]->Revoke();
+  }
+
   // Make sure we tell our listbox's box object we're being destroyed.
   for (nsIFrame *a = mParent; a; a = a->GetParent()) {
     nsIContent *content = a->GetContent();
     nsIDocument *doc;
 
     if (content &&
         content->NodeInfo()->Equals(nsGkAtoms::listbox, kNameSpaceID_XUL) &&
         (doc = content->GetDocument())) {
@@ -416,19 +421,16 @@ nsListBoxBodyFrame::PositionChanged(nsIS
   nsListScrollSmoother* smoother = GetSmoother();
 
   // if we can't scroll the rows in time then start a timer. We will eat
   // events until the user stops moving and the timer stops.
   if (smoother->IsRunning() || rowDelta*mTimePerRow > USER_TIME_THRESHOLD) {
 
      smoother->Stop();
 
-     // Don't flush anything but reflows lest it destroy us
-     mContent->GetDocument()->FlushPendingNotifications(Flush_OnlyReflow);
-
      smoother->mDelta = newTwipIndex > oldTwipIndex ? rowDelta : -rowDelta;
 
      smoother->Start();
 
      return NS_OK;
   }
 
   smoother->Stop();
@@ -563,17 +565,19 @@ nsListBoxBodyFrame::EnsureIndexIsVisible
     mCurrentIndex = aRowIndex;
   }
   else {
     // Bring it just into view.
     delta = 1 + (aRowIndex-bottomIndex);
     mCurrentIndex += delta; 
   }
 
-  InternalPositionChanged(up, delta);
+  // Safe to not go off an event here, since this is coming from the
+  // box object.
+  DoInternalPositionChangedSync(up, delta);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsListBoxBodyFrame::ScrollByLines(PRInt32 aNumLines)
 {
   PRInt32 scrollIndex, visibleRows;
   GetIndexOfFirstVisibleRow(&scrollIndex);
@@ -590,16 +594,17 @@ nsListBoxBodyFrame::ScrollByLines(PRInt3
       scrollIndex = lastPageTopRow;
   }
   
   ScrollToIndex(scrollIndex);
 
   // we have to do a sync update for mac because if we scroll too quickly
   // w/out going back to the main event loop we can easily scroll the wrong
   // bits and it looks like garbage (bug 63465).
+  // XXXbz is this seriously still needed?
     
   // I'd use Composite here, but it doesn't always work.
   // vm->Composite();
   PresContext()->GetViewManager()->ForceUpdate();
 
   return NS_OK;
 }
 
@@ -839,55 +844,98 @@ nsListBoxBodyFrame::ScrollToIndex(PRInt3
   PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
   if (lastPageTopRow < 0)
     lastPageTopRow = 0;
 
   if (aRowIndex > lastPageTopRow)
     return NS_OK;
 
   mCurrentIndex = newIndex;
-  InternalPositionChanged(up, delta);
+
+  // Since we're going to flush anyway, we need to not do this off an event
+  DoInternalPositionChangedSync(up, delta);
 
   // This change has to happen immediately.
   // Flush any pending reflow commands.
-  // Don't flush anything but reflows lest it destroy us
-  mContent->GetDocument()->FlushPendingNotifications(Flush_OnlyReflow);
+  // XXXbz why, exactly?
+  mContent->GetDocument()->FlushPendingNotifications(Flush_Layout);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 nsListBoxBodyFrame::InternalPositionChangedCallback()
 {
   nsListScrollSmoother* smoother = GetSmoother();
 
   if (smoother->mDelta == 0)
     return NS_OK;
 
   mCurrentIndex += smoother->mDelta;
 
   if (mCurrentIndex < 0)
     mCurrentIndex = 0;
 
-  return InternalPositionChanged(smoother->mDelta < 0, smoother->mDelta < 0 ? -smoother->mDelta : smoother->mDelta);
+  return DoInternalPositionChangedSync(smoother->mDelta < 0,
+                                       smoother->mDelta < 0 ?
+                                         -smoother->mDelta : smoother->mDelta);
+}
+
+nsresult
+nsListBoxBodyFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta)
+{
+  nsRefPtr<nsPositionChangedEvent> ev =
+    new nsPositionChangedEvent(this, aUp, aDelta);
+  nsresult rv = NS_DispatchToCurrentThread(ev);
+  if (NS_SUCCEEDED(rv)) {
+    if (!mPendingPositionChangeEvents.AppendElement(ev)) {
+      rv = NS_ERROR_OUT_OF_MEMORY;
+      ev->Revoke();
+    }
+  }
+  return rv;
 }
 
-NS_IMETHODIMP
-nsListBoxBodyFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta)
-{  
+nsresult
+nsListBoxBodyFrame::DoInternalPositionChangedSync(PRBool aUp, PRInt32 aDelta)
+{
+  nsWeakFrame weak(this);
+  
+  // Process all the pending position changes first
+  nsTArray< nsRefPtr<nsPositionChangedEvent> > temp;
+  temp.SwapElements(mPendingPositionChangeEvents);
+  for (PRUint32 i = 0; i < temp.Length(); ++i) {
+    temp[i]->Run();
+    temp[i]->Revoke();
+  }
+
+  if (!weak.IsAlive()) {
+    return NS_OK;
+  }
+
+  return DoInternalPositionChanged(aUp, aDelta);
+}
+
+nsresult
+nsListBoxBodyFrame::DoInternalPositionChanged(PRBool aUp, PRInt32 aDelta)
+{
   if (aDelta == 0)
     return NS_OK;
 
   nsPresContext *presContext = PresContext();
   nsBoxLayoutState state(presContext);
 
   // begin timing how long it takes to scroll a row
   PRTime start = PR_Now();
 
-  mContent->GetDocument()->FlushPendingNotifications(Flush_OnlyReflow);
+  nsWeakFrame weakThis(this);
+  mContent->GetDocument()->FlushPendingNotifications(Flush_Layout);
+  if (!weakThis.IsAlive()) {
+    return NS_OK;
+  }
 
   PRInt32 visibleRows = 0;
   if (mRowHeight)
     visibleRows = GetAvailableHeight()/mRowHeight;
   
   if (aDelta < visibleRows) {
     PRInt32 loseRows = aDelta;
     if (aUp) {
@@ -917,17 +965,21 @@ nsListBoxBodyFrame::InternalPositionChan
   mTopFrame = mBottomFrame = nsnull; 
   
   mYPosition = mCurrentIndex*mRowHeight;
   mScrolling = PR_TRUE;
   PresContext()->PresShell()->
     FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
   // Flush calls CreateRows
   // XXXbz there has to be a better way to do this than flushing!
-  presContext->PresShell()->FlushPendingNotifications(Flush_OnlyReflow);
+  presContext->PresShell()->FlushPendingNotifications(Flush_Layout);
+  if (!weakThis.IsAlive()) {
+    return NS_OK;
+  }
+
   mScrolling = PR_FALSE;
   
   VerticalScroll(mYPosition);
 
   PRTime end = PR_Now();
 
   PRTime difTime;
   LL_SUB(difTime, end, start);
--- a/layout/xul/base/src/nsListBoxBodyFrame.h
+++ b/layout/xul/base/src/nsListBoxBodyFrame.h
@@ -43,16 +43,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsBoxFrame.h"
 #include "nsIListBoxObject.h"
 #include "nsIScrollbarMediator.h"
 #include "nsIReflowCallback.h"
 #include "nsPresContext.h"
 #include "nsBoxLayoutState.h"
+#include "nsThreadUtils.h"
 
 class nsListScrollSmoother;
 nsIFrame* NS_NewListBoxBodyFrame(nsIPresShell* aPresShell,
                                  nsStyleContext* aContext,
                                  PRBool aIsRoot = PR_FALSE,
                                  nsIBoxLayout* aLayoutManager = nsnull);
 
 class nsListBoxBodyFrame : public nsBoxFrame,
@@ -100,18 +101,23 @@ public:
   PRInt32 GetRowHeightAppUnits() { return mRowHeight; }
   PRInt32 GetFixedRowSize();
   void SetRowHeight(PRInt32 aRowHeight);
   nscoord GetYPosition();
   nscoord GetAvailableHeight();
   nscoord ComputeIntrinsicWidth(nsBoxLayoutState& aBoxLayoutState);
 
   // scrolling
-  NS_IMETHOD InternalPositionChangedCallback();
-  NS_IMETHOD InternalPositionChanged(PRBool aUp, PRInt32 aDelta);
+  nsresult InternalPositionChangedCallback();
+  nsresult InternalPositionChanged(PRBool aUp, PRInt32 aDelta);
+  // Process pending position changed events, then do the position change.
+  // This can wipe out the frametree.
+  nsresult DoInternalPositionChangedSync(PRBool aUp, PRInt32 aDelta);
+  // Actually do the internal position change.  This can wipe out the frametree
+  nsresult DoInternalPositionChanged(PRBool aUp, PRInt32 aDelta);
   nsListScrollSmoother* GetSmoother();
   void VerticalScroll(PRInt32 aDelta);
 
   // frames
   nsIFrame* GetFirstFrame();
   nsIFrame* GetLastFrame();
 
   // lazy row creation and destruction
@@ -127,16 +133,47 @@ public:
   void OnContentRemoved(nsPresContext* aPresContext,  nsIFrame* aChildFrame, PRInt32 aIndex);
 
   void GetListItemContentAt(PRInt32 aIndex, nsIContent** aContent);
   void GetListItemNextSibling(nsIContent* aListItem, nsIContent** aContent, PRInt32& aSiblingIndex);
 
   void PostReflowCallback();
 
 protected:
+  class nsPositionChangedEvent;
+  friend class nsPositionChangedEvent;
+
+  class nsPositionChangedEvent : public nsRunnable
+  {
+  public:
+    nsPositionChangedEvent(nsListBoxBodyFrame* aFrame,
+                           PRBool aUp, PRInt32 aDelta) :
+      mFrame(aFrame), mUp(aUp), mDelta(aDelta)
+    {}
+  
+    NS_IMETHOD Run()
+    {
+      if (!mFrame) {
+        return NS_OK;
+      }
+
+      mFrame->mPendingPositionChangeEvents.RemoveElement(this);
+
+      return mFrame->DoInternalPositionChanged(mUp, mDelta);
+    }
+
+    void Revoke() {
+      mFrame = nsnull;
+    }
+
+    nsListBoxBodyFrame* mFrame;
+    PRBool mUp;
+    PRInt32 mDelta;
+  };
+
   void ComputeTotalRowCount();
   void RemoveChildFrame(nsBoxLayoutState &aState, nsIFrame *aChild);
 
   // row height
   PRInt32 mRowCount;
   nscoord mRowHeight;
   PRPackedBool mRowHeightWasSet;
   nscoord mAvailableHeight;
@@ -152,12 +189,14 @@ protected:
   PRInt32 mCurrentIndex; // Row-based
   PRInt32 mOldIndex; 
   PRPackedBool mScrolling;
   PRPackedBool mAdjustScroll;
   PRInt32 mYPosition;
   nsListScrollSmoother* mScrollSmoother;
   PRInt32 mTimePerRow;
 
+  nsTArray< nsRefPtr<nsPositionChangedEvent> > mPendingPositionChangeEvents;
+
   PRPackedBool mReflowCallbackPosted;
 }; 
 
 #endif // nsListBoxBodyFrame_h
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -1017,17 +1017,17 @@ NS_IMETHODIMP nsViewManager::DispatchEve
 
           if (!didResize) {
             //NS_ASSERTION(IsViewVisible(view), "painting an invisible view");
 
             // Just notify our own view observer that we're about to paint
             // XXXbz do we need to notify other view observers for viewmanagers
             // in our tree?
             // Make sure to not send WillPaint notifications while scrolling
-            nsViewManager* rootVM = RootViewManager();
+            nsRefPtr<nsViewManager> rootVM = RootViewManager();
 
             nsIWidget *widget = mRootView->GetWidget();
             PRBool translucentWindow = PR_FALSE;
             if (widget)
                 widget->GetWindowTranslucency(translucentWindow);
 
             if (rootVM->mScrollCnt == 0 && !translucentWindow) {
               nsIViewObserver* observer = GetViewObserver();