Bug 458202 - Speed up event handling, r+sr=jst
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Wed, 08 Oct 2008 14:35:29 +0300
changeset 20135 3447eeec927102e5204d71f7137c1de5b6b51c2e
parent 20134 3a3f885ce36da6030bc9c95cfa56bd8fe4da45ce
child 20136 ae1b32380e6501b4b63f2497a850088d2dcbc669
push idunknown
push userunknown
push dateunknown
bugs458202
milestone1.9.1b2pre
Bug 458202 - Speed up event handling, r+sr=jst
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/events/public/nsEventDispatcher.h
content/events/public/nsIEventListenerManager.h
content/events/public/nsPIDOMEventTarget.h
content/events/src/nsEventDispatcher.cpp
content/events/src/nsEventListenerManager.cpp
content/events/src/nsEventListenerManager.h
content/events/src/nsEventStateManager.cpp
content/html/content/src/nsHTMLFormElement.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -100,16 +100,17 @@ template<class E> class nsCOMArray;
 class nsIPref;
 class nsVoidArray;
 struct JSRuntime;
 class nsICaseConversion;
 class nsIUGenCategory;
 class nsIWidget;
 class nsIDragSession;
 class nsPIDOMWindow;
+class nsPIDOMEventTarget;
 #ifdef MOZ_XTF
 class nsIXTFService;
 #endif
 #ifdef IBMBIDI
 class nsIBidiKeyboard;
 #endif
 
 extern const char kLoadAsData[];
@@ -1332,17 +1333,21 @@ public:
 
   static nsresult GetContextForEventHandlers(nsINode* aNode,
                                              nsIScriptContext** aContext);
 
   static JSContext *GetCurrentJSContext();
 
                                              
   static nsIInterfaceRequestor* GetSameOriginChecker();
-                                           
+
+  static nsIThreadJSContextStack* ThreadJSContextStack()
+  {
+    return sThreadJSContextStack;
+  }
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult doReparentContentWrapper(nsIContent *aChild,
                                            JSContext *cx,
                                            JSObject *aOldGlobal,
                                            JSObject *aNewGlobal,
@@ -1429,22 +1434,21 @@ private:
 
 class NS_STACK_CLASS nsCxPusher
 {
 public:
   nsCxPusher();
   ~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;
 };
 
 class nsAutoGCRoot {
 public:
   // aPtr should be the pointer to the jsval we want to protect
   nsAutoGCRoot(jsval* aPtr, nsresult* aResult) :
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2686,28 +2686,27 @@ IsContextOnStack(nsIJSContextStack *aSta
     if (nsJSUtils::GetDynamicScriptContext(ctx) && ctx == aContext)
       return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
 PRBool
-nsCxPusher::Push(nsISupports *aCurrentTarget)
+nsCxPusher::Push(nsPIDOMEventTarget *aCurrentTarget)
 {
   if (mScx) {
     NS_ERROR("Whaaa! No double pushing with nsCxPusher::Push()!");
 
     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;
 
   if (scx) {
     cx = static_cast<JSContext*>(scx->GetNativeContext());
     // Bad, no JSContext from script context!
     NS_ENSURE_TRUE(cx, PR_FALSE);
   }
@@ -2730,47 +2729,45 @@ nsCxPusher::Push(JSContext *cx)
 
   if (cx) {
     mScx = GetScriptContextFromJSContext(cx);
     if (!mScx) {
       // Should probably return PR_FALSE. See bug 416916.
       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;
 }
 
 void
 nsCxPusher::Pop()
 {
-  if (!mScx || !mStack) {
+  nsIThreadJSContextStack* stack = nsContentUtils::ThreadJSContextStack();
+  if (!mScx || !stack) {
     mScx = nsnull;
 
     NS_ASSERTION(!mScriptIsRunning, "Huh, this can't be happening, "
                  "mScriptIsRunning can't be set here!");
 
     return;
   }
 
   JSContext *unused;
-  mStack->Pop(&unused);
+  stack->Pop(&unused);
 
   if (!mScriptIsRunning) {
     // No JS is running in the context, but executing the event handler might have
     // caused some JS to run. Tell the script context that it's done.
 
     mScx->ScriptEvaluated(PR_TRUE);
   }
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5991,17 +5991,18 @@ nsDocument::PreHandleEvent(nsEventChainP
 {
   aVisitor.mCanHandle = PR_TRUE;
    // FIXME! This is a hack to make middle mouse paste working also in Editor.
    // Bug 329119
   aVisitor.mForceContentDispatch = PR_TRUE;
 
   // 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;
 }
 
 nsresult
 nsDocument::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   return NS_OK;
--- a/content/events/public/nsEventDispatcher.h
+++ b/content/events/public/nsEventDispatcher.h
@@ -127,23 +127,25 @@ public:
 class nsEventChainPreVisitor : public nsEventChainVisitor {
 public:
   nsEventChainPreVisitor(nsPresContext* aPresContext,
                          nsEvent* aEvent,
                          nsIDOMEvent* aDOMEvent,
                          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;
   }
 
   /**
    * Member that must be set in PreHandleEvent by event targets. If set to false,
    * indicates that this event target will not be handling the event and
    * construction of the event target chain is complete. The target that sets
@@ -158,27 +160,34 @@ public:
    */
   PRPackedBool          mForceContentDispatch;
 
   /**
    * PR_TRUE if it is known that related target is or is a descendant of an
    * element which is anonymous for events.
    */
   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 {
 public:
   nsEventChainPostVisitor(nsEventChainVisitor& aOther)
   : nsEventChainVisitor(aOther.mPresContext, aOther.mEvent, aOther.mDOMEvent,
                         aOther.mEventStatus)
   {}
--- a/content/events/public/nsIEventListenerManager.h
+++ b/content/events/public/nsIEventListenerManager.h
@@ -42,23 +42,24 @@
 #include "nsISupports.h"
 
 class nsPresContext;
 class nsIDOMEventListener;
 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 {
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEVENTLISTENERMANAGER_IID)
 
   /**
@@ -133,17 +134,17 @@ public:
   /**
   * Causes a check for event listeners and processing by them if they exist.
   * Event flags live in nsGUIEvent.h
   * @param an event listener
   */
   NS_IMETHOD HandleEvent(nsPresContext* aPresContext,
                          nsEvent* aEvent,
                          nsIDOMEvent** aDOMEvent,
-                         nsISupports* aCurrentTarget,
+                         nsPIDOMEventTarget* aCurrentTarget,
                          PRUint32 aFlags,
                          nsEventStatus* aEventStatus) = 0;
 
   /**
   * Tells the event listener manager that its target (which owns it) is
   * no longer using it (and could go away).
   *
   * It also clears the weak pointer set by the call to
--- a/content/events/public/nsPIDOMEventTarget.h
+++ b/content/events/public/nsPIDOMEventTarget.h
@@ -89,17 +89,18 @@ public:
    * @param aVisitor the visitor object which is used to create the
    *                 event target chain for event dispatching.
    *
    * @note Only nsEventDispatcher should call this method.
    */
   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)
   {
     return NS_OK;
   }
 
   /**
    * Called after the bubble phase of the system event group.
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -35,37 +35,38 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsEventDispatcher.h"
 #include "nsDOMEvent.h"
 #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));
     return place
       ? ::new (place) nsEventTargetChainItem(aTarget, aChild)
       : nsnull;
   }
 
@@ -83,87 +84,103 @@ public:
     return !!(mTarget);
   }
 
   nsISupports* GetNewTarget()
   {
     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 {
       mFlags &= ~NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH;
     }
   }
 
-  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;
   }
 
   /**
    * Dispatches event through the event target chain.
    * Handles capture, target and bubble phases both in default
    * and system event group and calls also PostHandleEvent for each
    * item in the chain.
    */
   nsresult HandleEventTargetChain(nsEventChainPostVisitor& aVisitor,
                                   PRUint32 aFlags,
-                                  nsDispatchingCallback* aCallback);
+                                  nsDispatchingCallback* aCallback,
+                                  PRBool aMayHaveNewListenerManagers);
 
   /**
    * Resets aVisitor object and calls PreHandleEvent.
    * Copies mItemFlags and mItemData to the current nsEventTargetChainItem.
    */
   nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
 
   /**
    * If the current item in the event target chain has an event listener
    * 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.
    */
   nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
 
   nsCOMPtr<nsPIDOMEventTarget>      mTarget;
   nsEventTargetChainItem*           mChild;
   nsEventTargetChainItem*           mParent;
   PRUint16                          mFlags;
   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;
   }
 }
 
 void
 nsEventTargetChainItem::Destroy(nsFixedSizeAllocator* aAllocator)
 {
@@ -181,38 +198,46 @@ nsEventTargetChainItem::Destroy(nsFixedS
 }
 
 nsresult
 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;
 }
 
 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;
     }
   }
   return NS_OK;
 }
 
 nsresult
@@ -221,30 +246,34 @@ nsEventTargetChainItem::PostHandleEvent(
   aVisitor.mItemFlags = mItemFlags;
   aVisitor.mItemData = mItemData;
   mTarget->PostHandleEvent(aVisitor);
   return NS_OK;
 }
 
 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;
 
   // Capture
   nsEventTargetChainItem* item = this;
   aVisitor.mEvent->flags |= NS_EVENT_FLAG_CAPTURE;
   aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
   while (item->mChild) {
     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()) {
       // item is at anonymous boundary. Need to retarget for the child items.
       nsEventTargetChainItem* nextTarget = item->mChild;
       while (nextTarget) {
         nsISupports* newTarget = nextTarget->GetNewTarget();
         if (newTarget) {
@@ -261,17 +290,19 @@ nsEventTargetChainItem::HandleEventTarge
   // Target
   aVisitor.mEvent->flags |= NS_EVENT_FLAG_BUBBLE;
   if (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) &&
       (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
        item->ForceContentDispatch())) {
     // 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);
   }
 
   // Bubble
   aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_CAPTURE;
   item = item->mParent;
@@ -284,17 +315,18 @@ nsEventTargetChainItem::HandleEventTarge
     }
 
     if (!(aVisitor.mEvent->flags & NS_EVENT_FLAG_CANT_BUBBLE) || newTarget) {
       if ((!(aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH) ||
            item->ForceContentDispatch()) &&
           (!(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);
       }
     }
     item = item->mParent;
   }
   aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_BUBBLE;
@@ -313,17 +345,18 @@ nsEventTargetChainItem::HandleEventTarge
     if (aCallback) {
       aCallback->HandleEvent(aVisitor);
     }
 
     // Retarget for system event group (which does the default handling too).
     // 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;
 }
 
 class ChainItemPool {
 public:
   ChainItemPool() {
@@ -373,16 +406,17 @@ nsEventDispatcher::Dispatch(nsISupports*
                             nsIDOMEvent* aDOMEvent,
                             nsEventStatus* aEventStatus,
                             nsDispatchingCallback* aCallback)
 {
   NS_ASSERTION(aEvent, "Trying to dispatch without nsEvent!");
   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));
     if (privEvt) {
       nsEvent* innerEvent = nsnull;
       privEvt->GetInternalNSEvent(&innerEvent);
       NS_ASSERTION(innerEvent == aEvent,
                     "The inner event of aDOMEvent is not the same as aEvent!");
@@ -396,17 +430,17 @@ nsEventDispatcher::Dispatch(nsISupports*
   // If we have a PresContext, make sure it doesn't die before
   // event dispatching is finished.
   nsCOMPtr<nsPresContext> kungFuDeathGrip(aPresContext);
   ChainItemPool pool;
   NS_ENSURE_TRUE(pool.GetPool(), NS_ERROR_OUT_OF_MEMORY);
 
   // 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);
     return NS_ERROR_FAILURE;
   }
 
   // Make sure that nsIDOMEvent::target and nsIDOMNSEvent::originalTarget
   // point to the last item in the chain.
@@ -433,17 +467,18 @@ nsEventDispatcher::Dispatch(nsISupports*
   // PreHandleEvent for the original target.
   nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
   nsEventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status);
   targetEtci->PreHandleEvent(preVisitor);
 
   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 =
         nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget,
                                        topEtci);
       if (!parentEtci) {
         rv = NS_ERROR_OUT_OF_MEMORY;
         break;
@@ -471,17 +506,18 @@ nsEventDispatcher::Dispatch(nsISupports*
       }
     }
     if (NS_SUCCEEDED(rv)) {
       // Event target chain is created. Handle the chain.
       nsEventChainPostVisitor postVisitor(preVisitor);
       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.
       if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
         preVisitor.mDOMEvent = postVisitor.mDOMEvent;
       }
     }
   }
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -334,23 +334,25 @@ static const EventTypeData sEventTypes[]
 
 // Strong references to event groups
 nsIDOMEventGroup* gSystemEventGroup = nsnull;
 nsIDOMEventGroup* gDOM2EventGroup = nsnull;
 
 nsDataHashtable<nsISupportsHashKey, PRUint32>* gEventIdTable = nsnull;
 
 PRUint32 nsEventListenerManager::mInstanceCount = 0;
+PRUint32 nsEventListenerManager::sCreatedCount = 0;
 
 nsEventListenerManager::nsEventListenerManager() :
   mTarget(nsnull),
   mMayHaveMutationListeners(PR_FALSE),
   mNoListenerForEvent(NS_EVENT_TYPE_NULL)
 {
   ++mInstanceCount;
+  ++sCreatedCount;
 }
 
 nsEventListenerManager::~nsEventListenerManager() 
 {
   NS_ASSERTION(!mTarget, "didn't call Disconnect");
   RemoveAllListeners();
 
   --mInstanceCount;
@@ -1067,20 +1069,17 @@ nsEventListenerManager::HandleEventSubTy
                                              jslistener->GetEventScope(),
                                              jslistener->GetEventTarget(),
                                              atom, aListenerStruct,
                                              aCurrentTarget);
       }
     }
   }
 
-  // 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);
   }
 
   return result;
 }
 
 static PRUint32                 sLatestEventType = 0;
@@ -1090,17 +1089,17 @@ static const EventDispatchData* sLatestE
 /**
 * Causes a check for event listeners and processing by them if they exist.
 * @param an event listener
 */
 
 nsresult
 nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
                                     nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
-                                    nsISupports* aCurrentTarget,
+                                    nsPIDOMEventTarget* aCurrentTarget,
                                     PRUint32 aFlags,
                                     nsEventStatus* aEventStatus)
 {
   if (mListeners.IsEmpty() || aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
     return NS_OK;
   }
 
   // Check if we already know that there is no event listener for the event.
@@ -1150,16 +1149,20 @@ nsEventListenerManager::HandleEvent(nsPr
     }
   }
 
 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 =
       EVENT_TYPE_DATA_EQUALS(ls->mTypeData, typeData);
     PRBool useGenericInterface =
       (!useTypeInterface && ListenerCanHandle(ls, aEvent));
     // Don't fire the listener if it's been removed.
     // Check that the phase is same in event and event listener.
@@ -1173,19 +1176,24 @@ found:
              ls->mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED)) {
           if (!*aDOMEvent) {
             nsEventDispatcher::CreateEvent(aPresContext, aEvent,
                                            EmptyString(), aDOMEvent);
           }
           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);
             }
           }
         }
       }
     }
   }
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -47,16 +47,17 @@
 #include "nsIScriptContext.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsIDOMEvent;
 class nsIAtom;
 class nsIWidget;
 struct nsPoint;
 struct EventTypeData;
+class nsEventTargetChainItem;
 
 typedef struct {
   nsRefPtr<nsIDOMEventListener> mListener;
   PRUint32                      mEventType;
   nsCOMPtr<nsIAtom>             mTypeAtom;
   PRUint16                      mFlags;
   PRUint16                      mGroupFlags;
   PRBool                        mHandlerIsString;
@@ -108,17 +109,17 @@ public:
   NS_IMETHOD CompileScriptEventListener(nsIScriptContext *aContext,
                                         void *aScopeObject,
                                         nsISupports *aObject,
                                         nsIAtom* aName, PRBool *aDidCompile);
 
   NS_IMETHOD HandleEvent(nsPresContext* aPresContext, 
                          nsEvent* aEvent, 
                          nsIDOMEvent** aDOMEvent,
-                         nsISupports* aCurrentTarget,
+                         nsPIDOMEventTarget* aCurrentTarget,
                          PRUint32 aFlags,
                          nsEventStatus* aEventStatus);
 
   NS_IMETHOD Disconnect();
 
   NS_IMETHOD SetListenerTarget(nsISupports* aTarget);
 
   NS_IMETHOD HasMutationListeners(PRBool* aListener);
@@ -197,11 +198,14 @@ protected:
   // 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;
   static jsval                              sAddListenerID;
+
+  friend class nsEventTargetChainItem;
+  static PRUint32                           sCreatedCount;
 };
 
 #endif // nsEventListenerManager_h__
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -5124,17 +5124,18 @@ nsEventStateManager::SendFocusBlur(nsPre
         // The document's blur handler focused something else.
         // Abort firing any additional blur or focus events, and make sure
         // nsFocusController:mFocusedElement is not nulled out, but agrees
         // with our current concept of focus.
         EnsureFocusSynchronization();
         return NS_OK;
       }
 
-      if (pusher.Push(window)) {
+      nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(window);
+      if (pusher.Push(target)) {
         nsEventDispatcher::Dispatch(window, gLastFocusedPresContextWeak, &event,
                                     nsnull, &status);
 
         if (previousFocus && mCurrentFocus != previousFocus) {
           // The window's blur handler focused something else.
           // Abort firing any additional blur or focus events.
           EnsureFocusSynchronization();
           return NS_OK;
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -803,16 +803,17 @@ nsHTMLFormElement::UnbindFromTree(PRBool
     oldDocument->RemovedForm();
   }     
   ForgetCurrentSubmission();
 }
 
 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) {
       if (mGeneratingSubmit) {
         aVisitor.mCanHandle = PR_FALSE;
         return NS_OK;
       }
       mGeneratingSubmit = PR_TRUE;