Revision: 7291
Author: [email protected]
Date: Thu Dec 10 13:39:08 2009
Log: merge of runasync retry code, tr...@7281 as modified by 7290.

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

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

=======================================
--- /dev/null
+++  
/branches/snapshot-2009.11.20-r7061/user/src/com/google/gwt/core/AsyncFragmentLoader.gwt.xml
     
Thu Dec 10 13:39:08 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
+++  
/branches/snapshot-2009.11.20-r7061/user/src/com/google/gwt/core/client/impl/XhrLoadingStrategy.java
     
Thu Dec 10 13:39:08 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
+++  
/branches/snapshot-2009.11.20-r7061/user/test/com/google/gwt/core/client/impl/XhrLoadingStrategyTest.java
        
Thu Dec 10 13:39:08 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();
+  }
+}
=======================================
---  
/branches/snapshot-2009.11.20-r7061/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
       
Wed Nov 18 11:30:09 2009
+++  
/branches/snapshot-2009.11.20-r7061/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
       
Thu Dec 10 13:39:08 2009
@@ -225,13 +225,13 @@

    /**
     * Extract the initializer of AsyncFragmentLoader.BROWSER_LOADER. A  
couple of
-   * parts of the compiler modify this constructor call.
+   * parts of the compiler modify this initializer.
     */
    static JMethodCall getBrowserLoaderConstructor(JProgram program) {
      JField field =  
program.getIndexedField("AsyncFragmentLoader.BROWSER_LOADER");
-    JMethodCall constructorCall = (JMethodCall)  
field.getDeclarationStatement().getInitializer();
-    assert constructorCall.getArgs().size() == 4;
-    return constructorCall;
+    JMethodCall initializerCall = (JMethodCall)  
field.getDeclarationStatement().getInitializer();
+    assert initializerCall.getArgs().size() == 2;
+    return initializerCall;
    }

    /**
=======================================
---  
/branches/snapshot-2009.11.20-r7061/user/src/com/google/gwt/core/Core.gwt.xml   
 
Mon Aug 17 09:47:48 2009
+++  
/branches/snapshot-2009.11.20-r7061/user/src/com/google/gwt/core/Core.gwt.xml   
 
Thu Dec 10 13:39:08 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>
=======================================
---  
/branches/snapshot-2009.11.20-r7061/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
    
Wed Nov 18 11:30:09 2009
+++  
/branches/snapshot-2009.11.20-r7061/user/src/com/google/gwt/core/client/impl/AsyncFragmentLoader.java
    
Thu Dec 10 13:39:08 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,11 +151,12 @@
    /**
     * 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(int statusCode) {
-      super("HTTP download failed with status " + statusCode);
+    public HttpDownloadFailure(String url, int statusCode, String  
statusText) {
+      super("Download of " + url + " failed with status " + statusCode  
+ "("
+          + statusText + ")");
        this.statusCode = statusCode;
      }

@@ -164,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
@@ -258,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) {
-      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(
-                  xhr.getStatus()));
-            }
-          }
-        }
-      });
-
-      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
@@ -351,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.
=======================================
---  
/branches/snapshot-2009.11.20-r7061/user/test/com/google/gwt/core/CoreSuite.java
         
Wed Nov 11 15:38:45 2009
+++  
/branches/snapshot-2009.11.20-r7061/user/test/com/google/gwt/core/CoreSuite.java
         
Thu Dec 10 13:39:08 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