Fixes issue #1406; there was a memory leak on IE due to very subtle nuances of XHR on IE.  Turns out the only legal way to "unhook" an onreadystatechanged handler is to set it to a dummy func!

Found by: jmgodbout
Review by: knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1260 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/JavaScriptObject.java b/user/src/com/google/gwt/core/client/JavaScriptObject.java
index bd55e8d..4566f9d 100644
--- a/user/src/com/google/gwt/core/client/JavaScriptObject.java
+++ b/user/src/com/google/gwt/core/client/JavaScriptObject.java
@@ -37,6 +37,14 @@
   }-*/;
   
   /**
+   * Returns an empty function.
+   */
+  public static native JavaScriptObject createFunction() /*-{
+    return function() {
+    };
+  }-*/;
+
+  /**
    * Returns a new object.
    */
   public static native JavaScriptObject createObject() /*-{
diff --git a/user/src/com/google/gwt/http/client/XMLHTTPRequest.java b/user/src/com/google/gwt/http/client/XMLHTTPRequest.java
index ceeb06f..48e7374 100644
--- a/user/src/com/google/gwt/http/client/XMLHTTPRequest.java
+++ b/user/src/com/google/gwt/http/client/XMLHTTPRequest.java
@@ -34,37 +34,38 @@
    * JavaScript <code>XmlHttpRequest.onreadystatechange</code> handler function 
    * maybe still be called after it is deleted. The theory is that the callback 
    * is cached somewhere.  Setting the handler to null has the desired effect on 
-   * Mozilla but it causes IE to crash during the assignment.
+   * Mozilla but it causes IE to crash during the assignment. The solution is to
+   * set it to an empty function.
    */
   static native void abort(JavaScriptObject xmlHttpRequest) /*-{
-   delete xmlHttpRequest.onreadystatechange;
-   
-   xmlHttpRequest.abort();
-   }-*/;
+    xmlHttpRequest.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
+    xmlHttpRequest.abort();
+  }-*/;
 
   static native String getAllResponseHeaders(JavaScriptObject xmlHttpRequest) /*-{
-   return xmlHttpRequest.getAllResponseHeaders();
-   }-*/;
+    return xmlHttpRequest.getAllResponseHeaders();
+  }-*/;
 
   /**
    * Tests if the JavaScript <code>XmlHttpRequest.status</code> property is
    * readable. This can return failure in two different known scenarios:
-   *
+   * 
    * <ol>
-   * <li>On Mozilla, after a network error, attempting to read the status
-   * code results in an exception being thrown. See
-   * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=238559">https://bugzilla.mozilla.org/show_bug.cgi?id=238559</a>.
+   * <li>On Mozilla, after a network error, attempting to read the status code
+   * results in an exception being thrown. See <a
+   * href="https://bugzilla.mozilla.org/show_bug.cgi?id=238559">https://bugzilla.mozilla.org/show_bug.cgi?id=238559</a>.
    * </li>
-   * <li>On Safari, if the HTTP response does not include any response text. See
-   * <a href="http://bugs.webkit.org/show_bug.cgi?id=3810">http://bugs.webkit.org/show_bug.cgi?id=3810</a>.
+   * <li>On Safari, if the HTTP response does not include any response text.
+   * See <a
+   * href="http://bugs.webkit.org/show_bug.cgi?id=3810">http://bugs.webkit.org/show_bug.cgi?id=3810</a>.
    * </li>
    * </ol>
    * 
    * @param xmlHttpRequest the JavaScript <code>XmlHttpRequest</code> object
-   *        to test
+   *          to test
    * @return a String message containing an error message if the
-   * <code>XmlHttpRequest.status</code> code is unreadable or null if the
-   * status code could be successfully read.
+   *         <code>XmlHttpRequest.status</code> code is unreadable or null if
+   *         the status code could be successfully read.
    */
   static native String getBrowserSpecificFailure(
       JavaScriptObject xmlHttpRequest) /*-{
@@ -74,8 +75,7 @@
                "http://bugs.webkit.org/show_bug.cgi?id=3810 for more details";
       }
       return null;
-    }
-    catch ( e ) {
+    } catch (e) {
       return "Unable to read XmlHttpRequest.status; likely causes are a " +
              "networking error or bad cross-domain request. Please see " +
              "https://bugzilla.mozilla.org/show_bug.cgi?id=238559 for more " +
@@ -130,31 +130,30 @@
   }
 
   static native int getReadyState(JavaScriptObject xmlHttpRequest) /*-{
-   return xmlHttpRequest.readyState;
-   }-*/;
+    return xmlHttpRequest.readyState;
+  }-*/;
 
   static native String getResponseHeader(JavaScriptObject xmlHttpRequest,
       String header) /*-{
-   try {
-   return xmlHttpRequest.getResponseHeader(header);
-   } catch (ex) {
-   // purposely ignored
-   }
-   
-   return null;
-   }-*/;
+    try {
+      return xmlHttpRequest.getResponseHeader(header);
+    } catch (e) {
+      // purposely ignored
+    }
+    return null;
+  }-*/;
 
   static native String getResponseText(JavaScriptObject xmlHttpRequest) /*-{
-   return xmlHttpRequest.responseText;
-   }-*/;
+    return xmlHttpRequest.responseText;
+  }-*/;
 
   static native int getStatusCode(JavaScriptObject xmlHttpRequest) /*-{
-   return xmlHttpRequest.status;
-   }-*/;
+    return xmlHttpRequest.status;
+  }-*/;
 
   static native String getStatusText(JavaScriptObject xmlHttpRequest) /*-{
-   return xmlHttpRequest.statusText;
-   }-*/;
+    return xmlHttpRequest.statusText;
+  }-*/;
 
   static boolean isResponseReady(JavaScriptObject xmlHttpRequest) {
     return getReadyState(xmlHttpRequest) == LOADED;
@@ -175,51 +174,45 @@
    */
   static native String open(JavaScriptObject xmlHttpRequest, String httpMethod,
       String url, boolean async, String user, String password) /*-{
-   try {
-   xmlHttpRequest.open(httpMethod, url, async, user, password);
-   } catch (e) {
-   return e.toString();
-   }
-   
-   return null;
-   }-*/;
+    try {
+      xmlHttpRequest.open(httpMethod, url, async, user, password);
+      return null;
+    } catch (e) {
+      return e.toString();
+    }
+  }-*/;
 
   /*
    * Creates a closure that calls the HTTPRequest::fireOnResponseRecieved method
    * when the server's response is received and sends the data.
    */
-  static native String send(JavaScriptObject xmlHttpRequest,
-      Request httpRequest, String requestData, RequestCallback callback) /*-{
-   var xmlHttp = xmlHttpRequest;
-
-   xmlHttp.onreadystatechange = function() {
-   if (xmlHttp.readyState == @com.google.gwt.http.client.XMLHTTPRequest::LOADED) {
-   delete xmlHttp.onreadystatechange;
-   
-   httpRequest.@com.google.gwt.http.client.Request::fireOnResponseReceived(Lcom/google/gwt/http/client/RequestCallback;)(callback);
-   }
-   };
-   
-   try {
-   xmlHttp.send(requestData);
-   } catch (e) {
-   return e.toString();
-   }
-   
-   return null;
-   }-*/;
+  static native String send(JavaScriptObject xmlHttpRequest, Request httpRequest,
+      String requestData, RequestCallback callback) /*-{
+    xmlHttpRequest.onreadystatechange = function() {
+      if (xmlHttpRequest.readyState == @com.google.gwt.http.client.XMLHTTPRequest::LOADED) {
+        xmlHttpRequest.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
+        httpRequest.@com.google.gwt.http.client.Request::fireOnResponseReceived(Lcom/google/gwt/http/client/RequestCallback;)(callback);
+      }
+    };
+    try {
+      xmlHttpRequest.send(requestData);
+      return null;
+    } catch (e) {
+      xmlHttpRequest.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
+      return e.toString();
+    }
+  }-*/;
 
   static native String setRequestHeader(JavaScriptObject xmlHttpRequest,
       String header, String value) /*-{
-   try {
-   xmlHttpRequest.setRequestHeader(header, value);
-   } catch (e) {
-   return e.toString();
-   }
-   
-   return null;
-   }-*/;
-
+    try {
+      xmlHttpRequest.setRequestHeader(header, value);
+      return null;
+    } catch (e) {
+      return e.toString();
+    }
+  }-*/;
+  
   private XMLHTTPRequest() {
   }
 }
diff --git a/user/src/com/google/gwt/user/client/impl/HTTPRequestImpl.java b/user/src/com/google/gwt/user/client/impl/HTTPRequestImpl.java
index 6d3415d..b41da81 100644
--- a/user/src/com/google/gwt/user/client/impl/HTTPRequestImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/HTTPRequestImpl.java
@@ -24,6 +24,12 @@
  */
 public class HTTPRequestImpl {
 
+  static JavaScriptObject nullFunc;
+  
+  public HTTPRequestImpl() {
+    nullFunc = JavaScriptObject.createFunction();
+  }
+  
   public boolean asyncGet(String url, ResponseTextHandler handler) {
     return asyncGet(null, null, url, handler);
   }
@@ -62,21 +68,14 @@
       xmlHttp.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
       xmlHttp.onreadystatechange = function() {
         if (xmlHttp.readyState == 4) {
-          delete xmlHttp.onreadystatechange;
-          var localHandler = handler;
-          var responseText = xmlHttp.responseText;
-          handler = null;
-          xmlHttp = null;
-          localHandler.@com.google.gwt.user.client.ResponseTextHandler::onCompletion(Ljava/lang/String;)(responseText);
+          xmlHttp.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
+          handler.@com.google.gwt.user.client.ResponseTextHandler::onCompletion(Ljava/lang/String;)(xmlHttp.responseText);
         }
       };
       xmlHttp.send('');
       return true;
-    }
-    catch (e) {
-      delete xmlHttp.onreadystatechange;
-      handler = null;
-      xmlHttp = null;
+    } catch (e) {
+      xmlHttp.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
       return false;
     }
   }-*/;
@@ -89,21 +88,15 @@
       xmlHttp.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
       xmlHttp.onreadystatechange = function() {
         if (xmlHttp.readyState == 4) {
-          delete xmlHttp.onreadystatechange;
-          var localHandler = handler;
-          var responseText = xmlHttp.responseText;
-          handler = null;
-          xmlHttp = null;
-          localHandler.@com.google.gwt.user.client.ResponseTextHandler::onCompletion(Ljava/lang/String;)(responseText);
+          xmlHttp.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
+          handler.@com.google.gwt.user.client.ResponseTextHandler::onCompletion(Ljava/lang/String;)(xmlHttp.responseText);
         }
       };
       xmlHttp.send(postData);
       return true;
     }
     catch (e) {
-      delete xmlHttp.onreadystatechange;
-      handler = null;
-      xmlHttp = null;
+      xmlHttp.onreadystatechange = @com.google.gwt.user.client.impl.HTTPRequestImpl::nullFunc;
       return false;
     }
   }-*/;