Revision: 7281
Author: fabb...@google.com
Date: Wed Dec  9 10:31:24 2009
Log: Adding a simple 3x retry algorithm to XhrLoadingStrategy, and exposing  
it to GWT.create so users can more easily override both the  
AsyncFragmentLoader.LoadingStrategy and also AsyncFragmentLoader.Logger.   
Also tweaked the logging, so that load failures can see what was being  
requested and what data arrived.

Review by: spoon
http://code.google.com/p/google-web-toolkit/source/detail?r=7281

Added:
  /trunk/user/src/com/google/gwt/core/AsyncFragmentLoader.gwt.xml
  /trunk/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java
   
/trunk/user/test/com/google/gwt/core/client/impl/XhrLoadingStrategyTest.java
Modified:
  /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
  /trunk/user/src/com/google/gwt/core/Core.gwt.xml
  /trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
  /trunk/user/test/com/google/gwt/core/CoreSuite.java

=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/core/AsyncFragmentLoader.gwt.xml     Wed  
Dec  9 10:31:24 2009
@@ -0,0 +1,29 @@
+<!--                                                                         
-->
+<!-- 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   -->
+<!-- may obtain a copy of the License  
at                                    -->
+<!--                                                                         
-->
+<!--  
http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                         
-->
+<!-- Unless required by applicable law or agreed to in writing,  
software    -->
+<!-- distributed under the License is distributed on an "AS IS"  
BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express  
or        -->
+<!-- implied. License for the specific language governing permissions  
and   -->
+<!-- limitations under the  
License.                                         -->
+
+<!-- Types associated with GWT.runAsync() and its fragment  
loader           -->
+<module>
+
+  <replace-with class="com.google.gwt.core.client.impl.XhrLoadingStrategy">
+    <when-type-is
+         
class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy"/>
+  </replace-with>
+
+  <replace-with
+       
class="com.google.gwt.core.client.impl.AsyncFragmentLoader.StandardLogger">
+    <when-type-is
+         
class="com.google.gwt.core.client.impl.AsyncFragmentLoader.Logger"/>
+  </replace-with>
+
+</module>
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java     
 
Wed Dec  9 10:31:24 2009
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2009 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
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+import  
com.google.gwt.core.client.impl.AsyncFragmentLoader.HttpDownloadFailure;
+import  
com.google.gwt.core.client.impl.AsyncFragmentLoader.HttpInstallFailure;
+import  
com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadErrorHandler;
+import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy;
+import com.google.gwt.xhr.client.ReadyStateChangeHandler;
+import com.google.gwt.xhr.client.XMLHttpRequest;
+
+/**
+ * The standard loading strategy used in a web browser.
+ */
+public class XhrLoadingStrategy implements LoadingStrategy {
+
+  /**
+   * A {...@link MockableXMLHttpRequest} that is really just a vanilla
+   * XMLHttpRequest.  This wrapper (and thus {...@code  
MockableXMLHttpRequest) is
+   * needed because so much of {...@link XMLHttpRequest} is final, which in  
turn
+   * is because it extends {...@code JavaScriptObject} and is subject to its
+   * restrictions.
+   *
+   * It is important that these methods be simple enough to be inlined  
away.
+   */
+  class DelegatingXMLHttpRequest implements MockableXMLHttpRequest {
+    private final XMLHttpRequest delegate;
+
+    public DelegatingXMLHttpRequest(XMLHttpRequest xmlHttpRequest) {
+      delegate = xmlHttpRequest;
+    }
+
+    public void clearOnReadyStateChange() {
+      delegate.clearOnReadyStateChange();
+    }
+
+    public int getReadyState() {
+      return delegate.getReadyState();
+    }
+
+    public String getResponseText() {
+      return delegate.getResponseText();
+    }
+
+    public int getStatus() {
+      return delegate.getStatus();
+    }
+
+    public String getStatusText() {
+      return delegate.getStatusText();
+    }
+
+    public void open(String method, String url) {
+      delegate.open(method, url);
+    }
+
+    public void send() {
+      delegate.send();
+    }
+
+    public void setOnReadyStateChange(ReadyStateChangeHandler handler) {
+      delegate.setOnReadyStateChange(handler);
+    }
+
+    public void setRequestHeader(String header, String value) {
+      delegate.setRequestHeader(header, value);
+    }
+  }
+
+  /**
+   * Delegates to the real XMLHttpRequest, except in test when we make a  
mock
+   * to jump through error/retry hoops.
+   */
+  interface MockableXMLHttpRequest {
+    void clearOnReadyStateChange();
+    int getReadyState();
+    String getResponseText();
+    int getStatus();
+    String getStatusText();
+    void open(String method, String url);
+    void send();
+    void setOnReadyStateChange(ReadyStateChangeHandler handler);
+    void setRequestHeader(String header, String value);
+  }
+
+  /**
+   * Since LoadingStrategy must support concurrent requests, including  
figuring
+   * which is which in the onLoadError handling, we need to keep track of  
this
+   * data for each outstanding request, which we index by xhr object.
+   */
+  protected class RequestData {
+    String url;
+    int retryCount;
+    LoadErrorHandler errorHandler = null;
+
+    public RequestData(String url, LoadErrorHandler errorHandler) {
+      this.url = url;
+      this.errorHandler = errorHandler;
+      this.retryCount = 0;
+    }
+  }
+
+  static final String HTTP_GET = "GET";
+
+  /**
+   * Some UA's like Safari will have a "0" status code when loading from  
file:
+   * URLs. Additionally, the "0" status code is used sometimes if the  
server
+   * does not respond, e.g. if there is a connection refused.
+   */
+  static final int HTTP_STATUS_NON_HTTP = 0;
+
+  static final int HTTP_STATUS_OK = 200;
+
+  /**
+   * For error logging, max length of fragment response text to include in
+   * failed-to-install exception message.
+   */
+  private static final int MAX_LOG_LENGTH = 200;
+
+  /**
+   * Number of retry attempts for a single fragment.  If a fragment  
download
+   * fails, we try again this many times before "really" failing out to  
user
+   * error-handling code.  If a fragment downloads but doesn't install, we
+   * don't retry at all.
+   */
+  private static final int MAX_RETRY_COUNT = 3;
+
+  public void startLoadingFragment(int fragment,
+      final LoadErrorHandler loadErrorHandler) {
+    String url = gwtStartLoadingFragment(fragment, loadErrorHandler);
+    if (url == null) {
+      // The download has already started; nothing more to do
+      return;
+    }
+
+    RequestData request = new RequestData(url, loadErrorHandler);
+    tryLoad(request);
+  }
+
+  /**
+   * Overridable for tests.
+   */
+  protected MockableXMLHttpRequest createXhr() {
+    return new DelegatingXMLHttpRequest(XMLHttpRequest.create());
+  }
+
+  /**
+   * Call the linker-supplied <code>__gwtInstallCode</code> method. See the
+   * {...@link AsyncFragmentLoader class comment} for more details.
+   */
+  protected native void gwtInstallCode(String text) /*-{
+    __gwtInstallCode(text);
+  }-*/;
+
+  /**
+   * Call the linker-supplied __gwtStartLoadingFragment function. It should
+   * either start the download and return null or undefined, or it should
+   * return a URL that should be downloaded to get the code. If it starts  
the
+   * download itself, it can synchronously load it, e.g. from cache, if  
that
+   * makes sense.
+   */
+  protected native String gwtStartLoadingFragment(int fragment,
+      LoadErrorHandler loadErrorHandler) /*-{
+    function loadFailed(e) {
+       
loaderrorhandl...@com.google.gwt.core.client.impl.asyncfragmentloader$loaderrorhandler::loadFailed(Ljava/lang/Throwable;)(e);
+    }
+    return __gwtStartLoadingFragment(fragment, loadFailed);
+  }-*/;
+
+  /**
+   * Error recovery from loading or installing code.
+   * @param request the requestData of this request
+   * @param e exception of the error
+   * @param mayRetry {...@code true} if retrying might be helpful
+   */
+  protected void onLoadError(RequestData request, Throwable e, boolean  
mayRetry) {
+    if (mayRetry) {
+      request.retryCount++;
+      if (request.retryCount < MAX_RETRY_COUNT) {
+        tryLoad(request);
+        return;
+      }
+    }
+    request.errorHandler.loadFailed(e);
+  }
+
+  /**
+   * Makes a single load-and-install attempt.
+   */
+  protected void tryLoad(final RequestData request) {
+    final MockableXMLHttpRequest xhr = createXhr();
+
+    xhr.open(HTTP_GET, request.url);
+    if (request.retryCount > 0) {
+      // disable caching if we have to retry; one cause could be bad cache
+      xhr.setRequestHeader("Cache-Control", "no-cache");
+    }
+
+    xhr.setOnReadyStateChange(new ReadyStateChangeHandler() {
+      public void onReadyStateChange(XMLHttpRequest ignored) {
+        if (xhr.getReadyState() == XMLHttpRequest.DONE) {
+          xhr.clearOnReadyStateChange();
+          if ((xhr.getStatus() == HTTP_STATUS_OK || xhr.getStatus() ==  
HTTP_STATUS_NON_HTTP)
+              && xhr.getResponseText() != null
+              && xhr.getResponseText().length() != 0) {
+            try {
+              gwtInstallCode(xhr.getResponseText());
+            } catch (RuntimeException e) {
+              String textIntro = xhr.getResponseText();
+              if (textIntro != null && textIntro.length() >  
MAX_LOG_LENGTH) {
+                textIntro = textIntro.substring(0, MAX_LOG_LENGTH) + "...";
+              }
+              onLoadError(request,
+                  new HttpInstallFailure(request.url, textIntro, e),  
false);
+            }
+          } else {
+            onLoadError(request,
+                new HttpDownloadFailure(request.url, xhr.getStatus(),
+                    xhr.getStatusText()), true);
+          }
+        }
+      }
+    });
+
+    xhr.send();
+  }
+}
=======================================
--- /dev/null
+++  
/trunk/user/test/com/google/gwt/core/client/impl/XhrLoadingStrategyTest.java    
 
Wed Dec  9 10:31:24 2009
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2009 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
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+import  
com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadErrorHandler;
+import  
com.google.gwt.core.client.impl.XhrLoadingStrategy.MockableXMLHttpRequest;
+import com.google.gwt.xhr.client.ReadyStateChangeHandler;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * Tests the default loading strategy and its retry behavior.
+ */
+public class XhrLoadingStrategyTest extends TestCase {
+
+  static class MockXhr implements MockableXMLHttpRequest {
+    public static final String SUCCESSFUL_RESPONSE_TEXT =
+        "successful response text";
+    public static final String INSTALL_FAILED_RESPONSE_TEXT =
+        "install failed response text";
+
+    private ReadyStateChangeHandler handler;
+    private int httpStatus;
+    private int state;
+    private String statusText;
+    private String text;
+    private HashMap<String,String> headers;
+
+    public MockXhr(int status, String statusText, boolean loads,
+        boolean installs, String... headers) {
+      this.httpStatus = status;
+      this.statusText = statusText;
+      if (installs) {
+        text = SUCCESSFUL_RESPONSE_TEXT;
+      } else if (loads) {
+        text = INSTALL_FAILED_RESPONSE_TEXT;
+      } else {
+        text = null;
+      }
+      handler = null;
+      state = 0;
+      assert headers.length % 2 == 0;
+      this.headers = new HashMap<String,String>();
+      for (int i = 0; i < headers.length; i += 2) {
+        this.headers.put(headers[i], headers[i + 1]);
+      }
+    }
+
+    public void clearOnReadyStateChange() {
+      handler = null;
+    }
+
+    public int getReadyState() {
+      return state;
+    }
+
+    public String getResponseText() {
+      return state > 3 ? text : null;
+    }
+
+    public int getStatus() {
+      return state > 1 ? httpStatus : 0;
+    }
+
+    public String getStatusText() {
+      return state > 1 ? statusText : null;
+    }
+
+    public void open(String method, String url) {
+      state = 1;
+    }
+
+    public void send() {
+      state = 4;
+      if (headers.size() != 0) {
+        throw new IllegalStateException("not all expected headers set");
+      }
+      if (handler != null) {
+        /* This is brittle, but I don't have a better idea.  The problem is
+         * that onReadyStateChange takes a REAL XMLHttpRequest, which I  
can't
+         * mock because it's all final.  I don't want to open
+         * ReadyStateChangeHandler's long-standing API to let it take a
+         * non-real XMLHttpRequest, just for my wee test here, so instead I
+         * admit that null works 'cause the handler won't *use* its  
argument.
+         */
+        handler.onReadyStateChange(null);
+      }
+    }
+
+    public void setOnReadyStateChange(ReadyStateChangeHandler handler) {
+      this.handler = handler;
+    }
+
+    public void setRequestHeader(String header, String value) {
+      String val = headers.get(header);
+      if (val == null) {
+        throw new IllegalArgumentException("set of unexpected header "
+            + header);
+      }
+      if (!val.equals(value)) {
+        throw new IllegalArgumentException("set of header "
+            + header + " to unexpected value " + value + ", not " + val);
+      }
+      headers.remove(header);
+    }
+  }
+
+  /**
+   * {...@link XhrLoadingStrategy}, but without actual live XHRs.
+   */
+  static class MockXhrLoadingStrategy extends XhrLoadingStrategy {
+    private static final String FRAGMENT_URL  
= "http://nowhere.net/fragment";;
+    private ArrayList<MockXhr> xhrs;
+
+    public MockXhrLoadingStrategy(MockXhr... input) {
+      xhrs = new ArrayList<MockXhr>(Arrays.asList(input));
+    }
+
+    public void assertDone() {
+      if (xhrs.size() != 0) {
+        throw new IllegalStateException("leftover createXhr() data" +
+        " (too few load retries?)");
+      }
+    }
+
+    /**
+     * Test stub; install succeeds unless text says otherwise.
+     */
+    @Override
+    protected void gwtInstallCode(String text) {
+      if (MockXhr.INSTALL_FAILED_RESPONSE_TEXT.equals(text)) {
+        throw new RuntimeException(text);
+      }
+    }
+
+    /**
+     * Test stub; bypass the JSNI, but we're returning a (mock) URL.
+     */
+    @Override
+    protected String gwtStartLoadingFragment(int fragment,
+        LoadErrorHandler loadErrorHandler) {
+      return FRAGMENT_URL;
+    }
+
+    @Override
+    protected MockableXMLHttpRequest createXhr() {
+      if (xhrs.size() == 0) {
+        throw new IllegalStateException("createXhr() underflow" +
+            " (too many load retries?)");
+      }
+      return xhrs.remove(0);
+    }
+  }
+
+  /**
+   * Basic succeeds-on-first-try case.
+   */
+  public void testNoRetrySucceeds() {
+    MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(
+        new MockXhr(200, "200 Ok", true, true));
+    xls.startLoadingFragment(1, new LoadErrorHandler() {
+      public void loadFailed(Throwable reason) {
+        fail();
+      }
+    });
+    xls.assertDone();
+  }
+
+  /**
+   * Fails irrevocably on first try; doesn't retry.
+   */
+  public void testNoRetryFails() {
+    final boolean loadFailedCalled[] = new boolean[1];
+    loadFailedCalled[0] = false;
+    MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(
+        new MockXhr(200, "Ok", true, false));
+    xls.startLoadingFragment(1, new LoadErrorHandler() {
+      public void loadFailed(Throwable reason) {
+        loadFailedCalled[0] = true;
+      }
+    });
+    xls.assertDone();
+    if (!loadFailedCalled[0]) {
+      fail("should have failed to install, but didn't");
+    }
+  }
+
+  /**
+   * Needs some retries, but succeeds.
+   */
+  public void testRetrySucceeds() {
+    MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(
+        new MockXhr(0, "Could not connect", false, false),
+        new MockXhr(200, "Ok", true, true, "Cache-Control", "no-cache"));
+    xls.startLoadingFragment(1, new LoadErrorHandler() {
+      public void loadFailed(Throwable reason) {
+        fail();
+      }
+    });
+    xls.assertDone();
+  }
+
+  /**
+   * Needs retries, and never succeeds.
+   */
+  public void testRetryFails() {
+    final boolean loadFailedCalled[] = new boolean[1];
+    loadFailedCalled[0] = false;
+    MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(
+        new MockXhr(0, "Could not connect", false, false),
+        new MockXhr(0, "Could not connect", false, false,
+            "Cache-Control", "no-cache"),
+        new MockXhr(0, "Could not connect", false, false,
+            "Cache-Control", "no-cache"));
+    xls.startLoadingFragment(1, new LoadErrorHandler() {
+      public void loadFailed(Throwable reason) {
+        loadFailedCalled[0] = true;
+      }
+    });
+    xls.assertDone();
+    if (!loadFailedCalled[0]) {
+      fail("should have failed to install, but didn't");
+    }
+  }
+
+  /**
+   * A bizarre case we've seen in the wild...
+   */
+  public void testNull200Case() {
+    MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(
+        new MockXhr(200, "Ok", false, false),
+        new MockXhr(200, "Ok", false, false,
+            "Cache-Control", "no-cache"),
+        new MockXhr(200, "Ok", true, true,
+            "Cache-Control", "no-cache"));
+    xls.startLoadingFragment(1, new LoadErrorHandler() {
+      public void loadFailed(Throwable reason) {
+        fail();
+      }
+    });
+    xls.assertDone();
+  }
+
+  /**
+   * Check some HTTP status codes....
+   */
+  public void testRetryCodes() {
+    MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(
+        new MockXhr(500, "Server Error", false, false),
+        new MockXhr(404, "Not Found", false, false,
+            "Cache-Control", "no-cache"),
+        new MockXhr(200, "Ok", true, true,
+            "Cache-Control", "no-cache"));
+    xls.startLoadingFragment(1, new LoadErrorHandler() {
+      public void loadFailed(Throwable reason) {
+        fail();
+      }
+    });
+    xls.assertDone();
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java       
 
Wed Nov 18 11:30:09 2009
+++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java       
 
Wed Dec  9 10:31:24 2009
@@ -230,7 +230,7 @@
    static JMethodCall getBrowserLoaderConstructor(JProgram program) {
      JField field =  
program.getIndexedField("AsyncFragmentLoader.BROWSER_LOADER");
      JMethodCall constructorCall = (JMethodCall)  
field.getDeclarationStatement().getInitializer();
-    assert constructorCall.getArgs().size() == 4;
+    assert constructorCall.getArgs().size() == 2;
      return constructorCall;
    }

=======================================
--- /trunk/user/src/com/google/gwt/core/Core.gwt.xml    Mon Aug 17 09:47:48  
2009
+++ /trunk/user/src/com/google/gwt/core/Core.gwt.xml    Wed Dec  9 10:31:24  
2009
@@ -23,6 +23,7 @@
    <inherits name="com.google.gwt.xhr.XMLHttpRequest" />
    <inherits name="com.google.gwt.core.CompilerParameters" />
    <inherits name="com.google.gwt.core.EmulateJsStack" />
+  <inherits name="com.google.gwt.core.AsyncFragmentLoader" />

    <super-source path="translatable" />

@@ -37,4 +38,5 @@

    <add-linker name="soycReport" />
    <add-linker name="symbolMaps" />
+
  </module>
=======================================
---  
/trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java        
 
Fri Dec  4 14:17:43 2009
+++  
/trunk/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java        
 
Wed Dec  9 10:31:24 2009
@@ -15,9 +15,8 @@
   */
  package com.google.gwt.core.client.impl;

+import com.google.gwt.core.client.GWT;
  import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.xhr.client.ReadyStateChangeHandler;
-import com.google.gwt.xhr.client.XMLHttpRequest;

  import java.util.ArrayList;
  import java.util.Collection;
@@ -152,7 +151,7 @@
    /**
     * An exception indicating than at HTTP download failed.
     */
-  private static class HttpDownloadFailure extends RuntimeException {
+  static class HttpDownloadFailure extends RuntimeException {
      private final int statusCode;

      public HttpDownloadFailure(String url, int statusCode, String  
statusText) {
@@ -165,6 +164,16 @@
        return statusCode;
      }
    }
+
+  /**
+   * An exception indicating than at HTTP download succeeded, but  
installing
+   * its body failed.
+   */
+  static class HttpInstallFailure extends RuntimeException {
+    public HttpInstallFailure(String url, String text, Throwable  
rootCause) {
+      super("Install of " + url + " failed with text " + text, rootCause);
+    }
+  }

    /**
     * Internal load error handler. This calls all user-provided error  
handlers
@@ -259,90 +268,15 @@
    }

    /**
-   * The standard loading strategy used in a web browser.
+   * The standard instance of AsyncFragmentLoader used in a web browser.   
If
+   * not in GWT (i.e our vanilla JUnit tests, or if referenced in a server
+   * context), this filed is {...@code null}.  In GWT, the parameters to this  
call
+   * are rewritten by {...@link  
com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs}.  So
+   * this must be a method call of exactly two arguments, or that magic  
fails.
     */
-  private static class XhrLoadingStrategy implements LoadingStrategy {
-    public void startLoadingFragment(int fragment,
-        final LoadErrorHandler loadErrorHandler) {
-      final String fragmentUrl = gwtStartLoadingFragment(fragment,  
loadErrorHandler);
-
-      if (fragmentUrl == null) {
-        // The download has already started; nothing more to do
-        return;
-      }
-
-      // use XHR to download it
-
-      final XMLHttpRequest xhr = XMLHttpRequest.create();
-
-      xhr.open(HTTP_GET, fragmentUrl);
-
-      xhr.setOnReadyStateChange(new ReadyStateChangeHandler() {
-        public void onReadyStateChange(XMLHttpRequest ignored) {
-          if (xhr.getReadyState() == XMLHttpRequest.DONE) {
-            xhr.clearOnReadyStateChange();
-            if ((xhr.getStatus() == HTTP_STATUS_OK || xhr.getStatus() ==  
HTTP_STATUS_NON_HTTP)
-                && xhr.getResponseText() != null
-                && xhr.getResponseText().length() != 0) {
-              try {
-                gwtInstallCode(xhr.getResponseText());
-              } catch (RuntimeException e) {
-                loadErrorHandler.loadFailed(e);
-              }
-            } else {
-              loadErrorHandler.loadFailed(new  
HttpDownloadFailure(fragmentUrl,
-                  xhr.getStatus(), xhr.getStatusText()));
-            }
-          }
-        }
-      });
-
-      xhr.send();
-    }
-
-    /**
-     * Call the linker-supplied <code>__gwtInstallCode</code> method. See  
the
-     * {...@link AsyncFragmentLoader class comment} for more details.
-     */
-    private native void gwtInstallCode(String text) /*-{
-      __gwtInstallCode(text);
-    }-*/;
-
-    /**
-     * Call the linker-supplied __gwtStartLoadingFragment function. It  
should
-     * either start the download and return null or undefined, or it should
-     * return a URL that should be downloaded to get the code. If it  
starts the
-     * download itself, it can synchronously load it, e.g. from cache, if  
that
-     * makes sense.
-     */
-    private native String gwtStartLoadingFragment(int fragment,
-        LoadErrorHandler loadErrorHandler) /*-{
-      function loadFailed(e) {
-         
loaderrorhandl...@com.google.gwt.core.client.impl.asyncfragmentloader$loaderrorhandler::loadFailed(Ljava/lang/Throwable;)(e);
-      }
-      return __gwtStartLoadingFragment(fragment, loadFailed);
-    }-*/;
-  }
-
-  /**
-   * The standard instance of AsyncFragmentLoader used in a web browser.  
The
-   * parameters to this call are filled in by
-   * {...@link com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs}.
-   */
-  public static AsyncFragmentLoader BROWSER_LOADER = new  
AsyncFragmentLoader(1,
-      new int[] {}, new XhrLoadingStrategy(), new StandardLogger());
-
-  private static final String HTTP_GET = "GET";
-
-  /**
-   * Some UA's like Safari will have a "0" status code when loading from  
file:
-   * URLs. Additionally, the "0" status code is used sometimes if the  
server
-   * does not respond, e.g. if there is a connection refused.
-   */
-  private static final int HTTP_STATUS_NON_HTTP = 0;
-
-  private static final int HTTP_STATUS_OK = 200;
-
+  public static AsyncFragmentLoader BROWSER_LOADER =
+    makeBrowserLoader(1, new int[] {});
+
    /**
     * A helper static method that invokes
     * BROWSER_LOADER.leftoversFragmentHasLoaded(). Such a call is generated  
by
@@ -352,6 +286,23 @@
    public static void browserLoaderLeftoversFragmentHasLoaded() {
      BROWSER_LOADER.leftoversFragmentHasLoaded();
    }
+
+  /**
+   * Creates the loader stored as {...@link #BROWSER_LOADER}.
+   * @returns {...@code null} if not in GWT client code, where
+   *   {...@link GWT#create(Class)} cannot be used, or a fragment loader for
+   *   the user's application otherwise.
+   */
+  private static AsyncFragmentLoader makeBrowserLoader(int numFragments,
+      int initialLoad[]) {
+    if (GWT.isClient()) {
+      return new AsyncFragmentLoader(numFragments, initialLoad,
+          (LoadingStrategy) GWT.create(LoadingStrategy.class),
+          (Logger) GWT.create(Logger.class));
+    } else {
+      return null;
+    }
+  }

    /**
     * The fragment currently loading, or -1 if there aren't any.
=======================================
--- /trunk/user/test/com/google/gwt/core/CoreSuite.java Wed Nov 11 15:38:45  
2009
+++ /trunk/user/test/com/google/gwt/core/CoreSuite.java Wed Dec  9 10:31:24  
2009
@@ -23,6 +23,7 @@
  import com.google.gwt.core.client.impl.EmulatedStackTraceTest;
  import com.google.gwt.core.client.impl.SchedulerImplTest;
  import com.google.gwt.core.client.impl.StackTraceCreatorTest;
+import com.google.gwt.core.client.impl.XhrLoadingStrategyTest;
  import com.google.gwt.junit.tools.GWTTestSuite;

  import junit.framework.Test;
@@ -42,6 +43,7 @@
      suite.addTestSuite(StackTraceCreatorTest.class);
      suite.addTestSuite(EmulatedStackTraceTest.class);
      suite.addTestSuite(AsyncFragmentLoaderTest.class);
+    suite.addTestSuite(XhrLoadingStrategyTest.class);
      suite.addTestSuite(SchedulerImplTest.class);
      // $JUnit-END$

-- 
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to