| /* |
| * 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 isIE8Or9 = false; |
| private static final int TEST_DELAY = 10000; |
| |
| /** |
| * Check if the browser is IE8,9. |
| * |
| * @return <code>true</code> if the browser is IE8, IE9 |
| * <code>false</code> any other browser |
| */ |
| static boolean isIE8Or9() { |
| if (!browserChecked) { |
| isIE8Or9 = isIE8Or9Impl(); |
| browserChecked = true; |
| } |
| return isIE8Or9; |
| } |
| |
| private static native boolean isIE8Or9Impl() /*-{ |
| return /msie/i.test(navigator.userAgent) && $doc.documentMode >= 8 && $doc.documentMode <= 9; |
| }-*/; |
| |
| @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 (!isIE8Or9()) { |
| 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 (!isIE8Or9()) { |
| 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 (!isIE8Or9()) { |
| 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 = GWT.getModuleBaseForStaticFiles() + "script_injector_test_absolute.js"; |
| 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 = GWT.getModuleBaseForStaticFiles() + "script_injector_test_absolute_top.js"; |
| 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, the onerror mechanism used to trigger the failure event is a modern browser |
| * feature. |
| * |
| * On IE, the script.onerror tag has been documented, but 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 (isIE8Or9()) { |
| 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).setRemoveTag(false).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 (!isIE8Or9()) { |
| 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).setRemoveTag(false) |
| .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 (!isIE8Or9()) { |
| 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).setRemoveTag(false) |
| .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 (!isIE8Or9()) { |
| 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).setRemoveTag(false) |
| .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 (!isIE8Or9()) { |
| 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); |
| } |
| |
| /** |
| * Tests encoding of the injected script (UTF-8) |
| */ |
| public void testInjectUrlUtf8() { |
| delayTestFinish(TEST_DELAY); |
| final String scriptUrl = "script_injector_test_utf8.js"; |
| assertEquals("", nativeGetTestUtf8Var()); |
| JavaScriptObject injectedElement = ScriptInjector.fromUrl(scriptUrl).setRemoveTag(false) |
| .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) { |
| String testVar = nativeGetTestUtf8Var(); |
| JavaScriptObject scriptElement = findScriptUrlInTopWindow(scriptUrl); |
| if (!isIE8Or9()) { |
| cleanupTopWindow("__ti_utf8_var__", scriptElement); |
| assertEquals("cleanup failed", "", nativeGetTestUtf8Var()); |
| } |
| assertEquals("__ti_utf8_var not set in top window", "à", testVar); |
| assertNotNull("script element 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["__tiabsolutetop_var__"] && $wnd["__tiabsolutetop_var__"] == 102; |
| }-*/; |
| |
| 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["__tiabsolute_var__"] && window["__tiabsolute_var__"] == 101; |
| }-*/; |
| |
| 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 String nativeGetTestUtf8Var() /*-{ |
| return $wnd["__ti_utf8_var__"] || ""; |
| }-*/; |
| |
| private native JavaScriptObject nativeThisWindow() /*-{ |
| return window; |
| }-*/; |
| |
| private native JavaScriptObject nativeTopWindow() /*-{ |
| return $wnd; |
| }-*/; |
| } |