Flush out reflows before processing invalidates and paint events. Fixes a

variety of flicker issues of various sorts, especially when we're not quite
keeping up with the rate of updates in DHTML or in editor.  Bug 244366 and
various dependencies, r+sr=roc
This commit is contained in:
bzbarsky%mit.edu 2005-01-20 03:39:09 +00:00
Родитель 6831f17ebf
Коммит d222ecb623
11 изменённых файлов: 208 добавлений и 141 удалений

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

@ -471,10 +471,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
GenerateDragGesture(aPresContext, (nsGUIEvent*)aEvent);
UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
GenerateMouseEnterExit(aPresContext, (nsGUIEvent*)aEvent);
// Flush reflows and invalidates to eliminate flicker when both a reflow
// and visual change occur in an event callback. See bug #36849
// XXXbz eeeew. Why not fix viewmanager to flush reflows before painting??
FlushPendingEvents(aPresContext);
break;
case NS_MOUSE_EXIT:
GenerateMouseEnterExit(aPresContext, (nsGUIEvent*)aEvent);
@ -1514,10 +1510,11 @@ nsEventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
StopTrackingDragGesture();
}
}
// Now flush all pending notifications.
FlushPendingEvents(aPresContext);
// Now flush all pending notifications, for better responsiveness
// while dragging.
FlushPendingEvents(aPresContext);
}
} // GenerateDragGesture
nsresult
@ -2838,7 +2835,7 @@ nsEventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext,
//reset mCurretTargetContent to what it was
mCurrentTargetContent = targetBeforeEvent;
// Now flush all pending notifications.
// Now flush all pending notifications, for better responsiveness.
FlushPendingEvents(aPresContext);
}
@ -4429,12 +4426,7 @@ nsEventStateManager::FlushPendingEvents(nsPresContext* aPresContext)
NS_PRECONDITION(nsnull != aPresContext, "nsnull ptr");
nsIPresShell *shell = aPresContext->GetPresShell();
if (shell) {
// This is not flushing _Display because of the mess that is bug 36849
shell->FlushPendingNotifications(Flush_Layout);
nsIViewManager* viewManager = shell->GetViewManager();
if (viewManager) {
viewManager->FlushPendingInvalidates();
}
shell->FlushPendingNotifications(Flush_Display);
}
}

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

@ -4381,8 +4381,10 @@ nsresult nsEditor::EndUpdateViewBatch()
{
PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE;
// If we're doing async updates, use NS_VMREFRESH_DEFERRED here, so that
// the reflows we caused will get processed before the invalidates.
if (flags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask)
updateFlag = NS_VMREFRESH_NO_SYNC;
updateFlag = NS_VMREFRESH_DEFERRED;
mViewManager->EndUpdateViewBatch(updateFlag);
}

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

@ -39,6 +39,11 @@
#include "nsIPresShell.h"
#include "nsILinkHandler.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShell.h"
#include "nsIContentViewer.h"
#include "nsIDocumentViewer.h"
#include "nsPIDOMWindow.h"
#include "nsIFocusController.h"
#include "nsStyleSet.h"
#include "nsImageLoader.h"
#include "nsIContent.h"
@ -1229,6 +1234,42 @@ nsPresContext::SetPrintSettings(nsIPrintSettings *aPrintSettings)
mPrintSettings = aPrintSettings;
}
PRBool
nsPresContext::EnsureVisible(PRBool aUnsuppressFocus)
{
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
if (docShell) {
nsCOMPtr<nsIContentViewer> cv;
docShell->GetContentViewer(getter_AddRefs(cv));
// Make sure this is the content viewer we belong with
nsCOMPtr<nsIDocumentViewer> docV(do_QueryInterface(cv));
if (docV) {
nsCOMPtr<nsPresContext> currentPresContext;
docV->GetPresContext(getter_AddRefs(currentPresContext));
if (currentPresContext == this) {
// OK, this is us. We want to call Show() on the content viewer. But
// first, we need to suppress focus changes; otherwise the focus will
// get sent to the wrong place (toplevel window).
nsCOMPtr<nsPIDOMWindow> privWindow = do_GetInterface(docShell);
// XXXbz privWindow should never really be null!
nsIFocusController* fc =
privWindow ? privWindow->GetRootFocusController() : nsnull;
if (fc) {
fc->SetSuppressFocus(PR_TRUE,
"nsPresContext::EnsureVisible Suppression");
}
cv->Show();
if (fc && aUnsuppressFocus) {
fc->SetSuppressFocus(PR_FALSE,
"nsPresContext::EnsureVisible Suppression");
}
return PR_TRUE;
}
}
}
return PR_FALSE;
}
nsresult
NS_NewPresContext(nsPresContext::nsPresContextType aType,
nsPresContext** aInstancePtrResult)

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

@ -587,6 +587,16 @@ public:
/* Accessor for table of frame properties */
nsPropertyTable* PropertyTable() { return &mPropertyTable; }
/* Helper function that ensures that this prescontext is shown in its
docshell if it's the most recent prescontext for the docshell. Returns
whether the prescontext is now being shown.
@param aUnsuppressFocus If this is false, then focus will not be
unsuppressed when PR_TRUE is returned. It's the caller's responsibility
to unsuppress focus in that case.
*/
NS_HIDDEN_(PRBool) EnsureVisible(PRBool aUnsuppressFocus);
#ifdef MOZ_REFLOW_PERF
NS_HIDDEN_(void) CountReflows(const char * aName,
PRUint32 aType, nsIFrame * aFrame);

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

@ -188,7 +188,6 @@
// Content viewer interfaces
#include "nsIContentViewer.h"
#include "nsIDocumentViewer.h"
#ifdef IBMBIDI
#include "nsIBidiKeyboard.h"
@ -1222,6 +1221,7 @@ public:
nsEventStatus* aStatus);
NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight);
NS_IMETHOD_(PRBool) IsVisible();
NS_IMETHOD_(void) WillPaint();
// caret handling
NS_IMETHOD GetCaret(nsICaret **aOutCaret);
@ -1333,7 +1333,7 @@ protected:
nsresult AddDummyLayoutRequest(void);
nsresult RemoveDummyLayoutRequest(void);
void WillCauseReflow() {}
void WillCauseReflow() { ++mChangeNestCount; }
nsresult DidCauseReflow();
void DidDoReflow();
nsresult ProcessReflowCommands(PRBool aInterruptible);
@ -1393,6 +1393,11 @@ protected:
PRPackedBool mIgnoreFrameDestruction;
PRPackedBool mHaveShutDown;
// This is used to protect ourselves from triggering reflow while in the
// middle of frame construction and the like... it really shouldn't be
// needed, one hopes, but it is for now.
PRUint32 mChangeNestCount;
nsIFrame* mCurrentEventFrame;
nsCOMPtr<nsIContent> mCurrentEventContent;
nsVoidArray mCurrentEventFrameStack;
@ -4640,38 +4645,11 @@ PresShell::IsPaintingSuppressed(PRBool* aResult)
void
PresShell::UnsuppressAndInvalidate()
{
nsCOMPtr<nsPIDOMWindow> ourWindow = do_QueryInterface(mDocument->GetScriptGlobalObject());
nsIFocusController *focusController = nsnull;
if (ourWindow)
focusController = ourWindow->GetRootFocusController();
if (focusController)
// Suppress focus. The act of tearing down the old content viewer
// causes us to blur incorrectly.
focusController->SetSuppressFocus(PR_TRUE, "PresShell suppression on Web page loads");
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
if (container) {
nsCOMPtr<nsIDocShell> cvc(do_QueryInterface(container));
if (cvc) {
nsCOMPtr<nsIContentViewer> cv;
cvc->GetContentViewer(getter_AddRefs(cv));
if (cv) {
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
cv->Show();
// Calling |Show| may destroy us. Not sure why yet, but it's
// a smoketest blocker.
if (mIsDestroying) {
if (focusController) {
// Unsuppress focus now that we're exiting this code,
// otherwise we're stuck in focus suppression, which hoses most of Mozilla
focusController->SetSuppressFocus(PR_FALSE, "PresShell suppression on Web page loads");
}
return;
}
}
}
if (!mPresContext->EnsureVisible(PR_FALSE)) {
// No point; we're about to be torn down anyway.
return;
}
mPaintingSuppressed = PR_FALSE;
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
if (rootFrame) {
@ -4680,6 +4658,13 @@ PresShell::UnsuppressAndInvalidate()
rootFrame->Invalidate(rect, PR_FALSE);
}
// This makes sure to get the same thing that nsPresContext::EnsureVisible()
// got.
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
nsCOMPtr<nsPIDOMWindow> ourWindow = do_GetInterface(container);
nsIFocusController* focusController =
ourWindow ? ourWindow->GetRootFocusController() : nsnull;
if (ourWindow)
CheckForFocus(ourWindow, focusController, mDocument);
@ -4928,8 +4913,8 @@ PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
{
aIsSafeToFlush = PR_TRUE;
if (mIsReflowing) {
// Not safe if we are reflowing
if (mIsReflowing || mChangeNestCount) {
// Not safe if we are reflowing or in the middle of frame construction
aIsSafeToFlush = PR_FALSE;
} else {
// Not safe if we are painting
@ -5588,7 +5573,7 @@ PresShell::HandleEvent(nsIView *aView,
NS_ASSERTION(aView, "null view");
aHandled = PR_TRUE;
if (mIsDestroying || mIsReflowing) {
if (mIsDestroying || mIsReflowing || mChangeNestCount) {
return NS_OK;
}
@ -6018,6 +6003,26 @@ PresShell::IsVisible()
return res;
}
NS_IMETHODIMP_(void)
PresShell::WillPaint()
{
// Don't reenter reflow and don't reflow during frame construction
if (mIsReflowing || mChangeNestCount) {
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);
}
nsresult
PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
{
@ -6150,12 +6155,15 @@ PresShell::PostReflowEvent()
nsresult
PresShell::DidCauseReflow()
{
// We may have had more reflow commands appended to the queue during
// our reflow. Make sure these get processed at some point.
if (!gAsyncReflowDuringDocLoad && mDocumentLoading) {
FlushPendingNotifications(Flush_Layout);
} else {
PostReflowEvent();
NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
if (--mChangeNestCount == 0) {
// We may have had more reflow commands appended to the queue during
// our reflow. Make sure these get processed at some point.
if (!gAsyncReflowDuringDocLoad && mDocumentLoading) {
FlushPendingNotifications(Flush_Layout);
} else {
PostReflowEvent();
}
}
return NS_OK;

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

@ -104,15 +104,11 @@
#include "nsIPluginWidget.h"
#include "nsGUIEvent.h"
#include "nsIRenderingContext.h"
#include "nsIContentViewer.h"
#include "nsIDocumentViewer.h"
#include "nsIDocShell.h"
#include "npapi.h"
#include "nsGfxCIID.h"
#include "nsUnicharUtils.h"
#include "nsTransform2D.h"
#include "nsIImageLoadingContent.h"
#include "nsIFocusController.h"
#include "nsPIDOMWindow.h"
#include "nsContentUtils.h"
#include "nsIStringBundle.h"
@ -4104,39 +4100,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::Init(nsPresContext* aPresContext, nsObjectF
// a page is reloaded. Shutdown happens usually when the last instance
// is destroyed. Here we make sure the plugin instance in the old
// document is destroyed before we try to create the new one.
nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
if (container) {
// We need to suppress the focus controller so that destroying the old
// content viewer doesn't transfer focus to the toplevel window.
nsCOMPtr<nsPIDOMWindow> privWindow = do_GetInterface(container);
nsIFocusController *fc = nsnull;
if (privWindow) {
fc = privWindow->GetRootFocusController();
if (fc)
fc->SetSuppressFocus(PR_TRUE, "PluginInstanceOwner::Init Suppression");
}
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
if (docShell) {
nsCOMPtr<nsIContentViewer> cv;
docShell->GetContentViewer(getter_AddRefs(cv));
// Make sure that we're in the presentation that the current
// content viewer knows about
nsCOMPtr<nsIDocumentViewer> docV(do_QueryInterface(cv));
if (docV) {
nsCOMPtr<nsPresContext> currentPresContext;
docV->GetPresContext(getter_AddRefs(currentPresContext));
if (currentPresContext == aPresContext) {
cv->Show();
}
}
}
if (fc)
fc->SetSuppressFocus(PR_FALSE, "PluginInstanceOwner::Init Suppression");
}
aPresContext->EnsureVisible(PR_TRUE);
// register context menu listener
mCXMenuListener = new nsPluginDOMContextMenuListener();

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

@ -499,14 +499,6 @@ public:
*/
NS_IMETHOD IsPainting(PRBool& aIsPainting)=0;
/**
* Flush pending invalidates which have been queued up
* between DisableRefresh and EnableRefresh calls.
*/
NS_IMETHOD FlushPendingInvalidates()=0;
/**
* Set the default background color that the view manager should use
* to paint otherwise unowned areas. If the color isn't known, just set

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

@ -91,6 +91,13 @@ public:
* of having the view trees linked.
*/
NS_IMETHOD_(PRBool) IsVisible() = 0;
/**
* Notify the observer that we're about to start painting. This
* gives the observer a chance to make some last-minute invalidates
* and geometry changes if it wants to.
*/
NS_IMETHOD_(void) WillPaint() = 0;
};
#endif

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

@ -362,10 +362,11 @@ void nsView::SetPositionIgnoringChildWidgets(nscoord aX, nscoord aY)
void nsView::ResetWidgetBounds(PRBool aRecurse, PRBool aMoveOnly,
PRBool aInvalidateChangedSize) {
if (mWindow) {
// If our view manager has refresh disabled, then
// do nothing; the view manager will set our position when
// refresh is reenabled.
// If our view manager has refresh disabled, then do nothing; the view
// manager will set our position when refresh is reenabled. Just let it
// know that it has pending updates.
if (!mViewManager->IsRefreshEnabled()) {
mViewManager->PostPendingUpdate();
return;
}

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

@ -62,6 +62,7 @@
#include "nsInt64.h"
#include "nsScrollPortView.h"
#include "nsHashtable.h"
#include "nsCOMArray.h"
static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID);
static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
@ -427,8 +428,7 @@ static PRBool IsViewVisible(nsView *aView)
// Find out if the root view is visible by asking the view observer
// (this won't be needed anymore if we link view trees across chrome /
// content boundaries in DocumentViewerImpl::MakeWindow).
nsCOMPtr<nsIViewObserver> vo;
aView->GetViewManager()->GetViewObserver(*getter_AddRefs(vo));
nsIViewObserver* vo = aView->GetViewManager()->GetViewObserver();
return vo && vo->IsVisible();
}
@ -482,7 +482,7 @@ nsViewManager::nsViewManager()
// assumed to be cleared here.
mDefaultBackgroundColor = NS_RGBA(0, 0, 0, 0);
mAllowDoubleBuffering = PR_TRUE;
mHasPendingInvalidates = PR_FALSE;
mHasPendingUpdates = PR_FALSE;
mRecursiveRefreshPending = PR_FALSE;
mUpdateBatchFlags = 0;
}
@ -1696,7 +1696,7 @@ nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedReg
// Don't let dirtyRegion grow beyond 8 rects
dirtyRegion->SimplifyOutward(8);
nsViewManager* rootVM = RootViewManager();
rootVM->mHasPendingInvalidates = PR_TRUE;
rootVM->mHasPendingUpdates = PR_TRUE;
rootVM->IncrementUpdateCount();
return;
// this should only happen at the top level, and this result
@ -1940,6 +1940,22 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
UpdateView(view, NS_VMREFRESH_NO_SYNC);
} else {
//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?
nsIViewObserver* observer = GetViewObserver();
if (observer) {
// Do an update view batch, and make sure we don't process those
// invalidates right now. Note that the observer may try to
// reenter this code from inside WillPaint() by trying to do a
// synchronous paint, but since refresh will be disabled it won't
// be able to do the paint. We should really sort out the rules
// on our synch painting api....
BeginUpdateViewBatch();
observer->WillPaint();
EndUpdateViewBatch(NS_VMREFRESH_DEFERRED);
}
Refresh(view, event->renderingContext, region,
NS_VMREFRESH_DOUBLE_BUFFER);
}
@ -2007,9 +2023,11 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
case NS_SYSCOLORCHANGED:
{
// Hold a refcount to the observer. The continued existence of the observer will
// delay deletion of this view hierarchy should the event want to cause its
// destruction in, say, some JavaScript event handler.
nsView *view = nsView::GetViewFor(aEvent->widget);
nsCOMPtr<nsIViewObserver> obs;
GetViewObserver(*getter_AddRefs(obs));
nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
if (obs) {
PRBool handled;
obs->HandleEvent(view, aEvent, aStatus, PR_TRUE, handled);
@ -2344,8 +2362,7 @@ nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent, PRBo
// Hold a refcount to the observer. The continued existence of the observer will
// delay deletion of this view hierarchy should the event want to cause its
// destruction in, say, some JavaScript event handler.
nsCOMPtr<nsIViewObserver> obs;
GetViewObserver(*getter_AddRefs(obs));
nsCOMPtr<nsIViewObserver> obs = GetViewObserver();
// accessibility events and key events are dispatched directly to the focused view
if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT
@ -2361,7 +2378,7 @@ nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent, PRBo
}
nsAutoVoidArray targetViews;
nsAutoVoidArray heldRefCountsToOtherVMs;
nsCOMArray<nsIViewObserver> heldRefCountsToOtherVMs;
// In fact, we only need to take this expensive path when the event is a mouse event ... riiiight?
PLArenaPool displayArena;
@ -2377,10 +2394,9 @@ nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent, PRBo
nsView* v = element->mView;
nsViewManager* vVM = v->GetViewManager();
if (vVM != this) {
nsIViewObserver* vobs = nsnull;
vVM->GetViewObserver(vobs);
if (nsnull != vobs) {
heldRefCountsToOtherVMs.AppendElement(vobs);
nsIViewObserver* vobs = vVM->GetViewObserver();
if (vobs) {
heldRefCountsToOtherVMs.AppendObject(vobs);
}
}
}
@ -2406,8 +2422,10 @@ nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent, PRBo
obs->HandleEvent(v, aEvent, &status, i == targetViews.Count() - 1, handled);
}
} else {
nsCOMPtr<nsIViewObserver> vobs;
vVM->GetViewObserver(*getter_AddRefs(vobs));
// Hold a refcount to the observer. The continued existence of the observer will
// delay deletion of this view hierarchy should the event want to cause its
// destruction in, say, some JavaScript event handler.
nsCOMPtr<nsIViewObserver> vobs = GetViewObserver();
if (vobs) {
vobs->HandleEvent(v, aEvent, &status, i == targetViews.Count() - 1, handled);
}
@ -2427,12 +2445,6 @@ nsEventStatus nsViewManager::HandleEvent(nsView* aView, nsGUIEvent* aEvent, PRBo
PL_FreeArenaPool(&displayArena);
PL_FinishArenaPool(&displayArena);
// release death grips
for (i = 0; i < heldRefCountsToOtherVMs.Count(); i++) {
nsIViewObserver* element = NS_STATIC_CAST(nsIViewObserver*, heldRefCountsToOtherVMs.ElementAt(i));
NS_RELEASE(element);
}
return status;
}
@ -3294,14 +3306,12 @@ NS_IMETHODIMP nsViewManager::EnableRefresh(PRUint32 aUpdateFlags)
// nested batching can combine IMMEDIATE with DEFERRED. Favour
// IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC.
if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
ProcessPendingUpdates(mRootView);
mHasPendingInvalidates = PR_FALSE;
FlushPendingInvalidates();
Composite();
} else if (aUpdateFlags & NS_VMREFRESH_DEFERRED) {
PostInvalidateEvent();
} else { // NO_SYNC
ProcessPendingUpdates(mRootView);
mHasPendingInvalidates = PR_FALSE;
FlushPendingInvalidates();
}
return NS_OK;
@ -4146,18 +4156,53 @@ nsViewManager::IsPainting(PRBool& aIsPainting)
return NS_OK;
}
NS_IMETHODIMP
void
nsViewManager::FlushPendingInvalidates()
{
if (!IsRootVM()) {
return RootViewManager()->FlushPendingInvalidates();
}
NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!\n");
NS_ASSERTION(mUpdateBatchCnt == 0, "Must not be in an update batch!");
// XXXbz this is probably not quite OK yet, if callers can explicitly
// DisableRefresh while we have an event posted.
// NS_ASSERTION(mRefreshEnabled, "How did we get here?");
// Let all the view observers of all viewmanagers in this tree know that
// we're about to "paint" (this lets them get in their invalidates now so
// we don't go through two invalidate-processing cycles).
NS_ASSERTION(gViewManagers, "Better have a viewmanagers array!");
// Disable refresh while we notify our view observers, so that if they do
// vie w update batches we don't reenter this code and so that we batch
// all of them together. We don't use
// BeginUpdateViewBatch/EndUpdateViewBatch, since that would reenter this
// exact code, but we want the effect of a single big update batch.
PRBool refreshEnabled = mRefreshEnabled;
mRefreshEnabled = PR_FALSE;
++mUpdateBatchCnt;
if (mHasPendingInvalidates) {
ProcessPendingUpdates(mRootView);
mHasPendingInvalidates = PR_FALSE;
PRInt32 index;
for (index = 0; index < mVMCount; index++) {
nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
if (vm->RootViewManager() == this) {
// One of our kids
nsIViewObserver* observer = vm->GetViewObserver();
if (observer) {
observer->WillPaint();
NS_ASSERTION(mUpdateBatchCnt == 1, "Observer did not end view batch?");
}
}
}
--mUpdateBatchCnt;
// Someone could have called EnableRefresh on us from inside WillPaint().
// Only reset the old mRefreshEnabled value if the current value is false.
if (!mRefreshEnabled) {
mRefreshEnabled = refreshEnabled;
}
if (mHasPendingUpdates) {
ProcessPendingUpdates(mRootView);
mHasPendingUpdates = PR_FALSE;
}
return NS_OK;
}
void

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

@ -240,7 +240,6 @@ public:
NS_IMETHOD AllowDoubleBuffering(PRBool aDoubleBuffer);
NS_IMETHOD IsPainting(PRBool& aIsPainting);
NS_IMETHOD FlushPendingInvalidates();
NS_IMETHOD SetDefaultBackgroundColor(nscolor aColor);
NS_IMETHOD GetDefaultBackgroundColor(nscolor* aColor);
NS_IMETHOD GetLastUserEventTime(PRUint32& aTime);
@ -268,9 +267,10 @@ public:
protected:
virtual ~nsViewManager();
void ProcessPendingUpdates(nsView *aView);
private:
void FlushPendingInvalidates();
void ProcessPendingUpdates(nsView *aView);
void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
void ReparentWidgets(nsIView* aView, nsIView *aParent);
already_AddRefed<nsIRenderingContext> CreateRenderingContext(nsView &aView);
@ -471,6 +471,11 @@ public: // NOT in nsIViewManager, so private to the view module
PRBool IsRefreshEnabled() { return RootViewManager()->mRefreshEnabled; }
nsIViewObserver* GetViewObserver() { return mObserver; }
// Call this when you need to let the viewmanager know that it now has
// pending updates.
void PostPendingUpdate() { RootViewManager()->mHasPendingUpdates = PR_TRUE; }
private:
nsIDeviceContext *mContext;
float mTwipsToPixels;
@ -512,7 +517,7 @@ private:
// Use IsPainting() and SetPainting() to access mPainting.
PRPackedBool mPainting;
PRPackedBool mRecursiveRefreshPending;
PRPackedBool mHasPendingInvalidates;
PRPackedBool mHasPendingUpdates;
//from here to public should be static and locked... MMP
static PRInt32 mVMCount; //number of viewmanagers