зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
6831f17ebf
Коммит
d222ecb623
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче