Cherry picking r10362 into GWT 2.4 release git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.4@10374 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/ScriptInjector.java b/user/src/com/google/gwt/core/client/ScriptInjector.java new file mode 100644 index 0000000..69727a5 --- /dev/null +++ b/user/src/com/google/gwt/core/client/ScriptInjector.java
@@ -0,0 +1,300 @@ +/* + * 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.core.client; + +/* + * Design note: This class intentionally does not use the GWT DOM wrappers so + * that this code can pull in as few dependencies as possible and live in the + * Core module. + */ + +/** + * Dynamically create a script tag and attach it to the DOM. + * + * Usage with script as local string: + * <p> + * + * <pre> + * String scriptBody = "var foo = ..."; + * ScriptInjector.fromString(scriptBody).inject(); + * </pre> + * <p> + * Usage with script loaded as URL: + * <p> + * + * <pre> + * ScriptInjector.fromUrl("http://example.com/foo.js").setCallback( + * new Callback<Void, Exception>() { + * public void onFailure(Exception reason) { + * Window.alert("Script load failed."); + * } + * public void onSuccess(Void result) { + * Window.alert("Script load success."); + * } + * }).inject(); + * </pre> + * + * + */ +public class ScriptInjector { + + /** + * Builder for directly injecting a script body into the DOM. + */ + public static class FromString { + private boolean removeTag = true; + private final String scriptBody; + private JavaScriptObject window; + + /** + * @param scriptBody The script text to install into the document. + */ + public FromString(String scriptBody) { + this.scriptBody = scriptBody; + } + + /** + * Injects a script into the DOM. The JavaScript is evaluated and will be + * available immediately when this call returns. + * + * By default, the script is installed in the same window that the GWT code + * is installed in. + * + * @return the script element created for the injection. Note that it may be + * removed from the DOM. + */ + public JavaScriptObject inject() { + JavaScriptObject wnd = (window == null) ? nativeDefaultWindow() : window; + assert wnd != null; + JavaScriptObject doc = nativeGetDocument(wnd); + assert doc != null; + JavaScriptObject scriptElement = nativeMakeScriptElement(doc); + assert scriptElement != null; + nativeSetText(scriptElement, scriptBody); + nativeAttachToHead(doc, scriptElement); + if (removeTag) { + nativeRemove(scriptElement); + } + return scriptElement; + } + + /** + * @param removeTag If true, remove the tag immediately after injecting the + * source. This shrinks the DOM, possibly at the expense of + * readability if you are debugging javaScript. + * + * Default value is {@code true}. + */ + public FromString setRemoveTag(boolean removeTag) { + this.removeTag = removeTag; + return this; + } + + /** + * @param window Specify which window to use to install the script. If not + * specified, the top current window GWT is loaded in is used. + */ + public FromString setWindow(JavaScriptObject window) { + this.window = window; + return this; + } + } + + /** + * Build an injection call for adding a script by URL. + */ + public static class FromUrl { + private Callback<Void, Exception> callback; + private final String scriptUrl; + private JavaScriptObject window; + + private FromUrl(String scriptUrl) { + this.scriptUrl = scriptUrl; + } + + /** + * Injects an external JavaScript reference into the document and optionally + * calls a callback when it finishes loading. + * + * @return the script element created for the injection. + */ + public JavaScriptObject inject() { + JavaScriptObject wnd = (window == null) ? nativeDefaultWindow() : window; + assert wnd != null; + JavaScriptObject doc = nativeGetDocument(wnd); + assert doc != null; + JavaScriptObject scriptElement = nativeMakeScriptElement(doc); + assert scriptElement != null; + if (callback != null) { + attachListeners(scriptElement, callback); + } + nativeSetSrc(scriptElement, scriptUrl); + nativeAttachToHead(doc, scriptElement); + return scriptElement; + } + + /** + * Specify a callback to be invoked when the script is loaded or loading + * encounters an error. + * + * <b>Warning:</b> This class attempts to return onFailure() if the browser + * detects a load failure and calls the script objects onFailure() method, + * but it may not be reliable (doesn't work on IE7). + * <p> + * <b>Warning:</b> This class <b>does not</b> control whether or not a URL + * has already been injected into the document. The client of this class has + * the responsibility of keeping score of the injected JavaScript files. + * + * @param callback callback that gets invoked asynchronously. + */ + public FromUrl setCallback(Callback<Void, Exception> callback) { + this.callback = callback; + return this; + } + + /** + * This call allows you to specify which DOM window object to install the + * script tag in. To install into the Top level window call + * + * <code> + * builder.setWindow(ScriptInjector.TOP_WINDOW); + * </code> + * + * @param window Specifies which window to install in. + */ + public FromUrl setWindow(JavaScriptObject window) { + this.window = window; + return this; + } + } + + /** + * Returns the top level window object. Use this to inject a script so that + * global variable references are available under <code>$wnd</code> in JSNI + * access. + * <p> + * Note that if your GWT app is loaded from a different domain than the top + * window, you may not be able to add a script element to the top window. + */ + public static final JavaScriptObject TOP_WINDOW = nativeTopWindow(); + + /** + * Build an injection call for directly setting the script text in the DOM. + * + * @param scriptBody the script text to be injected and immediately executed. + */ + public static FromString fromString(String scriptBody) { + return new FromString(scriptBody); + } + + /** + * Build an injection call for adding a script by URL. + * + * @param scriptUrl URL of the JavaScript to be injected. + */ + public static FromUrl fromUrl(String scriptUrl) { + return new FromUrl(scriptUrl); + } + + /** + * Attaches event handlers to a script DOM element that will run just once a + * callback when it gets successfully loaded. + * <p> + * <b>IE Notes:</b> Internet Explorer calls {@code onreadystatechanged} + * several times while varying the {@code readyState} property: in theory, + * {@code "complete"} means the content is loaded, parsed and ready to be + * used, but in practice, {@code "complete"} happens when the JS file was + * already cached, and {@code "loaded"} happens when it was transferred over + * the network. Other browsers just call the {@code onload} event handler. To + * ensure the callback will be called at most once, we clear out both event + * handlers when the callback runs for the first time. More info at the <a + * href="http://www.phpied.com/javascript-include-ready-onload/">phpied.com + * blog</a>. + * <p> + * In IE, do not trust the "order" of {@code readyState} values. For instance, + * in IE 8 running in Vista, if the JS file is cached, only {@code "complete"} + * will happen, but if the file has to be downloaded, {@code "loaded"} can + * fire in parallel with {@code "loading"}. + * + * + * @param scriptElement element to which the event handlers will be attached + * @param callback callback that runs when the script is loaded and parsed. + */ + private static native void attachListeners(JavaScriptObject scriptElement, + Callback<Void, Exception> callback) /*-{ + function clearCallbacks() { + scriptElement.onerror = scriptElement.onreadystatechange = scriptElement.onload = function() { + }; + } + scriptElement.onload = $entry(function() { + clearCallbacks(); + callback.@com.google.gwt.core.client.Callback::onSuccess(Ljava/lang/Object;)(null); + }); + // or possibly more portable script_tag.addEventListener('error', function(){...}, true); + scriptElement.onerror = $entry(function() { + clearCallbacks(); + var ex = @com.google.gwt.core.client.CodeDownloadException::new(Ljava/lang/String;)("onerror() called."); + callback.@com.google.gwt.core.client.Callback::onFailure(Ljava/lang/Object;)(ex) + }); + scriptElement.onreadystatechange = $entry(function() { + if (scriptElement.readyState == 'complete' || scriptElement.readyState == 'loaded') { + scriptElement.onload(); + } + }); + }-*/; + + private static native void nativeAttachToHead(JavaScriptObject doc, JavaScriptObject scriptElement) /*-{ + doc.getElementsByTagName("head")[0].appendChild(scriptElement); + }-*/; + + private static native JavaScriptObject nativeDefaultWindow() /*-{ + return window; + }-*/; + + private static native JavaScriptObject nativeGetDocument(JavaScriptObject wnd) /*-{ + return wnd.document; + }-*/; + + private static native JavaScriptObject nativeMakeScriptElement(JavaScriptObject doc) /*-{ + var element = doc.createElement("script"); + element.type = "text/javascript"; + return element; + }-*/; + + private static native void nativeRemove(JavaScriptObject scriptElement) /*-{ + var p = scriptElement.parentNode; + p.removeChild(scriptElement); + }-*/; + + private static native void nativeSetSrc(JavaScriptObject element, String url) /*-{ + element.src = url; + }-*/; + + private static native void nativeSetText(JavaScriptObject element, String scriptBody) /*-{ + element.text = scriptBody; + }-*/; + + private static native JavaScriptObject nativeTopWindow() /*-{ + return $wnd; + }-*/; + + /** + * Utility class - do not instantiate + */ + private ScriptInjector() { + } +}
diff --git a/user/test/com/google/gwt/core/CoreSuite.java b/user/test/com/google/gwt/core/CoreSuite.java index 72bdc4b..293900f 100644 --- a/user/test/com/google/gwt/core/CoreSuite.java +++ b/user/test/com/google/gwt/core/CoreSuite.java
@@ -21,6 +21,7 @@ import com.google.gwt.core.client.JsArrayMixedTest; import com.google.gwt.core.client.JsArrayTest; import com.google.gwt.core.client.SchedulerTest; +import com.google.gwt.core.client.ScriptInjectorTest; import com.google.gwt.core.client.impl.AsyncFragmentLoaderTest; import com.google.gwt.core.client.impl.SchedulerImplTest; import com.google.gwt.core.client.impl.StackTraceCreatorTest; @@ -47,6 +48,7 @@ suite.addTestSuite(JsArrayMixedTest.class); suite.addTestSuite(SchedulerImplTest.class); suite.addTestSuite(SchedulerTest.class); + suite.addTestSuite(ScriptInjectorTest.class); suite.addTestSuite(StackTraceCreatorTest.class); suite.addTestSuite(StrictModeTest.class); suite.addTestSuite(XhrLoadingStrategyTest.class);
diff --git a/user/test/com/google/gwt/core/client/ScriptInjectorTest.java b/user/test/com/google/gwt/core/client/ScriptInjectorTest.java new file mode 100644 index 0000000..39bcd86 --- /dev/null +++ b/user/test/com/google/gwt/core/client/ScriptInjectorTest.java
@@ -0,0 +1,428 @@ +/* + * 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.core.client; + +import com.google.gwt.core.client.Scheduler.RepeatingCommand; +import com.google.gwt.core.client.ScriptInjector.FromString; +import com.google.gwt.junit.client.GWTTestCase; + +/** + * Tests for {@link ScriptInjector} + */ +public class ScriptInjectorTest extends GWTTestCase { + private static boolean browserChecked = false; + private static final int CHECK_DELAY = 100; + + private static boolean isIE = false; + private static final int TEST_DELAY = 10000; + + /** + * Check if the browser is IE6,7,8,9. + * + * @return <code>true</code> if the browser is IE6, IE7, IE8, IE9 + * <code>false</code> any other browser + */ + static boolean isIE() { + if (!browserChecked) { + isIE = isIEImpl(); + browserChecked = true; + } + return isIE; + } + + private static native boolean isIEImpl() /*-{ + var ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf("msie") != -1) { + return true; + } + return false; + }-*/; + + @Override + public String getModuleName() { + return "com.google.gwt.core.Core"; + } + + /** + * Install a script in the same window as GWT. + */ + public void testInjectDirectThisWindow() { + delayTestFinish(TEST_DELAY); + String scriptBody = "__ti1_var__ = 1;"; + assertFalse(nativeTest1Worked()); + new FromString(scriptBody).inject(); + boolean worked = nativeTest1Worked(); + JavaScriptObject scriptElement = findScriptTextInThisWindow(scriptBody); + if (!isIE()) { + cleanupThisWindow("__ti1_var__", scriptElement); + assertFalse("cleanup failed", nativeTest1Worked()); + } + assertTrue("__ti1_var not set in this window", worked); + assertNull("script element 1 not removed by injection", scriptElement); + finishTest(); + } + + /** + * Install a script in the top window. + */ + public void testInjectDirectTopWindow() { + String scriptBody = "__ti2_var__ = 2;"; + assertFalse(nativeTest2Worked()); + ScriptInjector.fromString(scriptBody).setWindow(ScriptInjector.TOP_WINDOW).inject(); + boolean worked = nativeTest2Worked(); + JavaScriptObject scriptElement = findScriptTextInTopWindow(scriptBody); + if (!isIE()) { + cleanupTopWindow("__ti2_var__", scriptElement); + assertTrue("__ti2_var not set in top window", worked); + } + assertNull("script element 2 not removed by injection", scriptElement); + } + + /** + * Install a script in the same window as GWT, turn off the tag removal. + */ + public void testInjectDirectWithoutRemoveTag() { + assertFalse(nativeTest3Worked()); + String scriptBody = "__ti3_var__ = 3;"; + new FromString(scriptBody).setRemoveTag(false).inject(); + boolean worked = nativeTest3Worked(); + JavaScriptObject scriptElement = findScriptTextInThisWindow(scriptBody); + if (!isIE()) { + cleanupThisWindow("__ti3_var__", scriptElement); + assertFalse("cleanup failed", nativeTest3Worked()); + } + assertTrue(worked); + assertNotNull("script element 3 should have been left in DOM", scriptElement); + } + + /** + * Inject an absolute URL on this window. + */ + public void testInjectUrlAbsolute() { + delayTestFinish(TEST_DELAY); + final String scriptUrl = "http://www.google.com/jsapi?key=GWTUNITEST"; + assertFalse(nativeInjectUrlAbsoluteWorked()); + ScriptInjector.fromUrl(scriptUrl).setCallback(new Callback<Void, Exception>() { + + @Override + public void onFailure(Exception reason) { + assertNotNull(reason); + fail("Injection failed: " + reason.toString()); + } + + @Override + public void onSuccess(Void result) { + assertTrue(nativeInjectUrlAbsoluteWorked()); + finishTest(); + } + + }).inject(); + } + + /** + * Inject an absolute URL on the top level window. + */ + public void testInjectUrlAbsoluteTop() { + delayTestFinish(TEST_DELAY); + final String scriptUrl = "http://www.google.com/jsapi?key=GWTUNITEST_ABSOLUTE"; + assertFalse(nativeAbsoluteTopUrlIsLoaded()); + ScriptInjector.fromUrl(scriptUrl).setWindow(ScriptInjector.TOP_WINDOW).setCallback( + new Callback<Void, Exception>() { + + @Override + public void onFailure(Exception reason) { + assertNotNull(reason); + fail("Injection failed: " + reason.toString()); + } + + @Override + public void onSuccess(Void result) { + assertTrue(nativeAbsoluteTopUrlIsLoaded()); + finishTest(); + } + }).inject(); + } + + /** + * This script injection should fail and fire the onFailure callback. + * + * Note, this won't work on IE. The script.onerror tag has been busted for <a + * href= + * "http://stackoverflow.com/questions/2027849/how-to-trigger-script-onerror-in-internet-explorer/2032014#2032014" + * >aeons</a>. + * + */ + public void testInjectUrlFail() { + if (isIE()) { + return; + } + delayTestFinish(TEST_DELAY); + final String scriptUrl = "uNkNoWn_sCrIpT_404.js"; + JavaScriptObject injectedElement = + ScriptInjector.fromUrl(scriptUrl).setCallback(new Callback<Void, Exception>() { + + @Override + public void onFailure(Exception reason) { + assertNotNull(reason); + finishTest(); + } + + @Override + public void onSuccess(Void result) { + fail("Injection unexpectedly succeeded."); + } + }).inject(); + assertNotNull(injectedElement); + } + + /** + * Install a script in the same window as GWT by URL + */ + public void testInjectUrlThisWindow() { + this.delayTestFinish(TEST_DELAY); + final String scriptUrl = "script_injector_test4.js"; + assertFalse(nativeTest4Worked()); + final JavaScriptObject injectedElement = ScriptInjector.fromUrl(scriptUrl).inject(); + + // We'll check using a callback in another test. This test will poll to see + // that the script had an effect. + Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { + int numLoops = 0; + + @Override + public boolean execute() { + numLoops++; + boolean worked = nativeTest4Worked(); + if (!worked && (numLoops * CHECK_DELAY < TEST_DELAY)) { + return true; + } + JavaScriptObject scriptElement = findScriptUrlInThisWindow(scriptUrl); + if (!isIE()) { + cleanupThisWindow("__ti4_var__", scriptElement); + assertFalse("cleanup failed", nativeTest4Worked()); + } + assertTrue("__ti4_var not set in this window", worked); + assertNotNull("script element 4 not found", scriptElement); + assertEquals(injectedElement, scriptElement); + finishTest(); + + // never reached + return false; + } + }, CHECK_DELAY); + assertNotNull(injectedElement); + } + + /** + * Install a script in the same window as GWT by URL + */ + public void testInjectUrlThisWindowCallback() { + delayTestFinish(TEST_DELAY); + final String scriptUrl = "script_injector_test5.js"; + assertFalse(nativeTest5Worked()); + JavaScriptObject injectedElement = + ScriptInjector.fromUrl(scriptUrl).setCallback(new Callback<Void, Exception>() { + @Override + public void onFailure(Exception reason) { + assertNotNull(reason); + fail("Injection failed: " + reason.toString()); + } + + @Override + public void onSuccess(Void result) { + boolean worked = nativeTest5Worked(); + JavaScriptObject scriptElement = findScriptUrlInThisWindow(scriptUrl); + if (!isIE()) { + cleanupThisWindow("__ti5_var__", scriptElement); + assertFalse("cleanup failed", nativeTest5Worked()); + } + assertTrue("__ti5_var not set in this window", worked); + assertNotNull("script element 5 not found", scriptElement); + finishTest(); + } + }).inject(); + assertNotNull(injectedElement); + } + + /** + * Install a script in the top window by URL + */ + public void testInjectUrlTopWindow() { + final String scriptUrl = "script_injector_test6.js"; + assertFalse(nativeTest6Worked()); + JavaScriptObject injectedElement = + ScriptInjector.fromUrl(scriptUrl).setWindow(ScriptInjector.TOP_WINDOW).inject(); + // We'll check using a callback in another test. This test will poll to see + // that the script had an effect. + Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { + int numLoops = 0; + + @Override + public boolean execute() { + numLoops++; + + boolean worked = nativeTest6Worked(); + if (!worked && (numLoops * CHECK_DELAY < TEST_DELAY)) { + return true; + } + JavaScriptObject scriptElement = findScriptUrlInTopWindow(scriptUrl); + if (!isIE()) { + cleanupTopWindow("__ti6_var__", scriptElement); + assertFalse("cleanup failed", nativeTest6Worked()); + } + assertTrue("__ti6_var not set in top window", worked); + assertNotNull("script element 6 not found", scriptElement); + finishTest(); + // never reached + return false; + } + }, CHECK_DELAY); + assertNotNull(injectedElement); + } + + /** + * Install a script in the top window by URL + */ + public void testInjectUrlTopWindowCallback() { + delayTestFinish(TEST_DELAY); + final String scriptUrl = "script_injector_test7.js"; + assertFalse(nativeTest7Worked()); + JavaScriptObject injectedElement = + ScriptInjector.fromUrl(scriptUrl).setWindow(ScriptInjector.TOP_WINDOW).setCallback( + new Callback<Void, Exception>() { + + @Override + public void onFailure(Exception reason) { + assertNotNull(reason); + fail("Injection failed: " + reason.toString()); + } + + @Override + public void onSuccess(Void result) { + boolean worked = nativeTest7Worked(); + JavaScriptObject scriptElement = findScriptUrlInTopWindow(scriptUrl); + if (!isIE()) { + cleanupTopWindow("__ti7_var__", scriptElement); + assertFalse("cleanup failed", nativeTest7Worked()); + } + assertTrue("__ti7_var not set in top window", worked); + assertNotNull("script element 7 not found", scriptElement); + finishTest(); + } + }).inject(); + assertNotNull(injectedElement); + } + + private void cleanupThisWindow(String property, JavaScriptObject scriptElement) { + cleanupWindow(nativeThisWindow(), property, scriptElement); + } + + private void cleanupTopWindow(String property, JavaScriptObject scriptElement) { + cleanupWindow(nativeTopWindow(), property, scriptElement); + } + + private native void cleanupWindow(JavaScriptObject wnd, String property, + JavaScriptObject scriptElement) /*-{ + delete wnd[property]; + if (scriptElement) { + scriptElement.parentNode.removeChild(scriptElement); + } + }-*/; + + private JavaScriptObject findScriptTextInThisWindow(String text) { + return nativeFindScriptText(nativeThisWindow(), text); + } + + private JavaScriptObject findScriptTextInTopWindow(String text) { + return nativeFindScriptText(nativeTopWindow(), text); + } + + private JavaScriptObject findScriptUrlInThisWindow(String url) { + return nativeFindScriptUrl(nativeThisWindow(), url); + } + + private JavaScriptObject findScriptUrlInTopWindow(String url) { + return nativeFindScriptUrl(nativeTopWindow(), url); + } + + private native boolean nativeAbsoluteTopUrlIsLoaded() /*-{ + return !!$wnd.google && !!$wnd.google.load; + }-*/; + + private native JavaScriptObject nativeFindScriptText(JavaScriptObject wnd, String text) /*-{ + var scripts = wnd.document.getElementsByTagName("script"); + for ( var i = 0; i < scripts.length; ++i) { + if (scripts[i].text.match("^" + text)) { + return scripts[i]; + } + } + return null; + }-*/; + + /** + * Won't work for all urls, uses a regular expression match + */ + private native JavaScriptObject nativeFindScriptUrl(JavaScriptObject wnd, String url) /*-{ + var scripts = wnd.document.getElementsByTagName("script"); + for ( var i = 0; i < scripts.length; ++i) { + if (scripts[i].src.match(url)) { + return scripts[i]; + } + } + return null; + }-*/; + + private native boolean nativeInjectUrlAbsoluteWorked() /*-{ + return !!window.google && !!window.google.load; + }-*/; + + private native boolean nativeTest1Worked() /*-{ + return !!window["__ti1_var__"] && window["__ti1_var__"] == 1; + }-*/; + + private native boolean nativeTest2Worked() /*-{ + return !!$wnd["__ti2_var__"] && $wnd["__ti2_var__"] == 2; + }-*/; + + private native boolean nativeTest3Worked() /*-{ + return !!window["__ti3_var__"] && window["__ti3_var__"] == 3; + }-*/; + + private native boolean nativeTest4Worked() /*-{ + return !!window["__ti4_var__"] && window["__ti4_var__"] == 4; + }-*/; + + private native boolean nativeTest5Worked() /*-{ + return !!window["__ti5_var__"] && window["__ti5_var__"] == 5; + }-*/; + + private native boolean nativeTest6Worked() /*-{ + return !!$wnd["__ti6_var__"] && $wnd["__ti6_var__"] == 6; + }-*/; + + private native boolean nativeTest7Worked() /*-{ + return !!$wnd["__ti7_var__"] && $wnd["__ti7_var__"] == 7; + }-*/; + + private native JavaScriptObject nativeThisWindow() /*-{ + return window; + }-*/; + + private native JavaScriptObject nativeTopWindow() /*-{ + return $wnd; + }-*/; +}
diff --git a/user/test/com/google/gwt/core/public/script_injector_test4.js b/user/test/com/google/gwt/core/public/script_injector_test4.js new file mode 100644 index 0000000..52d39ab --- /dev/null +++ b/user/test/com/google/gwt/core/public/script_injector_test4.js
@@ -0,0 +1 @@ +__ti4_var__ = 4; \ No newline at end of file
diff --git a/user/test/com/google/gwt/core/public/script_injector_test5.js b/user/test/com/google/gwt/core/public/script_injector_test5.js new file mode 100644 index 0000000..ca1d38a --- /dev/null +++ b/user/test/com/google/gwt/core/public/script_injector_test5.js
@@ -0,0 +1 @@ +__ti5_var__ = 5; \ No newline at end of file
diff --git a/user/test/com/google/gwt/core/public/script_injector_test6.js b/user/test/com/google/gwt/core/public/script_injector_test6.js new file mode 100644 index 0000000..ea1948d --- /dev/null +++ b/user/test/com/google/gwt/core/public/script_injector_test6.js
@@ -0,0 +1 @@ +__ti6_var__ = 6; \ No newline at end of file
diff --git a/user/test/com/google/gwt/core/public/script_injector_test7.js b/user/test/com/google/gwt/core/public/script_injector_test7.js new file mode 100644 index 0000000..76d55ff --- /dev/null +++ b/user/test/com/google/gwt/core/public/script_injector_test7.js
@@ -0,0 +1 @@ +__ti7_var__ = 7; \ No newline at end of file