From e51e8403e6c8bf2ce8ea442c336b2fc704223dba Mon Sep 17 00:00:00 2001 From: "bzbarsky@mit.edu" Date: Tue, 21 Aug 2007 19:57:06 -0700 Subject: [PATCH] Make sure to process style updates before reflow, and both before painting. Bug 375436, r+sr=roc, a=dbaron --- content/base/public/mozFlushType.h | 23 ++---- content/base/src/nsDocument.cpp | 6 +- .../html/document/src/nsHTMLContentSink.cpp | 4 +- content/xml/document/src/nsXMLContentSink.cpp | 4 +- layout/base/nsPresShell.cpp | 40 +++++----- layout/forms/nsComboboxControlFrame.cpp | 5 +- layout/forms/nsListControlFrame.cpp | 9 --- layout/generic/nsGfxScrollFrame.cpp | 2 +- layout/generic/nsSelection.cpp | 50 ++++++++---- layout/printing/nsPrintEngine.cpp | 2 +- layout/style/nsInspectorCSSUtils.cpp | 2 +- layout/xul/base/src/nsListBoxBodyFrame.cpp | 78 +++++++++++++++---- layout/xul/base/src/nsListBoxBodyFrame.h | 43 +++++++++- view/src/nsViewManager.cpp | 2 +- 14 files changed, 182 insertions(+), 88 deletions(-) diff --git a/content/base/public/mozFlushType.h b/content/base/public/mozFlushType.h index fd058a0afa94..bb2c590f4e23 100644 --- a/content/base/public/mozFlushType.h +++ b/content/base/public/mozFlushType.h @@ -42,21 +42,14 @@ * 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___ */ diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 04bfe5749f5e..a6c0f790cc2c 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -4786,8 +4786,7 @@ 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 sink = mParser->GetContentSink(); if (sink) { sink->FlushPendingNotifications(aType); @@ -4798,8 +4797,7 @@ nsDocument::FlushPendingNotifications(mozFlushType aType) nsPIDOMWindow *window = GetWindow(); - if (aType == (aType & (Flush_Content | Flush_SinkNotifications)) || - !window) { + if (aType <= Flush_ContentAndNotify || !window) { // Nothing to do here return; } diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp index c9a57c92f967..90e0a08cbcf9 100644 --- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -3199,13 +3199,13 @@ 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); diff --git a/content/xml/document/src/nsXMLContentSink.cpp b/content/xml/document/src/nsXMLContentSink.cpp index e81d8fd0db34..1d95815a09a1 100644 --- a/content/xml/document/src/nsXMLContentSink.cpp +++ b/content/xml/document/src/nsXMLContentSink.cpp @@ -1579,13 +1579,13 @@ 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); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 79e05a4d10f5..347047e024c0 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1099,6 +1099,11 @@ protected: // 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; @@ -4369,9 +4374,14 @@ PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush) 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); @@ -4391,22 +4401,20 @@ PresShell::FlushPendingNotifications(mozFlushType aType) // batching if we only have style reresolve viewManager->BeginUpdateViewBatch(); - if (aType & Flush_StyleReresolves) { - mFrameConstructor->ProcessPendingRestyles(); - } + mFrameConstructor->ProcessPendingRestyles(); - if (aType & Flush_OnlyReflow && !mIsDestroying) { + 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. @@ -5786,12 +5794,7 @@ PresShell::WillPaint() // 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 @@ -5929,9 +5932,8 @@ PresShell::ReflowEvent::Run() { // 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 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; diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index 52ac115cb602..2036bc52a0c3 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -418,10 +418,9 @@ nsComboboxControlFrame::ShowList(nsPresContext* aPresContext, PRBool aShowList) 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; } diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 4014543f2fdb..b048221b8e51 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -2738,15 +2738,6 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) } #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); diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 9e7b0b743003..473a97b6e0a1 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1875,7 +1875,7 @@ nsGfxScrollFrameInner::AsyncScrollPortEvent::Run() { if (mInner) { mInner->mOuter->PresContext()->GetPresShell()-> - FlushPendingNotifications(Flush_OnlyReflow); + FlushPendingNotifications(Flush_Layout); } return mInner ? mInner->FireScrollPortEvent() : NS_OK; } diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 385ce7fd3dc3..8e07299ce095 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -212,7 +212,10 @@ public: 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); @@ -1262,7 +1265,9 @@ nsFrameSelection::MoveCaret(PRUint32 aKeycode, } 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 : @@ -1277,7 +1282,9 @@ nsFrameSelection::MoveCaret(PRUint32 aKeycode, } 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; } } @@ -1426,7 +1433,9 @@ nsFrameSelection::MoveCaret(PRUint32 aKeycode, 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; @@ -2538,7 +2547,8 @@ nsFrameSelection::ScrollSelectionIntoView(SelectionType aType, if (!mDomSelections[index]) return NS_ERROR_NULL_POINTER; - return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous); + return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous, + PR_FALSE); } nsresult @@ -5831,7 +5841,8 @@ nsTypedSelection::RemoveRange(nsIDOMRange* aRange) if (cnt > 0) { setAnchorFocusRange(cnt - 1);//reset anchor to LAST range. - ScrollIntoView(); + ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE, + PR_FALSE); } } if (!mFrameSelection) @@ -7251,7 +7262,7 @@ nsTypedSelection::ScrollSelectionIntoViewEvent::Run() return NS_OK; // event revoked mTypedSelection->mScrollEvent.Forget(); - mTypedSelection->ScrollIntoView(mRegion, PR_TRUE); + mTypedSelection->ScrollIntoView(mRegion, PR_TRUE, PR_TRUE); return NS_OK; } @@ -7273,8 +7284,9 @@ nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion) return NS_OK; } -NS_IMETHODIMP -nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous) +nsresult +nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, + PRBool aIsSynchronous, PRBool aDoFlush) { nsresult result; if (!mFrameSelection) @@ -7298,13 +7310,21 @@ nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous) presShell->GetCaret(getter_AddRefs(caret)); if (caret) { - StCaretHider caretHider(caret); // stack-based class hides and shows the 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); - // 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); + // 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 // // Scroll the selection region into view. diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp index cc90d9ddb4f8..20bd62c4179a 100644 --- a/layout/printing/nsPrintEngine.cpp +++ b/layout/printing/nsPrintEngine.cpp @@ -1918,7 +1918,7 @@ nsPrintEngine::ReflowPrintObject(nsPrintObject * aPO) 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 displayShell; aPO->mDocShell->GetPresShell(getter_AddRefs(displayShell)); diff --git a/layout/style/nsInspectorCSSUtils.cpp b/layout/style/nsInspectorCSSUtils.cpp index 3da04911907f..0e38d7f8d38f 100644 --- a/layout/style/nsInspectorCSSUtils.cpp +++ b/layout/style/nsInspectorCSSUtils.cpp @@ -122,7 +122,7 @@ nsInspectorCSSUtils::GetStyleContextForContent(nsIContent* aContent, nsIPresShell* aPresShell) { if (!aPseudo) { - aPresShell->FlushPendingNotifications(Flush_StyleReresolves); + aPresShell->FlushPendingNotifications(Flush_Style); nsIFrame* frame = aPresShell->GetPrimaryFrameFor(aContent); if (frame) { nsStyleContext* result = GetStyleContextForFrame(frame); diff --git a/layout/xul/base/src/nsListBoxBodyFrame.cpp b/layout/xul/base/src/nsListBoxBodyFrame.cpp index 5ba269f65c4f..5632c9d92fab 100644 --- a/layout/xul/base/src/nsListBoxBodyFrame.cpp +++ b/layout/xul/base/src/nsListBoxBodyFrame.cpp @@ -263,6 +263,11 @@ nsListBoxBodyFrame::Destroy() 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(); @@ -421,9 +426,6 @@ nsListBoxBodyFrame::PositionChanged(nsISupports* aScrollbar, PRInt32 aOldIndex, 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(); @@ -568,7 +570,9 @@ nsListBoxBodyFrame::EnsureIndexIsVisible(PRInt32 aRowIndex) 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; } @@ -595,6 +599,7 @@ nsListBoxBodyFrame::ScrollByLines(PRInt32 aNumLines) // 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(); @@ -844,17 +849,19 @@ nsListBoxBodyFrame::ScrollToIndex(PRInt32 aRowIndex) 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(); @@ -867,12 +874,49 @@ nsListBoxBodyFrame::InternalPositionChangedCallback() 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); } -NS_IMETHODIMP +nsresult nsListBoxBodyFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta) -{ +{ + nsRefPtr 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; +} + +nsresult +nsListBoxBodyFrame::DoInternalPositionChangedSync(PRBool aUp, PRInt32 aDelta) +{ + nsWeakFrame weak(this); + + // Process all the pending position changes first + nsTArray< nsRefPtr > 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; @@ -882,7 +926,11 @@ nsListBoxBodyFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta) // 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) @@ -922,7 +970,11 @@ nsListBoxBodyFrame::InternalPositionChanged(PRBool aUp, PRInt32 aDelta) 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); diff --git a/layout/xul/base/src/nsListBoxBodyFrame.h b/layout/xul/base/src/nsListBoxBodyFrame.h index 6e6b41553102..fcb8a109e2bc 100644 --- a/layout/xul/base/src/nsListBoxBodyFrame.h +++ b/layout/xul/base/src/nsListBoxBodyFrame.h @@ -48,6 +48,7 @@ #include "nsIReflowCallback.h" #include "nsPresContext.h" #include "nsBoxLayoutState.h" +#include "nsThreadUtils.h" class nsListScrollSmoother; nsIFrame* NS_NewListBoxBodyFrame(nsIPresShell* aPresShell, @@ -105,8 +106,13 @@ public: 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); @@ -132,6 +138,37 @@ public: 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); @@ -157,6 +194,8 @@ protected: nsListScrollSmoother* mScrollSmoother; PRInt32 mTimePerRow; + nsTArray< nsRefPtr > mPendingPositionChangeEvents; + PRPackedBool mReflowCallbackPosted; }; diff --git a/view/src/nsViewManager.cpp b/view/src/nsViewManager.cpp index c89ce7d9be15..665590edafe7 100644 --- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -1022,7 +1022,7 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS // 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 rootVM = RootViewManager(); nsIWidget *widget = mRootView->GetWidget(); PRBool translucentWindow = PR_FALSE;