Bug 458202 - Speed up event handling, r+sr=jst

This commit is contained in:
Olli Pettay 2008-10-08 14:35:29 +03:00
parent 960a26406e
commit 256070b717
11 changed files with 122 additions and 59 deletions

View File

@ -105,6 +105,7 @@ class nsIUGenCategory;
class nsIWidget;
class nsIDragSession;
class nsPIDOMWindow;
class nsPIDOMEventTarget;
#ifdef MOZ_XTF
class nsIXTFService;
#endif
@ -1338,6 +1339,10 @@ public:
static nsIInterfaceRequestor* GetSameOriginChecker();
static nsIThreadJSContextStack* ThreadJSContextStack()
{
return sThreadJSContextStack;
}
private:
static PRBool InitializeEventTable();
@ -1434,12 +1439,11 @@ public:
~nsCxPusher(); // Calls Pop();
// Returns PR_FALSE if something erroneous happened.
PRBool Push(nsISupports *aCurrentTarget);
PRBool Push(nsPIDOMEventTarget *aCurrentTarget);
PRBool Push(JSContext *cx);
void Pop();
private:
nsCOMPtr<nsIJSContextStack> mStack;
nsCOMPtr<nsIScriptContext> mScx;
PRBool mScriptIsRunning;
};

View File

@ -2691,7 +2691,7 @@ IsContextOnStack(nsIJSContextStack *aStack, JSContext *aContext)
}
PRBool
nsCxPusher::Push(nsISupports *aCurrentTarget)
nsCxPusher::Push(nsPIDOMEventTarget *aCurrentTarget)
{
if (mScx) {
NS_ERROR("Whaaa! No double pushing with nsCxPusher::Push()!");
@ -2699,10 +2699,9 @@ nsCxPusher::Push(nsISupports *aCurrentTarget)
return PR_FALSE;
}
nsCOMPtr<nsPIDOMEventTarget> eventTarget = do_QueryInterface(aCurrentTarget);
NS_ENSURE_TRUE(eventTarget, PR_FALSE);
NS_ENSURE_TRUE(aCurrentTarget, PR_FALSE);
nsCOMPtr<nsIScriptContext> scx;
nsresult rv = eventTarget->GetContextForEventHandlers(getter_AddRefs(scx));
nsresult rv = aCurrentTarget->GetContextForEventHandlers(getter_AddRefs(scx));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
JSContext* cx = nsnull;
@ -2735,18 +2734,15 @@ nsCxPusher::Push(JSContext *cx)
return PR_TRUE;
}
if (!mStack) {
mStack = do_GetService(kJSStackContractID);
}
if (mStack) {
if (IsContextOnStack(mStack, cx)) {
nsIThreadJSContextStack* stack = nsContentUtils::ThreadJSContextStack();
if (stack) {
if (IsContextOnStack(stack, cx)) {
// If the context is on the stack, that means that a script
// is running at the moment in the context.
mScriptIsRunning = PR_TRUE;
}
mStack->Push(cx);
stack->Push(cx);
}
}
return PR_TRUE;
@ -2755,7 +2751,8 @@ nsCxPusher::Push(JSContext *cx)
void
nsCxPusher::Pop()
{
if (!mScx || !mStack) {
nsIThreadJSContextStack* stack = nsContentUtils::ThreadJSContextStack();
if (!mScx || !stack) {
mScx = nsnull;
NS_ASSERTION(!mScriptIsRunning, "Huh, this can't be happening, "
@ -2765,7 +2762,7 @@ nsCxPusher::Pop()
}
JSContext *unused;
mStack->Pop(&unused);
stack->Pop(&unused);
if (!mScriptIsRunning) {
// No JS is running in the context, but executing the event handler might have

View File

@ -5996,7 +5996,8 @@ nsDocument::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// Load events must not propagate to |window| object, see bug 335251.
if (aVisitor.mEvent->message != NS_LOAD) {
aVisitor.mParentTarget = GetWindow();
nsCOMPtr<nsPIDOMEventTarget> parentTarget = do_QueryInterface(GetWindow());
aVisitor.mParentTarget = parentTarget;
}
return NS_OK;
}

View File

@ -132,13 +132,15 @@ public:
nsEventStatus aEventStatus = nsEventStatus_eIgnore)
: nsEventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus),
mCanHandle(PR_TRUE), mForceContentDispatch(PR_FALSE),
mRelatedTargetIsInAnon(PR_FALSE) {}
mRelatedTargetIsInAnon(PR_FALSE), mWantsWillHandleEvent(PR_FALSE),
mParentTarget(nsnull), mEventTargetAtParent(nsnull) {}
void Reset() {
mItemFlags = 0;
mItemData = nsnull;
mCanHandle = PR_TRUE;
mForceContentDispatch = PR_FALSE;
mWantsWillHandleEvent = PR_FALSE;
mParentTarget = nsnull;
mEventTargetAtParent = nsnull;
}
@ -164,16 +166,23 @@ public:
*/
PRPackedBool mRelatedTargetIsInAnon;
/**
* Whether or not nsPIDOMEventTarget::WillHandleEvent will be
* called. Default is PR_FALSE;
*/
PRPackedBool mWantsWillHandleEvent;
/**
* Parent item in the event target chain.
*/
nsCOMPtr<nsISupports> mParentTarget;
nsPIDOMEventTarget* mParentTarget;
/**
* If the event needs to be retargeted, this is the event target,
* which should be used when the event is handled at mParentTarget.
*/
nsCOMPtr<nsISupports> mEventTargetAtParent;
nsPIDOMEventTarget* mEventTargetAtParent;
};
class nsEventChainPostVisitor : public nsEventChainVisitor {

View File

@ -47,13 +47,14 @@ class nsIScriptContext;
class nsIDOMEventTarget;
class nsIDOMEventGroup;
class nsIAtom;
class nsPIDOMEventTarget;
/*
* Event listener manager interface.
*/
#define NS_IEVENTLISTENERMANAGER_IID \
{ 0x0056ac6b, 0xc25b, 0x4fbb, \
{ 0x92, 0x98, 0x8d, 0xce, 0x53, 0x6e } }
{ 0x0cdf1660, 0x3ac1, 0x4b84, \
{ 0xa9, 0x35, 0xc0, 0xc0, 0xe5, 0x5d, 0x73, 0xca } }
class nsIEventListenerManager : public nsISupports {
@ -138,7 +139,7 @@ public:
NS_IMETHOD HandleEvent(nsPresContext* aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
nsISupports* aCurrentTarget,
nsPIDOMEventTarget* aCurrentTarget,
PRUint32 aFlags,
nsEventStatus* aEventStatus) = 0;

View File

@ -94,7 +94,8 @@ public:
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor) = 0;
/**
* Called just before possible event handlers on this object will be called.
* If nsEventChainPreVisitor.mWantsWillHandleEvent is set PR_TRUE,
* called just before possible event handlers on this object will be called.
*/
virtual nsresult WillHandleEvent(nsEventChainPostVisitor& aVisitor)
{

View File

@ -40,27 +40,28 @@
#include "nsPIDOMEventTarget.h"
#include "nsPresContext.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIEventListenerManager.h"
#include "nsEventListenerManager.h"
#include "nsContentUtils.h"
#include "nsDOMError.h"
#include "nsMutationEvent.h"
#include NEW_H
#include "nsFixedSizeAllocator.h"
#define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0)
#define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0)
#define NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT (1 << 1)
// nsEventTargetChainItem represents a single item in the event target chain.
class nsEventTargetChainItem
{
private:
nsEventTargetChainItem(nsISupports* aTarget,
nsEventTargetChainItem(nsPIDOMEventTarget* aTarget,
nsEventTargetChainItem* aChild = nsnull);
void Destroy(nsFixedSizeAllocator* aAllocator);
public:
static nsEventTargetChainItem* Create(nsFixedSizeAllocator* aAllocator,
nsISupports* aTarget,
nsPIDOMEventTarget* aTarget,
nsEventTargetChainItem* aChild = nsnull)
{
void* place = aAllocator->Alloc(sizeof(nsEventTargetChainItem));
@ -88,12 +89,13 @@ public:
return mNewTarget;
}
void SetNewTarget(nsISupports* aNewTarget)
void SetNewTarget(nsPIDOMEventTarget* aNewTarget)
{
mNewTarget = aNewTarget;
}
void SetForceContentDispatch(PRBool aForce) {
void SetForceContentDispatch(PRBool aForce)
{
if (aForce) {
mFlags |= NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH;
} else {
@ -101,10 +103,26 @@ public:
}
}
PRBool ForceContentDispatch() {
PRBool ForceContentDispatch()
{
return !!(mFlags & NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH);
}
void SetWantsWillHandleEvent(PRBool aWants)
{
if (aWants) {
mFlags |= NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT;
} else {
mFlags &= ~NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT;
}
}
PRBool WantsWillHandleEvent()
{
return !!(mFlags & NS_TARGET_CHAIN_WANTS_WILL_HANDLE_EVENT);
}
nsPIDOMEventTarget* CurrentTarget()
{
return mTarget;
@ -118,7 +136,8 @@ public:
*/
nsresult HandleEventTargetChain(nsEventChainPostVisitor& aVisitor,
PRUint32 aFlags,
nsDispatchingCallback* aCallback);
nsDispatchingCallback* aCallback,
PRBool aMayHaveNewListenerManagers);
/**
* Resets aVisitor object and calls PreHandleEvent.
@ -131,7 +150,8 @@ public:
* manager, this method sets the .currentTarget to the CurrentTarget()
* and calls nsIEventListenerManager::HandleEvent().
*/
nsresult HandleEvent(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags);
nsresult HandleEvent(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags,
PRBool aMayHaveNewListenerManagers);
/**
* Copies mItemFlags and mItemData to aVisitor and calls PostHandleEvent.
@ -146,19 +166,16 @@ public:
PRUint16 mItemFlags;
nsCOMPtr<nsISupports> mItemData;
// Event retargeting must happen whenever mNewTarget is non-null.
nsCOMPtr<nsISupports> mNewTarget;
nsCOMPtr<nsPIDOMEventTarget> mNewTarget;
// Cache mTarget's event listener manager.
nsCOMPtr<nsIEventListenerManager> mManager;
};
nsEventTargetChainItem::nsEventTargetChainItem(nsISupports* aTarget,
nsEventTargetChainItem::nsEventTargetChainItem(nsPIDOMEventTarget* aTarget,
nsEventTargetChainItem* aChild)
: mChild(aChild), mParent(nsnull), mFlags(0), mItemFlags(0)
{
nsCOMPtr<nsPIDOMEventTarget> t = do_QueryInterface(aTarget);
if (t) {
mTarget = t->GetTargetForEventTargetChain();
}
mTarget = aTarget->GetTargetForEventTargetChain();
if (mChild) {
mChild->mParent = this;
}
@ -186,6 +203,7 @@ nsEventTargetChainItem::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
aVisitor.Reset();
nsresult rv = mTarget->PreHandleEvent(aVisitor);
SetForceContentDispatch(aVisitor.mForceContentDispatch);
SetWantsWillHandleEvent(aVisitor.mWantsWillHandleEvent);
mItemFlags = aVisitor.mItemFlags;
mItemData = aVisitor.mItemData;
return rv;
@ -193,21 +211,28 @@ nsEventTargetChainItem::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
nsresult
nsEventTargetChainItem::HandleEvent(nsEventChainPostVisitor& aVisitor,
PRUint32 aFlags)
PRUint32 aFlags,
PRBool aMayHaveNewListenerManagers)
{
mTarget->WillHandleEvent(aVisitor);
if (WantsWillHandleEvent()) {
mTarget->WillHandleEvent(aVisitor);
}
if (aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
return NS_OK;
}
if (!mManager) {
if (!aMayHaveNewListenerManagers) {
return NS_OK;
}
mTarget->GetListenerManager(PR_FALSE, getter_AddRefs(mManager));
}
if (mManager) {
aVisitor.mEvent->currentTarget = CurrentTarget()->GetTargetForDOMEvent();
nsPIDOMEventTarget* currentTarget = CurrentTarget()->GetTargetForDOMEvent();
aVisitor.mEvent->currentTarget = currentTarget;
if (aVisitor.mEvent->currentTarget) {
mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
&aVisitor.mDOMEvent,
aVisitor.mEvent->currentTarget, aFlags,
currentTarget, aFlags,
&aVisitor.mEventStatus);
aVisitor.mEvent->currentTarget = nsnull;
}
@ -226,8 +251,10 @@ nsEventTargetChainItem::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
nsresult
nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor, PRUint32 aFlags,
nsDispatchingCallback* aCallback)
nsDispatchingCallback* aCallback,
PRBool aMayHaveNewListenerManagers)
{
PRUint32 createdELMs = nsEventListenerManager::sCreatedCount;
// Save the target so that it can be restored later.
nsCOMPtr<nsISupports> firstTarget = aVisitor.mEvent->target;
@ -239,7 +266,9 @@ nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor
if ((!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
item->ForceContentDispatch()) &&
!(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH)) {
item->HandleEvent(aVisitor, aFlags & NS_EVENT_CAPTURE_MASK);
item->HandleEvent(aVisitor, aFlags & NS_EVENT_CAPTURE_MASK,
aMayHaveNewListenerManagers ||
createdELMs != nsEventListenerManager::sCreatedCount);
}
if (item->GetNewTarget()) {
@ -266,7 +295,9 @@ nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor
// FIXME Should use aFlags & NS_EVENT_BUBBLE_MASK because capture phase
// event listeners should not be fired. But it breaks at least
// <xul:dialog>'s buttons. Bug 235441.
item->HandleEvent(aVisitor, aFlags);
item->HandleEvent(aVisitor, aFlags,
aMayHaveNewListenerManagers ||
createdELMs != nsEventListenerManager::sCreatedCount);
}
if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
item->PostHandleEvent(aVisitor);
@ -289,7 +320,8 @@ nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor
(!(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) ||
aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) &&
!(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH)) {
item->HandleEvent(aVisitor, aFlags & NS_EVENT_BUBBLE_MASK);
item->HandleEvent(aVisitor, aFlags & NS_EVENT_BUBBLE_MASK,
createdELMs != nsEventListenerManager::sCreatedCount);
}
if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
item->PostHandleEvent(aVisitor);
@ -318,7 +350,8 @@ nsEventTargetChainItem::HandleEventTargetChain(nsEventChainPostVisitor& aVisitor
// Setting back the target which was used also for default event group.
aVisitor.mEvent->target = firstTarget;
HandleEventTargetChain(aVisitor, aFlags | NS_EVENT_FLAG_SYSTEM_EVENT,
aCallback);
aCallback,
createdELMs != nsEventListenerManager::sCreatedCount);
}
return NS_OK;
@ -378,6 +411,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
NS_ENSURE_TRUE(!NS_IS_EVENT_IN_DISPATCH(aEvent),
NS_ERROR_ILLEGAL_VALUE);
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(aTarget);
#ifdef DEBUG
if (aDOMEvent) {
nsCOMPtr<nsIPrivateDOMEvent> privEvt(do_QueryInterface(aDOMEvent));
@ -401,7 +435,7 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
// Create the event target chain item for the event target.
nsEventTargetChainItem* targetEtci =
nsEventTargetChainItem::Create(pool.GetPool(), aTarget);
nsEventTargetChainItem::Create(pool.GetPool(), target);
NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY);
if (!targetEtci->IsValid()) {
nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci);
@ -438,7 +472,8 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
if (preVisitor.mCanHandle) {
// At least the original target can handle the event.
// Setting the retarget to the |target| simplifies retargeting code.
targetEtci->SetNewTarget(aEvent->target);
nsCOMPtr<nsPIDOMEventTarget> t = do_QueryInterface(aEvent->target);
targetEtci->SetNewTarget(t);
nsEventTargetChainItem* topEtci = targetEtci;
while (preVisitor.mParentTarget) {
nsEventTargetChainItem* parentEtci =
@ -476,7 +511,8 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
rv = topEtci->HandleEventTargetChain(postVisitor,
NS_EVENT_FLAG_BUBBLE |
NS_EVENT_FLAG_CAPTURE,
aCallback);
aCallback,
PR_TRUE);
preVisitor.mEventStatus = postVisitor.mEventStatus;
// If the DOM event was created during event flow.

View File

@ -339,6 +339,7 @@ nsIDOMEventGroup* gDOM2EventGroup = nsnull;
nsDataHashtable<nsISupportsHashKey, PRUint32>* gEventIdTable = nsnull;
PRUint32 nsEventListenerManager::mInstanceCount = 0;
PRUint32 nsEventListenerManager::sCreatedCount = 0;
nsEventListenerManager::nsEventListenerManager() :
mTarget(nsnull),
@ -346,6 +347,7 @@ nsEventListenerManager::nsEventListenerManager() :
mNoListenerForEvent(NS_EVENT_TYPE_NULL)
{
++mInstanceCount;
++sCreatedCount;
}
nsEventListenerManager::~nsEventListenerManager()
@ -1072,10 +1074,7 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
}
}
// nsCxPusher will push and pop (automatically) the current cx onto the
// context stack
nsCxPusher pusher;
if (NS_SUCCEEDED(result) && pusher.Push(aCurrentTarget)) {
if (NS_SUCCEEDED(result)) {
// nsIDOMEvent::currentTarget is set in nsEventDispatcher.
result = aListener->HandleEvent(aDOMEvent);
}
@ -1095,7 +1094,7 @@ static const EventDispatchData* sLatestEventDispData = nsnull;
nsresult
nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
nsISupports* aCurrentTarget,
nsPIDOMEventTarget* aCurrentTarget,
PRUint32 aFlags,
nsEventStatus* aEventStatus)
{
@ -1155,6 +1154,10 @@ found:
nsAutoTObserverArray<nsListenerStruct, 2>::EndLimitedIterator iter(mListeners);
nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
PRBool hasListener = PR_FALSE;
// nsCxPusher will push and pop (automatically) the current cx onto the
// context stack
nsCxPusher pusher;
PRBool didPush = PR_FALSE;
while (iter.HasMore()) {
nsListenerStruct* ls = &iter.GetNext();
PRBool useTypeInterface =
@ -1178,9 +1181,14 @@ found:
if (*aDOMEvent) {
nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = ls->mListener;
if (useTypeInterface) {
if (didPush) {
didPush = PR_FALSE;
pusher.Pop();
}
DispatchToInterface(*aDOMEvent, ls->mListener,
dispData->method, *typeData->iid);
} else if (useGenericInterface) {
} else if (useGenericInterface &&
(didPush || (didPush = pusher.Push(aCurrentTarget)))) {
HandleEventSubType(ls, ls->mListener, *aDOMEvent,
aCurrentTarget, aFlags);
}

View File

@ -52,6 +52,7 @@ class nsIAtom;
class nsIWidget;
struct nsPoint;
struct EventTypeData;
class nsEventTargetChainItem;
typedef struct {
nsRefPtr<nsIDOMEventListener> mListener;
@ -113,7 +114,7 @@ public:
NS_IMETHOD HandleEvent(nsPresContext* aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
nsISupports* aCurrentTarget,
nsPIDOMEventTarget* aCurrentTarget,
PRUint32 aFlags,
nsEventStatus* aEventStatus);
@ -202,6 +203,9 @@ protected:
static PRUint32 mInstanceCount;
static jsval sAddListenerID;
friend class nsEventTargetChainItem;
static PRUint32 sCreatedCount;
};
#endif // nsEventListenerManager_h__

View File

@ -5129,7 +5129,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext,
return NS_OK;
}
if (pusher.Push(window)) {
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(window);
if (pusher.Push(target)) {
nsEventDispatcher::Dispatch(window, gLastFocusedPresContextWeak, &event,
nsnull, &status);

View File

@ -808,6 +808,7 @@ nsHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
nsresult
nsHTMLFormElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
{
aVisitor.mWantsWillHandleEvent = PR_TRUE;
if (aVisitor.mEvent->originalTarget == static_cast<nsIContent*>(this)) {
PRUint32 msg = aVisitor.mEvent->message;
if (msg == NS_FORM_SUBMIT) {