Make sure to process style updates before reflow, and both before painting. Bug 375436, r+sr=roc, a=dbaron

This commit is contained in:
bzbarsky@mit.edu 2007-08-21 19:57:06 -07:00
Родитель 6e9906b98c
Коммит 6b0b848b3c
14 изменённых файлов: 182 добавлений и 88 удалений

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

@ -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___ */

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

@ -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<nsIContentSink> 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;
}

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

@ -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);

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

@ -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);

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

@ -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<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;

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

@ -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;
}

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

@ -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);

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

@ -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;
}

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

@ -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.

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

@ -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<nsIPresShell> displayShell;
aPO->mDocShell->GetPresShell(getter_AddRefs(displayShell));

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

@ -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);

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

@ -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<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;
}
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;
@ -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);

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

@ -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<nsPositionChangedEvent> > mPendingPositionChangeEvents;
PRPackedBool mReflowCallbackPosted;
};

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

@ -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<nsViewManager> rootVM = RootViewManager();
nsIWidget *widget = mRootView->GetWidget();
PRBool translucentWindow = PR_FALSE;