Add option to use JSONP in ExternalTextResource
Review at http://gwt-code-reviews.appspot.com/1310801
Review by: robertvawter@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9622 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/api-checker/config/gwt21_22userApi.conf b/tools/api-checker/config/gwt21_22userApi.conf
index 63bdabe..ef56579 100644
--- a/tools/api-checker/config/gwt21_22userApi.conf
+++ b/tools/api-checker/config/gwt21_22userApi.conf
@@ -36,6 +36,7 @@
:com/google/gwt/junit/client/GWTTestCase.java\
:com/google/gwt/junit/client/impl/GWTRunner.java\
:com/google/gwt/junit/remote/**\
+:com/google/gwt/resources/client/impl/**\
:com/google/gwt/resources/css/**\
:com/google/gwt/resources/ext/**\
:com/google/gwt/resources/rg/**\
diff --git a/user/src/com/google/gwt/jsonp/client/JsonpRequest.java b/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
index 54ca127..c2859af 100644
--- a/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
+++ b/user/src/com/google/gwt/jsonp/client/JsonpRequest.java
@@ -44,6 +44,17 @@
private static final JavaScriptObject CALLBACKS = getOrCreateCallbacksObject();
/**
+ * Prefix appended to all id's that are determined by the callbacks counter
+ */
+ private static final String INCREMENTAL_ID_PREFIX = "P";
+
+ /**
+ * Prefix appended to all id's that are passed in by the user. The "P"
+ * suffix must stay in sync with ExternalTextResourceGenerator.java
+ */
+ private static final String PREDETERMINED_ID_PREFIX = "P";
+
+ /**
* Returns the next ID to use, incrementing the global counter.
*/
private static native int getAndIncrementCallbackCounter() /*-{
@@ -71,8 +82,12 @@
return $wnd[name];
}-*/;
+ private static String getPredeterminedId(String suffix) {
+ return PREDETERMINED_ID_PREFIX + suffix;
+ }
+
private static String nextCallbackId() {
- return "I" + getAndIncrementCallbackCounter();
+ return INCREMENTAL_ID_PREFIX + getAndIncrementCallbackCounter();
}
private final String callbackId;
@@ -91,6 +106,8 @@
private final String failureCallbackParam;
+ private final boolean canHaveMultipleRequestsForSameId;
+
/**
* Timer which keeps track of timeouts.
*/
@@ -117,10 +134,41 @@
this.expectInteger = expectInteger;
this.callbackParam = callbackParam;
this.failureCallbackParam = failureCallbackParam;
+ this.canHaveMultipleRequestsForSameId = false;
}
/**
- * Cancels a pending request.
+ * Create a new JSONP request with a hardcoded id. This could be used to
+ * manually control which resources are considered duplicates (by giving them
+ * identical ids). Could also be used if the callback name needs to be
+ * completely user controlled (since the id is part of the callback name).
+ *
+ * @param callback The callback instance to notify when the response comes
+ * back
+ * @param timeout Time in ms after which a {@link TimeoutException} will be
+ * thrown
+ * @param expectInteger Should be true if T is {@link Integer}, false
+ * otherwise
+ * @param callbackParam Name of the url param of the callback function name
+ * @param failureCallbackParam Name of the url param containing the the
+ * failure callback function name, or null for no failure callback
+ * @param id unique id for the resource that is being fetched
+ */
+ JsonpRequest(AsyncCallback<T> callback, int timeout, boolean expectInteger,
+ String callbackParam, String failureCallbackParam, String id) {
+ callbackId = getPredeterminedId(id);
+ this.callback = callback;
+ this.timeout = timeout;
+ this.expectInteger = expectInteger;
+ this.callbackParam = callbackParam;
+ this.failureCallbackParam = failureCallbackParam;
+ this.canHaveMultipleRequestsForSameId = true;
+ }
+
+ /**
+ * Cancels a pending request. Note that if you are using preset ID's, this
+ * will not work, since there is no way of knowing if there are other
+ * requests pending (or have already returned) for the same data.
*/
public void cancel() {
timer.cancel();
@@ -151,7 +199,7 @@
* @param baseUri To be sent to the server.
*/
void send(final String baseUri) {
- registerCallbacks(CALLBACKS);
+ registerCallbacks(CALLBACKS, canHaveMultipleRequestsForSameId);
StringBuffer uri = new StringBuffer(baseUri);
uri.append(baseUri.contains("?") ? "&" : "?");
String prefix = CALLBACKS_NAME + "." + callbackId;
@@ -176,7 +224,7 @@
timer.schedule(timeout);
getHeadElement().appendChild(script);
}
-
+
@SuppressWarnings("unused") // used by JSNI
private void onFailure(String message) {
onFailure(new Exception(message));
@@ -212,10 +260,9 @@
*
* @param callbacks the global JS object which stores callbacks
*/
- private native void registerCallbacks(JavaScriptObject callbacks) /*-{
+ private native void registerCallbacks(JavaScriptObject callbacks, boolean canHaveMultipleRequestsForId) /*-{
var self = this;
var callback = new Object();
- callbacks[this.@com.google.gwt.jsonp.client.JsonpRequest::callbackId] = callback;
callback.onSuccess = $entry(function(data) {
// Box primitive types
if (typeof data == 'boolean') {
@@ -234,6 +281,36 @@
self.@com.google.gwt.jsonp.client.JsonpRequest::onFailure(Ljava/lang/String;)(message);
});
}
+
+ if (canHaveMultipleRequestsForId) {
+ // In this case, we keep a wrapper, with a list of callbacks. Since the
+ // response for the request is the same each time, we call all of the
+ // callbacks as soon as any response comes back.
+ var callbackWrapper =
+ callbacks[this.@com.google.gwt.jsonp.client.JsonpRequest::callbackId];
+ if (!callbackWrapper) {
+ callbackWrapper = new Object();
+ callbackWrapper.callbackList = new Array();
+
+ callbackWrapper.onSuccess = function(data) {
+ while (callbackWrapper.callbackList.length > 0) {
+ callbackWrapper.callbackList.shift().onSuccess(data);
+ }
+ }
+ callbackWrapper.onFailure = function(data) {
+ while (callbackWrapper.callbackList.length > 0) {
+ callbackWrapper.callbackList.shift().onFailure(data);
+ }
+ }
+ callbacks[this.@com.google.gwt.jsonp.client.JsonpRequest::callbackId] =
+ callbackWrapper;
+ }
+ callbackWrapper.callbackList.push(callback);
+ } else {
+ // In this simple case, just associate the callback directly with the
+ // particular id in the callbacks object
+ callbacks[this.@com.google.gwt.jsonp.client.JsonpRequest::callbackId] = callback;
+ }
}-*/;
/**
@@ -248,7 +325,13 @@
*/
DeferredCommand.addCommand(new Command() {
public void execute() {
- unregisterCallbacks(CALLBACKS);
+ if (!canHaveMultipleRequestsForSameId) {
+ // If there can me multiple requests for a particular ID, then we
+ // don't want to unregister the callback since there may be pending
+ // requests that have not yet come back and we don't want them to
+ // have an undefined callback function.
+ unregisterCallbacks(CALLBACKS);
+ }
Node script = Document.get().getElementById(callbackId);
if (script != null) {
// The script may have already been deleted
diff --git a/user/src/com/google/gwt/jsonp/client/JsonpRequestBuilder.java b/user/src/com/google/gwt/jsonp/client/JsonpRequestBuilder.java
index 9fe9b86..fd4043a 100644
--- a/user/src/com/google/gwt/jsonp/client/JsonpRequestBuilder.java
+++ b/user/src/com/google/gwt/jsonp/client/JsonpRequestBuilder.java
@@ -104,6 +104,7 @@
private int timeout = 10000;
private String callbackParam = "callback";
private String failureCallbackParam = null;
+ private String predeterminedId = null;
/**
* Returns the name of the callback url parameter to send to the server. The
@@ -184,6 +185,10 @@
this.failureCallbackParam = failureCallbackParam;
}
+ public void setPredeterminedId(String id) {
+ this.predeterminedId = id;
+ }
+
/**
* @param timeout The expected timeout (ms) for this request. The default is 10s.
*/
@@ -192,8 +197,14 @@
}
private <T> JsonpRequest<T> send(String url, AsyncCallback<T> callback, boolean expectInteger) {
- JsonpRequest<T> request = new JsonpRequest<T>(callback, timeout, expectInteger, callbackParam,
- failureCallbackParam);
+ JsonpRequest<T> request;
+ if (predeterminedId != null) {
+ request = new JsonpRequest<T>(callback, timeout, expectInteger, callbackParam,
+ failureCallbackParam, predeterminedId);
+ } else {
+ request = new JsonpRequest<T>(callback, timeout, expectInteger, callbackParam,
+ failureCallbackParam);
+ }
request.send(url);
return request;
}
diff --git a/user/src/com/google/gwt/resources/Resources.gwt.xml b/user/src/com/google/gwt/resources/Resources.gwt.xml
index e685abf..a78e59a 100644
--- a/user/src/com/google/gwt/resources/Resources.gwt.xml
+++ b/user/src/com/google/gwt/resources/Resources.gwt.xml
@@ -20,6 +20,7 @@
<inherits name="com.google.gwt.dom.DOM" />
<!-- Used by ExternalTextResource -->
<inherits name="com.google.gwt.http.HTTP" />
+ <inherits name="com.google.gwt.jsonp.Jsonp" />
<!-- This acts as a switch to disable the use of data: URLs -->
<define-property name="ClientBundle.enableInlining" values="true,false" />
@@ -78,4 +79,9 @@
<!-- This can be used to make CssResource produce human-readable CSS -->
<define-configuration-property name="CssResource.style" is-multi-valued="false" />
<set-configuration-property name="CssResource.style" value="obf" />
+
+ <!-- This can be used to make ExternalTextResource use JSONP rather than XHR -->
+ <!-- by setting the value to true. -->
+ <define-configuration-property name="ExternalTextResource.useJsonp" is-multi-valued="false" />
+ <set-configuration-property name="ExternalTextResource.useJsonp" value="false" />
</module>
diff --git a/user/src/com/google/gwt/resources/client/impl/ExternalTextResourcePrototype.java b/user/src/com/google/gwt/resources/client/impl/ExternalTextResourcePrototype.java
index c76ae01..ca8f381 100644
--- a/user/src/com/google/gwt/resources/client/impl/ExternalTextResourcePrototype.java
+++ b/user/src/com/google/gwt/resources/client/impl/ExternalTextResourcePrototype.java
@@ -21,10 +21,12 @@
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
+import com.google.gwt.jsonp.client.JsonpRequestBuilder;
import com.google.gwt.resources.client.ExternalTextResource;
import com.google.gwt.resources.client.ResourceCallback;
import com.google.gwt.resources.client.ResourceException;
import com.google.gwt.resources.client.TextResource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* Implements external resource fetching of TextResources.
@@ -34,25 +36,35 @@
/**
* Maps the HTTP callback onto the ResourceCallback.
*/
- private class ETRCallback implements RequestCallback {
+ private class ETRCallback implements RequestCallback, AsyncCallback<JavaScriptObject> {
final ResourceCallback<TextResource> callback;
public ETRCallback(ResourceCallback<TextResource> callback) {
this.callback = callback;
}
+ // For RequestCallback
public void onError(Request request, Throwable exception) {
+ onFailure(exception);
+ }
+
+ // For AsyncCallback
+ public void onFailure(Throwable exception) {
callback.onError(new ResourceException(
ExternalTextResourcePrototype.this,
"Unable to retrieve external resource", exception));
}
+ // For RequestCallback
public void onResponseReceived(Request request, final Response response) {
- // Get the contents of the JSON bundle
String responseText = response.getText();
-
// Call eval() on the object.
JavaScriptObject jso = evalObject(responseText);
+ onSuccess(jso);
+ }
+
+ // For AsyncCallback
+ public void onSuccess(JavaScriptObject jso) {
if (jso == null) {
callback.onError(new ResourceException(
ExternalTextResourcePrototype.this, "eval() returned null"));
@@ -60,20 +72,18 @@
}
// Populate the TextResponse cache array
- for (int i = 0; i < cache.length; i++) {
- final String resourceText = extractString(jso, i);
- cache[i] = new TextResource() {
+ final String resourceText = extractString(jso, index);
+ cache[index] = new TextResource() {
- public String getName() {
- return name;
- }
+ public String getName() {
+ return name;
+ }
- public String getText() {
- return resourceText;
- }
+ public String getText() {
+ return resourceText;
+ }
- };
- }
+ };
// Finish by invoking the callback
callback.onSuccess(cache[index]);
@@ -117,6 +127,7 @@
*/
private final TextResource[] cache;
private final int index;
+ private final String md5Hash;
private final String name;
private final String url;
@@ -126,6 +137,16 @@
this.url = url;
this.cache = cache;
this.index = index;
+ this.md5Hash = null;
+ }
+
+ public ExternalTextResourcePrototype(String name, String url,
+ TextResource[] cache, int index, String md5Hash) {
+ this.name = name;
+ this.url = url;
+ this.cache = cache;
+ this.index = index;
+ this.md5Hash = md5Hash;
}
public String getName() {
@@ -144,13 +165,19 @@
return;
}
- // Otherwise, fire an HTTP request.
- RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
- try {
- rb.sendRequest("", new ETRCallback(callback));
- } catch (RequestException e) {
- throw new ResourceException(this,
- "Unable to initiate request for external resource", e);
+ if (md5Hash != null) {
+ // If we have an md5Hash, we should be using JSONP
+ JsonpRequestBuilder rb = new JsonpRequestBuilder();
+ rb.setPredeterminedId(md5Hash);
+ rb.requestObject(url, new ETRCallback(callback));
+ } else {
+ RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
+ try {
+ rb.sendRequest("", new ETRCallback(callback));
+ } catch (RequestException e) {
+ throw new ResourceException(this,
+ "Unable to initiate request for external resource", e);
+ }
}
}
}
diff --git a/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java b/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java
index 279e109..928f989 100644
--- a/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/ExternalTextResourceGenerator.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.resources.rg;
+import com.google.gwt.core.ext.BadPropertyValueException;
+import com.google.gwt.core.ext.ConfigurationProperty;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
@@ -42,6 +44,15 @@
*/
public final class ExternalTextResourceGenerator extends
AbstractResourceGenerator {
+ /**
+ * The name of a deferred binding property that determines whether or not this
+ * generator will use JSONP to fetch the files.
+ */
+ static final String USE_JSONP = "ExternalTextResource.useJsonp";
+
+ // This string must stay in sync with the values in JsonpRequest.java
+ static final String JSONP_CALLBACK_PREFIX = "__gwt_jsonp__.P";
+
private StringBuffer data;
private boolean first;
private String urlExpression;
@@ -65,6 +76,9 @@
// These are field names
sw.println(externalTextUrlIdent + ", " + externalTextCacheIdent + ", ");
sw.println(offsets.get(method.getName()).toString());
+ if (shouldUseJsonp(context, logger)) {
+ sw.println(", \"" + getMd5HashOfData() + "\"");
+ }
sw.outdent();
sw.print(")");
@@ -75,10 +89,20 @@
public void createFields(TreeLogger logger, ResourceContext context,
ClientBundleFields fields) throws UnableToCompleteException {
data.append(']');
+ StringBuffer wrappedData = new StringBuffer();
+ if (shouldUseJsonp(context, logger)) {
+ wrappedData.append(JSONP_CALLBACK_PREFIX);
+ wrappedData.append(getMd5HashOfData());
+ wrappedData.append(".onSuccess(\n");
+ wrappedData.append(data.toString());
+ wrappedData.append(")");
+ } else {
+ wrappedData = data;
+ }
urlExpression = context.deploy(
context.getClientBundleType().getQualifiedSourceName().replace('.', '_')
- + "_jsonbundle.txt", "text/plain", data.toString().getBytes(), true);
+ + "_jsonbundle.txt", "text/plain", wrappedData.toString().getBytes(), true);
TypeOracle typeOracle = context.getGeneratorContext().getTypeOracle();
JClassType stringType = typeOracle.findType(String.class.getName());
@@ -142,4 +166,22 @@
// Store the (possibly n:1) mapping of resource function to bundle index.
offsets.put(method.getName(), hashes.get(toWrite));
}
+
+ private String getMd5HashOfData() {
+ return Util.computeStrongName(data.toString().getBytes());
+ }
+
+ private boolean shouldUseJsonp(ResourceContext context, TreeLogger logger) {
+ String useJsonpProp = null;
+ try {
+ ConfigurationProperty prop = context.getGeneratorContext()
+ .getPropertyOracle().getConfigurationProperty(USE_JSONP);
+ useJsonpProp = prop.getValues().get(0);
+ } catch (BadPropertyValueException e) {
+ logger.log(TreeLogger.ERROR, "Bad value for " + USE_JSONP, e);
+ return false;
+ }
+ return Boolean.parseBoolean(useJsonpProp);
+ }
+
}
diff --git a/user/test/com/google/gwt/jsonp/client/JsonpRequestTest.java b/user/test/com/google/gwt/jsonp/client/JsonpRequestTest.java
index e88a64b..ff99107 100644
--- a/user/test/com/google/gwt/jsonp/client/JsonpRequestTest.java
+++ b/user/test/com/google/gwt/jsonp/client/JsonpRequestTest.java
@@ -283,6 +283,17 @@
new AssertSuccessCallback<String>("C", counter));
}
+ public void testPredeterminedIds() {
+ delayTestFinish(RESPONSE_DELAY);
+ String PREDETERMINED = "pred";
+ jsonp.setPredeterminedId(PREDETERMINED);
+ JsonpRequest<String> reqA = jsonp.requestString(echo("'A'"),
+ new AssertSuccessCallback<String>("A"));
+ String idA = reqA.getCallbackId().substring(1);
+ assertEquals("Unexpected ID sequence", PREDETERMINED, idA);
+ jsonp.setPredeterminedId(null);
+ }
+
public void testString() {
delayTestFinish(RESPONSE_DELAY);
jsonp.requestString(echo("'Hello'"), new AssertSuccessCallback<String>(
diff --git a/user/test/com/google/gwt/resources/ExternalTextResourceJsonp.gwt.xml b/user/test/com/google/gwt/resources/ExternalTextResourceJsonp.gwt.xml
new file mode 100644
index 0000000..e395dd0
--- /dev/null
+++ b/user/test/com/google/gwt/resources/ExternalTextResourceJsonp.gwt.xml
@@ -0,0 +1,19 @@
+<!-- -->
+<!-- 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 -->
+<!-- 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. -->
+<module>
+ <inherits name="com.google.gwt.resources.Resources" />
+ <set-configuration-property name="ExternalTextResource.useJsonp"
+ value="true" />
+</module>
+
\ No newline at end of file
diff --git a/user/test/com/google/gwt/resources/ResourcesSuite.java b/user/test/com/google/gwt/resources/ResourcesSuite.java
index 1c1085c..b101b12 100644
--- a/user/test/com/google/gwt/resources/ResourcesSuite.java
+++ b/user/test/com/google/gwt/resources/ResourcesSuite.java
@@ -17,6 +17,8 @@
import com.google.gwt.junit.tools.GWTTestSuite;
import com.google.gwt.resources.client.CSSResourceTest;
+import com.google.gwt.resources.client.ExternalTextResourceJsonpTest;
+import com.google.gwt.resources.client.ExternalTextResourceTest;
import com.google.gwt.resources.client.ImageResourceNoInliningTest;
import com.google.gwt.resources.client.ImageResourceTest;
import com.google.gwt.resources.client.NestedBundleTest;
@@ -43,17 +45,19 @@
GWTTestSuite suite = new GWTTestSuite("Test for com.google.gwt.resources");
suite.addTestSuite(CssClassNamesTestCase.class);
suite.addTestSuite(CssExternalTest.class);
- suite.addTestSuite(CSSResourceTest.class);
- suite.addTestSuite(CssReorderTest.class);
- suite.addTestSuite(CssRtlTest.class);
suite.addTestSuite(CssNodeClonerTest.class);
- suite.addTestSuite(ExtractClassNamesVisitorTest.class);
- suite.addTestSuite(ImageResourceTest.class);
- suite.addTestSuite(ImageResourceNoInliningTest.class);
- suite.addTestSuite(NestedBundleTest.class);
+ suite.addTestSuite(CssReorderTest.class);
+ suite.addTestSuite(CSSResourceTest.class);
+ suite.addTestSuite(CssRtlTest.class);
suite.addTestSuite(DataResourceDoNotEmbedTest.class);
- suite.addTestSuite(ResourceGeneratorUtilTest.class);
suite.addTestSuite(DataResourceMimeTypeTest.class);
+ suite.addTestSuite(ExternalTextResourceJsonpTest.class);
+ suite.addTestSuite(ExternalTextResourceTest.class);
+ suite.addTestSuite(ExtractClassNamesVisitorTest.class);
+ suite.addTestSuite(ImageResourceNoInliningTest.class);
+ suite.addTestSuite(ImageResourceTest.class);
+ suite.addTestSuite(NestedBundleTest.class);
+ suite.addTestSuite(ResourceGeneratorUtilTest.class);
suite.addTestSuite(TextResourceTest.class);
suite.addTestSuite(UnknownAtRuleTest.class);
return suite;
diff --git a/user/test/com/google/gwt/resources/client/ExternalTextResourceJsonpTest.java b/user/test/com/google/gwt/resources/client/ExternalTextResourceJsonpTest.java
new file mode 100644
index 0000000..c00aec4
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/ExternalTextResourceJsonpTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2011 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.resources.client;
+
+/**
+ * Tests for ExternalTextResource's jsonp functionality.
+ */
+public class ExternalTextResourceJsonpTest extends ExternalTextResourceTest {
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.resources.ExternalTextResourceJsonp";
+ }
+}
diff --git a/user/test/com/google/gwt/resources/client/ExternalTextResourceTest.java b/user/test/com/google/gwt/resources/client/ExternalTextResourceTest.java
new file mode 100644
index 0000000..56b01df
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/ExternalTextResourceTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2011 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.resources.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests for ExternalTextResource assembly and use.
+ */
+public class ExternalTextResourceTest extends GWTTestCase {
+
+ static interface Resources extends ClientBundleWithLookup {
+ @Source("hello.txt")
+ ExternalTextResource helloWorldExternal();
+
+ @Source("shouldBeEscaped.txt")
+ ExternalTextResource needsEscapeExternal();
+ }
+
+ private static final String HELLO = "Hello World!";
+ private static final String NEEDS_ESCAPE = "\"'\\";
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.resources.Resources";
+ }
+
+ public void testExternal() throws ResourceException {
+ final Resources r = GWT.create(Resources.class);
+ int numReturned = 0;
+
+ delayTestFinish(2000);
+
+ class TestCallback implements ResourceCallback<TextResource> {
+ private final String name;
+ private final String contents;
+ private TestCallback otherTest;
+ private boolean done = false;
+
+ TestCallback(String name, String contents) {
+ this.name = name;
+ this.contents = contents;
+ }
+
+ public boolean isDone() {
+ return done;
+ }
+
+ public void onError(ResourceException e) {
+ e.printStackTrace();
+ fail("Unable to fetch " + e.getResource().getName());
+ }
+
+ public void onSuccess(TextResource resource) {
+ assertEquals(name, resource.getName());
+ assertEquals(contents, resource.getText());
+ done = true;
+ if (otherTest.isDone()) {
+ finishTest();
+ }
+ }
+
+ public void setOtherTest(TestCallback test) {
+ otherTest = test;
+ }
+ };
+
+ TestCallback needsEscape = new TestCallback(
+ r.needsEscapeExternal().getName(), NEEDS_ESCAPE);
+ TestCallback helloWorld = new TestCallback(
+ r.helloWorldExternal().getName(), HELLO);
+ needsEscape.setOtherTest(helloWorld);
+ helloWorld.setOtherTest(needsEscape);
+
+ r.needsEscapeExternal().getText(needsEscape);
+ r.helloWorldExternal().getText(helloWorld);
+ }
+}
diff --git a/user/test/com/google/gwt/resources/client/TextResourceTest.java b/user/test/com/google/gwt/resources/client/TextResourceTest.java
index 11080e3..3066812 100644
--- a/user/test/com/google/gwt/resources/client/TextResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/TextResourceTest.java
@@ -58,28 +58,6 @@
assertEquals(length, 12737792);
}
- public void testExternal() throws ResourceException {
- final Resources r = GWT.create(Resources.class);
-
- delayTestFinish(2000);
-
- ResourceCallback<TextResource> c = new ResourceCallback<TextResource>() {
-
- public void onError(ResourceException e) {
- e.printStackTrace();
- fail("Unable to fetch " + e.getResource().getName());
- }
-
- public void onSuccess(TextResource resource) {
- assertEquals(r.helloWorldExternal().getName(), resource.getName());
- assertEquals(HELLO, resource.getText());
- finishTest();
- }
- };
-
- r.helloWorldExternal().getText(c);
- }
-
public void testInline() {
Resources r = GWT.create(Resources.class);
assertEquals(HELLO, r.helloWorldRelative().getText());
diff --git a/user/test/com/google/gwt/resources/client/shouldBeEscaped.txt b/user/test/com/google/gwt/resources/client/shouldBeEscaped.txt
new file mode 100644
index 0000000..3d9f92b
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/shouldBeEscaped.txt
@@ -0,0 +1 @@
+"'\
\ No newline at end of file