Bug 455988. Optimize MozAfterPaint so we never even dispatch an XPCOM event if there are no listeners. r=smaug,sr=mats

This commit is contained in:
Robert O'Callahan 2008-10-16 10:06:32 +13:00
Родитель 7211ccf1cd
Коммит 6899d04162
6 изменённых файлов: 119 добавлений и 38 удалений

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

@ -590,6 +590,9 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
aNode->GetListenerManager(PR_FALSE, getter_AddRefs(elm));
if (elm) {
window->SetMutationListeners(elm->MutationListenerBits());
if (elm->MayHavePaintEventListener()) {
window->SetHasPaintEventListeners();
}
}
}
}

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

@ -38,7 +38,7 @@
#ifndef nsIEventListenerManager_h__
#define nsIEventListenerManager_h__
#include "nsEvent.h"
#include "nsGUIEvent.h"
#include "nsISupports.h"
class nsPresContext;
@ -53,15 +53,19 @@ class nsPIDOMEventTarget;
* Event listener manager interface.
*/
#define NS_IEVENTLISTENERMANAGER_IID \
{ 0x0cdf1660, 0x3ac1, 0x4b84, \
{ 0xa9, 0x35, 0xc0, 0xc0, 0xe5, 0x5d, 0x73, 0xca } }
{ 0xadfdc265, 0xea1c, 0x4c0b, \
{ 0x91, 0xca, 0x37, 0x67, 0x2c, 0x83, 0x92, 0x1f } }
class nsIEventListenerManager : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEVENTLISTENERMANAGER_IID)
nsIEventListenerManager() : mMayHavePaintEventListener(PR_FALSE),
mMayHaveMutationListeners(PR_FALSE),
mNoListenerForEvent(NS_EVENT_TYPE_NULL)
{}
/**
* Sets events listeners of all types.
* @param an event listener
@ -194,6 +198,21 @@ public:
* Returns PR_TRUE if there is at least one event listener.
*/
virtual PRBool HasListeners() = 0;
/**
* Returns PR_TRUE if there may be a paint event listener registered,
* PR_FALSE if there definitely isn't.
*/
PRBool MayHavePaintEventListener() { return mMayHavePaintEventListener; }
protected:
PRUint32 mMayHavePaintEventListener : 1;
PRUint32 mMayHaveMutationListeners : 1;
// These two member variables are used to cache the information
// about the last event which was handled but for which event listener manager
// didn't have event listeners.
PRUint32 mNoListenerForEvent : 30;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIEventListenerManager,

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

@ -342,9 +342,7 @@ PRUint32 nsEventListenerManager::mInstanceCount = 0;
PRUint32 nsEventListenerManager::sCreatedCount = 0;
nsEventListenerManager::nsEventListenerManager() :
mTarget(nsnull),
mMayHaveMutationListeners(PR_FALSE),
mNoListenerForEvent(NS_EVENT_TYPE_NULL)
mTarget(nsnull)
{
++mInstanceCount;
++sCreatedCount;
@ -430,6 +428,27 @@ nsEventListenerManager::GetTypeDataForEventName(nsIAtom* aName)
return nsnull;
}
nsPIDOMWindow*
nsEventListenerManager::GetInnerWindowForTarget()
{
nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
if (node) {
// XXX sXBL/XBL2 issue -- do we really want the owner here? What
// if that's the XBL document?
nsIDocument* document = node->GetOwnerDoc();
if (document)
return document->GetInnerWindow();
}
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
if (window) {
NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
return window;
}
return nsnull;
}
nsresult
nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
PRUint32 aType,
@ -497,29 +516,19 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
ls->mHandlerIsString = PR_FALSE;
ls->mTypeData = aTypeData;
// For mutation listeners, we need to update the global bit on the DOM window.
// Otherwise we won't actually fire the mutation event.
if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
if (aType == NS_AFTERPAINT) {
mMayHavePaintEventListener = PR_TRUE;
nsPIDOMWindow* window = GetInnerWindowForTarget();
if (window) {
window->SetHasPaintEventListener();
}
} else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
// For mutation listeners, we need to update the global bit on the DOM window.
// Otherwise we won't actually fire the mutation event.
mMayHaveMutationListeners = PR_TRUE;
// Go from our target to the nearest enclosing DOM window.
nsCOMPtr<nsPIDOMWindow> window;
nsCOMPtr<nsIDocument> document;
nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
if (node) {
// XXX sXBL/XBL2 issue -- do we really want the owner here? What
// if that's the XBL document?
document = node->GetOwnerDoc();
if (document) {
window = document->GetInnerWindow();
}
}
if (!window) {
window = do_QueryInterface(mTarget);
}
nsPIDOMWindow* window = GetInnerWindowForTarget();
if (window) {
NS_ASSERTION(window->IsInnerWindow(),
"Setting mutation listener bits on outer window?");
// If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
// mutations. nsContentUtils::HasMutationListeners relies on this.
window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?

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

@ -191,14 +191,10 @@ protected:
nsIPresShell *aPresShell, nsPoint& aTargetPt);
nsresult GetDOM2EventGroup(nsIDOMEventGroup** aGroup);
PRBool ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent);
nsPIDOMWindow* GetInnerWindowForTarget();
nsAutoTObserverArray<nsListenerStruct, 2> mListeners;
nsISupports* mTarget; //WEAK
PRUint32 mMayHaveMutationListeners : 1;
// These two member variables are used to cache the information
// about the last event which was handled but for which event listener manager
// didn't have event listeners.
PRUint32 mNoListenerForEvent : 31;
nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
static PRUint32 mInstanceCount;

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

@ -76,8 +76,8 @@ class nsScriptObjectHolder;
class nsXBLPrototypeHandler;
#define NS_PIDOMWINDOW_IID \
{ 0x909852b5, 0xb9e6, 0x4d94, \
{ 0x8d, 0xe3, 0x05, 0x16, 0x34, 0x80, 0x0b, 0x73 } }
{ 0x3d2b6b38, 0x810d, 0x4ac5, \
{ 0x81, 0x7c, 0xb9, 0x70, 0x81, 0x80, 0x4d, 0x9f } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -379,6 +379,24 @@ public:
return mIsModalContentWindow;
}
/**
* Call this to indicate that some node (this window, its document,
* or content in that document) has a paint event listener.
*/
void SetHasPaintEventListeners()
{
mMayHavePaintEventListener = PR_TRUE;
}
/**
* Call this to check whether some node (this window, its document,
* or content in that document) has a paint event listener.
*/
PRBool HasPaintEventListeners()
{
return mMayHavePaintEventListener;
}
/**
* Initialize window.java and window.Packages, and start LiveConnect
* if we're running with a non-NPRuntime enabled Java plugin.
@ -398,6 +416,7 @@ protected:
: mFrameElement(nsnull), mDocShell(nsnull), mModalStateDepth(0),
mRunningTimeout(nsnull), mMutationBits(0), mIsDocumentLoaded(PR_FALSE),
mIsHandlingResizeEvent(PR_FALSE), mIsInnerWindow(aOuterWindow != nsnull),
mMayHavePaintEventListener(PR_FALSE),
mIsModalContentWindow(PR_FALSE), mInnerWindow(nsnull),
mOuterWindow(aOuterWindow)
{
@ -427,6 +446,7 @@ protected:
PRPackedBool mIsDocumentLoaded;
PRPackedBool mIsHandlingResizeEvent;
PRPackedBool mIsInnerWindow;
PRPackedBool mMayHavePaintEventListener;
// This variable is used on both inner and outer windows (and they
// should match).

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

@ -84,6 +84,7 @@
#include "nsRuleNode.h"
#include "nsEventDispatcher.h"
#include "gfxUserFontSet.h"
#include "nsIEventListenerManager.h"
#ifdef IBMBIDI
#include "nsBidiPresUtils.h"
@ -1541,10 +1542,10 @@ nsPresContext::SetUserFontSet(gfxUserFontSet *aUserFontSet)
void
nsPresContext::FireDOMPaintEvent()
{
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
if (!docShell)
nsCOMPtr<nsPIDOMWindow> ourWindow = mDocument->GetWindow();
if (!ourWindow)
return;
nsCOMPtr<nsPIDOMWindow> ourWindow = do_GetInterface(docShell);
nsISupports* eventTarget = ourWindow;
if (mSameDocDirtyRegion.IsEmpty() && !IsChrome()) {
// Don't tell the window about this event, it should not know that
@ -1570,10 +1571,43 @@ nsPresContext::FireDOMPaintEvent()
nsEventDispatcher::Dispatch(eventTarget, this, &event);
}
static PRBool MayHavePaintEventListener(nsPIDOMWindow* aInnerWindow)
{
if (!aInnerWindow)
return PR_FALSE;
if (aInnerWindow->HasPaintEventListeners())
return PR_TRUE;
nsPIDOMEventTarget* chromeEventHandler = aInnerWindow->GetChromeEventHandler();
if (!chromeEventHandler)
return PR_FALSE;
nsCOMPtr<nsIEventListenerManager> manager;
chromeEventHandler->GetListenerManager(PR_FALSE, getter_AddRefs(manager));
if (manager && manager->MayHavePaintEventListener())
return PR_TRUE;
nsCOMPtr<nsINode> node = do_QueryInterface(chromeEventHandler);
if (node)
return MayHavePaintEventListener(node->GetOwnerDoc()->GetInnerWindow());
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(chromeEventHandler);
if (window)
return MayHavePaintEventListener(window);
return PR_FALSE;
}
void
nsPresContext::NotifyInvalidation(const nsRect& aRect, PRBool aIsCrossDoc)
{
if (aRect.IsEmpty())
// If there is no paint event listener, then we don't need to fire
// the asynchronous event. We don't even need to record invalidation.
// MayHavePaintEventListener is pretty cheap and we could make it
// even cheaper by providing a more efficient
// nsPIDOMWindow::GetListenerManager.
if (aRect.IsEmpty() ||
!MayHavePaintEventListener(mDocument->GetInnerWindow()))
return;
if (mSameDocDirtyRegion.IsEmpty() && mCrossDocDirtyRegion.IsEmpty()) {