Issues: 1546, 1778, 1788, 1804 (multi-module bug fixes)
Patch by: jgw, jason.essington, mmastrac, benjamin.vitale
Review by: scottb



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2661 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImpl.java b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
index 298b8c9..ce4d7a6 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.impl;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.EventListener;
@@ -26,6 +27,28 @@
 
   protected static boolean eventSystemIsInitialized;
 
+  /**
+   * Returns <code>true</code>if the object is an instance of EventListener
+   * and the object belongs to this module.
+   */
+  protected static boolean isMyListener(Object object) {
+    /*
+     * The first test ensures the Object belongs to this module in hosted mode,
+     * because this hosted mode class loader will have a different copy of
+     * the EventListener class than some other module would have.
+     * 
+     * However, in web mode we could still get a collision where another module
+     * happens to use the same typeId. The second test ensures the Object is not
+     * "foreign". See Cast.isJavaScriptObject().
+     */
+    boolean b = object instanceof com.google.gwt.user.client.EventListener
+        && !(object instanceof JavaScriptObject);
+    if (!b) {
+      System.out.println(object.toString());
+    }
+    return b;
+  }
+
   public native void eventCancelBubble(Event evt, boolean cancel) /*-{
     evt.cancelBubble = cancel;
    }-*/;
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
index 288a3d6..a3c7589 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.impl;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 
@@ -24,12 +25,15 @@
  */
 class DOMImplIE6 extends DOMImpl {
 
-  /**
-   * Referenced from JavaScript.
-   */
   @SuppressWarnings("unused")
   private static Element currentEventTarget;
 
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchEvent;
+
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchDblClickEvent;
+
   @Override
   public native int eventGetClientX(Event evt) /*-{
     return evt.clientX -
@@ -101,8 +105,7 @@
 
   @Override
   public native void initEventSystem() /*-{
-    // Set up event dispatchers.
-    $wnd.__dispatchEvent = function() {
+    @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent = function() {
       // IE doesn't define event.currentTarget, so we squirrel it away here. It
       // also seems that IE won't allow you to add expandos to the event object,
       // so we have to store it in a global. This is ok because only one event
@@ -119,33 +122,44 @@
       }
 
       var listener, curElem = this;
-      while (curElem && !(listener = curElem.__listener))
+      while (curElem && !(listener = curElem.__listener)) {
         curElem = curElem.parentElement;
+      }
 
-      if (listener)
-        @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)($wnd.event, curElem, listener);
+      if (listener) {
+        if (@com.google.gwt.user.client.impl.DOMImpl::isMyListener(Ljava/lang/Object;)(listener)) {
+          @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)($wnd.event, curElem, listener);
+        }
+      }
 
       @com.google.gwt.user.client.impl.DOMImplIE6::currentEventTarget = oldEventTarget;
     };
 
-    $wnd.__dispatchDblClickEvent = function() {
+    @com.google.gwt.user.client.impl.DOMImplIE6::dispatchDblClickEvent = function() {
       var newEvent = $doc.createEventObject();
       this.fireEvent('onclick', newEvent);
-      if (this.__eventBits & 2)
-        $wnd.__dispatchEvent.call(this);
+      if (this.__eventBits & 2) {
+        (@com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent)();
+      }
     };
 
-    $doc.body.onclick       =
-    $doc.body.onmousedown   =
-    $doc.body.onmouseup     =
-    $doc.body.onmousemove   =
-    $doc.body.onmousewheel  =
-    $doc.body.onkeydown     =
-    $doc.body.onkeypress    =
-    $doc.body.onkeyup       =
-    $doc.body.onfocus       =
-    $doc.body.onblur        =
-    $doc.body.ondblclick    = $wnd.__dispatchEvent;
+    // We need to create these delegate functions to fix up the 'this' context.
+    // Normally, 'this' is the firing element, but this is only true for
+    // 'onclick = ...' event handlers, not for handlers setup via attachEvent().
+    var bodyDispatcher = function() { @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent.call($doc.body); };
+    var bodyDblClickDispatcher = function() { @com.google.gwt.user.client.impl.DOMImplIE6::dispatchDblClickEvent.call($doc.body); };
+
+    $doc.body.attachEvent('onclick', bodyDispatcher);
+    $doc.body.attachEvent('onmousedown', bodyDispatcher);
+    $doc.body.attachEvent('onmouseup', bodyDispatcher);
+    $doc.body.attachEvent('onmousemove', bodyDispatcher);
+    $doc.body.attachEvent('onmousewheel', bodyDispatcher);
+    $doc.body.attachEvent('onkeydown', bodyDispatcher);
+    $doc.body.attachEvent('onkeypress', bodyDispatcher);
+    $doc.body.attachEvent('onkeyup', bodyDispatcher);
+    $doc.body.attachEvent('onfocus', bodyDispatcher);
+    $doc.body.attachEvent('onblur', bodyDispatcher);
+    $doc.body.attachEvent('ondblclick', bodyDblClickDispatcher);
   }-*/;
 
   @Override
@@ -186,44 +200,44 @@
     var chMask = (elem.__eventBits || 0) ^ bits;
     elem.__eventBits = bits;
     if (!chMask) return;
-     
+
     if (chMask & 0x00001) elem.onclick       = (bits & 0x00001) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     // Add a ondblclick handler if onclick is desired to ensure that 
     // a user's double click will result in two onlclick events.
     if (chMask & (0x00003)) elem.ondblclick  = (bits & (0x00003)) ?
-        $wnd.__dispatchDblClickEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchDblClickEvent : null;
     if (chMask & 0x00004) elem.onmousedown   = (bits & 0x00004) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00008) elem.onmouseup     = (bits & 0x00008) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00010) elem.onmouseover   = (bits & 0x00010) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00020) elem.onmouseout    = (bits & 0x00020) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00040) elem.onmousemove   = (bits & 0x00040) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00080) elem.onkeydown     = (bits & 0x00080) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00100) elem.onkeypress    = (bits & 0x00100) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00200) elem.onkeyup       = (bits & 0x00200) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00400) elem.onchange      = (bits & 0x00400) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x00800) elem.onfocus       = (bits & 0x00800) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x01000) elem.onblur        = (bits & 0x01000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x02000) elem.onlosecapture = (bits & 0x02000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x04000) elem.onscroll      = (bits & 0x04000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x08000) elem.onload        = (bits & 0x08000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x10000) elem.onerror       = (bits & 0x10000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
     if (chMask & 0x20000) elem.onmousewheel  = (bits & 0x20000) ? 
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
   }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java b/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java
index 9474037..c7b7734 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplMozilla.java
@@ -36,7 +36,7 @@
 
   public native void sinkEventsMozilla(Element elem, int bits) /*-{
     if (bits & 0x20000) {
-      elem.addEventListener('DOMMouseScroll', $wnd.__dispatchEvent, false);
+      elem.addEventListener('DOMMouseScroll', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent, false);
     }
   }-*/;
 
@@ -69,7 +69,7 @@
       true
     );
 
-    $wnd.addEventListener('DOMMouseScroll', $wnd.__dispatchCapturedMouseEvent,
+    $wnd.addEventListener('DOMMouseScroll', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent,
       true);
   }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
index 9961511..31d7a4d 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.user.client.impl;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 
@@ -25,6 +26,18 @@
  */
 abstract class DOMImplStandard extends DOMImpl {
 
+  @SuppressWarnings("unused")
+  private static JavaScriptObject captureElem;
+
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchCapturedEvent;
+
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchCapturedMouseEvent;
+
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchEvent;
+
   @Override
   public native int eventGetButton(Event evt) /*-{
     // Standard browsers and IE disagree on what the button codes for buttons
@@ -150,18 +163,19 @@
 
   @Override
   protected native void initEventSystem() /*-{
-    // Set up capture event dispatchers.
-    $wnd.__dispatchCapturedMouseEvent = function(evt) {
-      if ($wnd.__dispatchCapturedEvent(evt)) {
-        var cap = $wnd.__captureElem;
+    @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent = function(evt) {
+      if ((@com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedEvent)(evt)) {
+        var cap = @com.google.gwt.user.client.impl.DOMImplStandard::captureElem;
         if (cap && cap.__listener) {
-          @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)(evt, cap, cap.__listener);
-          evt.stopPropagation();
+          if (@com.google.gwt.user.client.impl.DOMImpl::isMyListener(Ljava/lang/Object;)(cap.__listener)) {
+            @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)(evt, cap, cap.__listener);
+            evt.stopPropagation();
+          }
         }
       }
     };
 
-    $wnd.__dispatchCapturedEvent = function(evt) {
+    @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedEvent = function(evt) {
       if (!@com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)(evt)) {
         evt.stopPropagation();
         evt.preventDefault();
@@ -171,31 +185,34 @@
       return true;
     };
 
-    $wnd.addEventListener('click', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('dblclick', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('mousedown', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('mouseup', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('mousemove', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('mouseover', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('mouseout', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('mousewheel', $wnd.__dispatchCapturedMouseEvent, true);
-    $wnd.addEventListener('keydown', $wnd.__dispatchCapturedEvent, true);
-    $wnd.addEventListener('keyup', $wnd.__dispatchCapturedEvent, true);
-    $wnd.addEventListener('keypress', $wnd.__dispatchCapturedEvent, true);
-
-    // Set up normal event dispatcher.
-    $wnd.__dispatchEvent = function(evt) {
+    @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent = function(evt) {
       var listener, curElem = this;
-      while (curElem && !(listener = curElem.__listener))
+      while (curElem && !(listener = curElem.__listener)) {
         curElem = curElem.parentNode;
-      if (curElem && curElem.nodeType != 1)
-        curElem = null;
+      }
 
-      if (listener)
-        @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)(evt, curElem, listener);
+      if (curElem && curElem.nodeType != 1) {
+        curElem = null;
+      }
+  
+      if (listener) {
+        if (@com.google.gwt.user.client.impl.DOMImpl::isMyListener(Ljava/lang/Object;)(listener)) {
+          @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)(evt, curElem, listener);
+        }
+      }
     };
 
-    $wnd.__captureElem = null;
+    $wnd.addEventListener('click', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('dblclick', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('mousedown', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('mouseup', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('mousemove', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('mouseover', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('mouseout', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('mousewheel', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
+    $wnd.addEventListener('keydown', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedEvent, true);
+    $wnd.addEventListener('keyup', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedEvent, true);
+    $wnd.addEventListener('keypress', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedEvent, true);
   }-*/;
 
   protected native void sinkEventsImpl(Element elem, int bits) /*-{
@@ -204,50 +221,50 @@
     if (!chMask) return;
    
     if (chMask & 0x00001) elem.onclick       = (bits & 0x00001) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00002) elem.ondblclick    = (bits & 0x00002) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00004) elem.onmousedown   = (bits & 0x00004) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00008) elem.onmouseup     = (bits & 0x00008) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00010) elem.onmouseover   = (bits & 0x00010) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00020) elem.onmouseout    = (bits & 0x00020) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00040) elem.onmousemove   = (bits & 0x00040) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00080) elem.onkeydown     = (bits & 0x00080) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00100) elem.onkeypress    = (bits & 0x00100) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00200) elem.onkeyup       = (bits & 0x00200) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00400) elem.onchange      = (bits & 0x00400) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x00800) elem.onfocus       = (bits & 0x00800) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x01000) elem.onblur        = (bits & 0x01000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x02000) elem.onlosecapture = (bits & 0x02000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x04000) elem.onscroll      = (bits & 0x04000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x08000) elem.onload        = (bits & 0x08000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x10000) elem.onerror       = (bits & 0x10000) ?
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
     if (chMask & 0x20000) elem.onmousewheel  = (bits & 0x20000) ? 
-        $wnd.__dispatchEvent : null;
+        @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
   }-*/;
 
   private native void releaseCaptureImpl(Element elem) /*-{
-    if (elem === $wnd.__captureElem) {
-      $wnd.__captureElem = null;
+    if (elem === @com.google.gwt.user.client.impl.DOMImplStandard::captureElem) {
+      @com.google.gwt.user.client.impl.DOMImplStandard::captureElem = null;
     }
   }-*/;
 
   private native void setCaptureImpl(Element elem) /*-{
-    $wnd.__captureElem = elem;
+    @com.google.gwt.user.client.impl.DOMImplStandard::captureElem = elem;
   }-*/;
 }