Revision: 8510
Author: [email protected]
Date: Tue Aug 10 07:06:57 2010
Log: Recommiting the new CrossSiteIframeLinker.
This linker works cross-site, because it uses a script tag to
download code instead of XHR. However, like the iframe linker,
it still uses an iframe to hold all the installed code.
This version adds a head element to the inner iframe if one is not already
present.
Review at http://gwt-code-reviews.appspot.com/726802
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=8510
Added:
/trunk/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
/trunk/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
/trunk/user/src/com/google/gwt/core/CrossSiteIframeLinker.gwt.xml
/trunk/user/src/com/google/gwt/core/client/impl/CrossSiteIframeLoadingStrategy.java
/trunk/user/test/com/google/gwt/core/ext/CrossSiteIframeLinkerTest.gwt.xml
/trunk/user/test/com/google/gwt/core/ext/test/CrossSiteIframeLinkerTest.java
/trunk/user/test/com/google/gwt/dev/jjs/CompilerSuiteCrossSiteIframe.gwt.xml
/trunk/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncFailure.gwt.xml
/trunk/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
/trunk/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncSuite.java
/trunk/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncFailureTest.java
/trunk/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncMetricsTest.java
/trunk/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncTest.java
Modified:
/trunk/user/src/com/google/gwt/core/Core.gwt.xml
/trunk/user/test/com/google/gwt/core/ext/LinkerSuite.java
=======================================
--- /dev/null
+++
/trunk/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2010 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.linker;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
+import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
+import com.google.gwt.dev.About;
+import com.google.gwt.dev.js.JsToStringGenerationVisitor;
+import com.google.gwt.dev.util.DefaultTextOutput;
+import com.google.gwt.dev.util.TextOutput;
+import com.google.gwt.dev.util.Util;
+
+/**
+ * This linker uses an iframe to hold the code and a script tag to
download the
+ * code. It can download code cross-site, because it uses a script tag to
+ * download it and because it never uses XHR. The iframe, meanwhile, makes
it
+ * trivial to install additional code as the app runs.
+ */
+...@linkerorder(Order.PRIMARY)
+...@shardable
+public class CrossSiteIframeLinker extends SelectionScriptLinker {
+ @Override
+ public String getDescription() {
+ return "Cross-Site-Iframe";
+ }
+
+ @Override
+ protected String generateDeferredFragment(TreeLogger logger,
+ LinkerContext context, int fragment, String js) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("$wnd.");
+ sb.append(context.getModuleFunctionName());
+ sb.append(".runAsyncCallback");
+ sb.append(fragment);
+ sb.append("(");
+ sb.append(JsToStringGenerationVisitor.javaScriptString(js));
+ sb.append(");\n");
+ return sb.toString();
+ }
+
+ @Override
+ protected byte[] generatePrimaryFragment(TreeLogger logger,
+ LinkerContext context, CompilationResult result, String[] js) {
+ // Wrap the script code with its prefix and suffix
+ TextOutput script = new DefaultTextOutput(context.isOutputCompact());
+ script.print(getModulePrefix(context, result.getStrongName(),
js.length > 1));
+ script.print(js[0]);
+ script.print(getModuleSuffix(logger, context));
+
+ // Rewrite the code so it can be installed with
+ // __MODULE_FUNC__.onScriptDownloaded
+
+ StringBuffer out = new StringBuffer();
+ out.append(context.getModuleFunctionName());
+ out.append(".onScriptDownloaded(");
+
out.append(JsToStringGenerationVisitor.javaScriptString(script.toString()));
+ out.append(")");
+
+ return Util.getBytes(out.toString());
+ }
+
+ @Override
+ protected String getCompilationExtension(TreeLogger logger,
+ LinkerContext context) {
+ return ".cache.js";
+ }
+
+ @Override
+ protected String getModulePrefix(TreeLogger logger, LinkerContext
context,
+ String strongName) {
+ throw new UnsupportedOperationException("Should not be called");
+ }
+
+ @Override
+ protected String getModulePrefix(TreeLogger logger, LinkerContext
context,
+ String strongName, int numFragments) {
+ throw new UnsupportedOperationException("Should not be called");
+ }
+
+ @Override
+ protected String getModuleSuffix(TreeLogger logger, LinkerContext
context) {
+ DefaultTextOutput out = new
DefaultTextOutput(context.isOutputCompact());
+
+ out.print("$stats && $stats({moduleName:'" + context.getModuleName()
+ + "',sessionId:$sessionId"
+ + ",subSystem:'startup',evtGroup:'moduleStartup'"
+ + ",millis:(new Date()).getTime(),type:'moduleEvalEnd'});");
+
+ // Generate the call to tell the bootstrap code that we're ready to go.
+ out.newlineOpt();
+ out.print("if ($wnd." + context.getModuleFunctionName() + " && $wnd."
+ + context.getModuleFunctionName() + ".onScriptInstalled) $wnd."
+ + context.getModuleFunctionName()
+ ".onScriptInstalled(gwtOnLoad);");
+ out.newlineOpt();
+
+ return out.toString();
+ }
+
+ @Override
+ protected String getSelectionScriptTemplate(TreeLogger logger,
+ LinkerContext context) {
+ return "com/google/gwt/core/linker/CrossSiteIframeTemplate.js";
+ }
+
+ private String getModulePrefix(LinkerContext context, String strongName,
+ boolean supportRunAsync) {
+ TextOutput out = new DefaultTextOutput(context.isOutputCompact());
+
+ out.print("var $gwt_version = \"" + About.getGwtVersionNum() + "\";");
+ out.newlineOpt();
+ out.print("var $wnd = window.parent;");
+ out.newlineOpt();
+ out.print("var $doc = $wnd.document;");
+ out.newlineOpt();
+ out.print("var $moduleName, $moduleBase;");
+ out.newlineOpt();
+ out.print("var $strongName = '" + strongName + "';");
+ out.newlineOpt();
+ out.print("var $stats = $wnd.__gwtStatsEvent ? function(a) {return
$wnd.__gwtStatsEvent(a);} : null;");
+ out.newlineOpt();
+ out.print("var $sessionId = $wnd.__gwtStatsSessionId ?
$wnd.__gwtStatsSessionId : null;");
+ out.newlineOpt();
+
+ out.print("$stats && $stats({moduleName:'" + context.getModuleName()
+ + "',sessionId:$sessionId"
+ + ",subSystem:'startup',evtGroup:'moduleStartup'"
+ + ",millis:(new Date()).getTime(),type:'moduleEvalStart'});");
+ out.newlineOpt();
+
+ if (supportRunAsync) {
+ out.print("var __gwtModuleFunction = $wnd.");
+ out.print(context.getModuleFunctionName());
+ out.print(";");
+ out.newlineOpt();
+ }
+
+ return out.toString();
+ }
+}
=======================================
--- /dev/null
+++
/trunk/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2010 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.
+ */
+
+function __MODULE_FUNC__() {
+ // ---------------- INTERNAL GLOBALS ----------------
+
+ // Cache symbols locally for good obfuscation
+ var $wnd = window
+ ,$doc = document
+ ,$stats = $wnd.__gwtStatsEvent ? function(a) {return
$wnd.__gwtStatsEvent(a);} : null
+ ,$sessionId = $wnd.__gwtStatsSessionId ? $wnd.__gwtStatsSessionId : null
+
+ // Whether the document body element has finished loading
+ ,bodyDone
+
+ // The downloaded script code, once it arrives
+ ,compiledScript
+
+ // Whether the main script has been injected yet
+ ,scriptInjected
+
+ // The iframe holding the app's code
+ ,scriptFrame
+
+ // If non-empty, an alternate base url for this module
+ ,base = ''
+
+ // A map of properties that were declared in meta tags
+ ,metaProps = {}
+
+ // Maps property names onto sets of legal values for that property.
+ ,values = []
+
+ // Maps property names onto a function to compute that property.
+ ,providers = []
+
+ // A multi-tier lookup map that uses actual property values to quickly
find
+ // the strong name of the cache.js file to load.
+ ,answers = []
+
+ // Provides the module with the soft permutation id
+ ,softPermutationId = 0
+
+ // Error functions. Default unset in compiled mode, may be set by meta
props.
+ ,onLoadErrorFunc, propertyErrorFunc
+
+ ; // end of global vars
+
+ $stats && $stats({
+ moduleName: '__MODULE_NAME__',
+ sessionId: $sessionId,
+ subSystem: 'startup',
+ evtGroup: 'bootstrap',
+ millis:(new Date()).getTime(),
+ type: 'begin',
+ });
+
+ // ------------------ TRUE GLOBALS ------------------
+
+ // Maps to synchronize the loading of styles and scripts; resources are
loaded
+ // only once, even when multiple modules depend on them. This API must
not
+ // change across GWT versions.
+ if (!$wnd.__gwt_stylesLoaded) { $wnd.__gwt_stylesLoaded = {}; }
+ if (!$wnd.__gwt_scriptsLoaded) { $wnd.__gwt_scriptsLoaded = {}; }
+
+ // --------------- INTERNAL FUNCTIONS ---------------
+
+ function isHostedMode() {
+ var result = false;
+ try {
+ var query = $wnd.location.search;
+ return (query.indexOf('gwt.codesvr=') != -1
+ || query.indexOf('gwt.hosted=') != -1
+ || ($wnd.external && $wnd.external.gwtOnLoad)) &&
+ (query.indexOf('gwt.hybrid') == -1);
+ } catch (e) {
+ // Defensive: some versions of IE7 reportedly can throw an exception
+ // evaluating "external.gwtOnLoad".
+ }
+ isHostedMode = function() { return result; };
+ return result;
+ }
+
+ // Called when the body has been finished and when the script
+ // to install is available. These can happen in either order.
+ // When both have happened, create the code-holding iframe.
+ //
+ function maybeCreateFrame() {
+ if (bodyDone && compiledScript && !scriptInjected) {
+ scriptInjected = true;
+
+ // Create the script frame, making sure it's invisible, but not
+ // "display:none", which keeps some browsers from running code in it.
+ scriptFrame = $doc.createElement('iframe');
+ scriptFrame.src = 'javascript:""';
+ scriptFrame.id = '__MODULE_NAME__';
+ scriptFrame.style.cssText = 'position:absolute; width:0; height:0;
border:none; left: -1000px; top: -1000px; !important';
+ scriptFrame.tabIndex = -1;
+ document.body.appendChild(scriptFrame);
+
+ // For some reason, adding this setTimeout makes code installation
+ // more reliable.
+ setTimeout(function() {
+ installCode(compiledScript);
+ })
+ }
+ }
+
+ // Install code into scriptFrame
+ //
+ function installCode(code) {
+ var doc = scriptFrame.contentDocument;
+ if (!doc) {
+ doc = scriptFrame.contentWindow.document;
+ }
+
+ var dochead = doc.getElementsByTagName('head')[0];
+ if (!dochead) {
+ // This has been seen on Safari 3.
+ // Give the iframe a skeletal HTML document and try again.
+ doc.write('<html><head></head><body></body></html>');
+ dochead = doc.getElementsByTagName('head')[0];
+ }
+
+ // Inject the fetched script into the script frame.
+ // The script will call onScriptLoad.
+ var script = doc.createElement('script');
+ script.language='javascript';
+ script.text = code;
+
+ dochead.appendChild(script);
+
+ // Remove the tag to shrink the DOM a little.
+ // It should have installed its code immediately after being added.
+ dochead.removeChild(script);
+ }
+
+
+ __PROCESS_METAS__
+ __COMPUTE_SCRIPT_BASE__
+
+ /**
+ * Determines whether or not a particular property value is allowed.
Called by
+ * property providers.
+ *
+ * @param propName the name of the property being checked
+ * @param propValue the property value being tested
+ */
+ function __gwt_isKnownPropertyValue(propName, propValue) {
+ return propValue in values[propName];
+ }
+
+ /**
+ * Returns a meta property value, if any. Used by
DefaultPropertyProvider.
+ */
+ function __gwt_getMetaProperty(name) {
+ var value = metaProps[name];
+ return (value == null) ? null : value;
+ }
+
+ // Deferred-binding mapper function. Sets a value into the
several-level-deep
+ // answers map. The keys are specified by a non-zero-length propValArray,
+ // which should be a flat array target property values. Used by the
generated
+ // PERMUTATIONS code.
+ //
+ function unflattenKeylistIntoAnswers(propValArray, value) {
+ var answer = answers;
+ for (var i = 0, n = propValArray.length - 1; i < n; ++i) {
+ // lazy initialize an empty object for the current key if needed
+ answer = answer[propValArray[i]] || (answer[propValArray[i]] = []);
+ }
+ // set the final one to the value
+ answer[propValArray[n]] = value;
+ }
+
+ // Computes the value of a given property. propName must be a valid
property
+ // name. Used by the generated PERMUTATIONS code.
+ //
+ function computePropValue(propName) {
+ var value = providers[propName](), allowedValuesMap = values[propName];
+ if (value in allowedValuesMap) {
+ return value;
+ }
+ var allowedValuesList = [];
+ for (var k in allowedValuesMap) {
+ allowedValuesList[allowedValuesMap[k]] = k;
+ }
+ if (propertyErrorFunc) {
+ propertyErrorFunc(propName, allowedValuesList, value);
+ }
+ throw null;
+ }
+
+ // --------------- PROPERTY PROVIDERS ---------------
+
+// __PROPERTIES_BEGIN__
+// __PROPERTIES_END__
+
+ // --------------- EXPOSED FUNCTIONS ----------------
+
+ // Called when the initial script code has been downloaded
+ __MODULE_FUNC__.onScriptDownloaded = function(code) {
+ compiledScript = code;
+ maybeCreateFrame();
+ }
+
+ // Called when the compiled script has been installed
+ //
+ __MODULE_FUNC__.onScriptInstalled = function(gwtOnLoadFunc) {
+ // remove the callback to prevent it being called twice
+ __MODULE_FUNC__.onScriptInstalled = null;
+ gwtOnLoadFunc(onLoadErrorFunc, '__MODULE_NAME__', base,
softPermutationId);
+ // Record when the module EntryPoints return.
+ $stats && $stats({
+ moduleName: '__MODULE_NAME__',
+ sessionId: $sessionId,
+ subSystem: 'startup',
+ evtGroup: 'moduleStartup',
+ millis:(new Date()).getTime(),
+ type: 'end',
+ });
+ }
+
+ // Install code pulled in via runAsync
+ //
+ __MODULE_FUNC__.installCode = installCode;
+
+ // --------------- STRAIGHT-LINE CODE ---------------
+
+ if (isHostedMode()) {
+ alert("Cross-site hosted mode not yet implemented. See issue " +
+ "http://code.google.com/p/google-web-toolkit/issues/detail?id=2079");
+ return;
+ }
+
+ // do it early for compile/browse rebasing
+ processMetas();
+ computeScriptBase();
+
+ // --------------- WINDOW ONLOAD HOOK ---------------
+
+ $stats && $stats({
+ moduleName:'__MODULE_NAME__',
+ sessionId: $sessionId,
+ subSystem:'startup',
+ evtGroup: 'bootstrap',
+ millis:(new Date()).getTime(),
+ type: 'selectingPermutation'
+ });
+
+ var strongName;
+ try {
+// __PERMUTATIONS_BEGIN__
+ // Permutation logic
+// __PERMUTATIONS_END__
+ var idx = strongName.indexOf(':');
+ if (idx != -1) {
+ softPermutationId = +(strongName.substring(idx + 1));
+ strongName = strongName.substring(0, idx);
+ }
+ } catch (e) {
+ // intentionally silent on property failure
+ return;
+ }
+
+ var onBodyDoneTimerId;
+ function onBodyDone() {
+ if (!bodyDone) {
+ bodyDone = true;
+// __MODULE_STYLES_BEGIN__
+ // Style resources are injected here to prevent operation aborted
errors on ie
+// __MODULE_STYLES_END__
+ maybeCreateFrame();
+
+ if ($doc.removeEventListener) {
+ $doc.removeEventListener("DOMContentLoaded", onBodyDone, false);
+ }
+ if (onBodyDoneTimerId) {
+ clearInterval(onBodyDoneTimerId);
+ }
+ }
+ }
+
+ // For everyone that supports DOMContentLoaded.
+ if ($doc.addEventListener) {
+ $doc.addEventListener("DOMContentLoaded", function() {
+ onBodyDone();
+ }, false);
+ }
+
+ // Fallback. If onBodyDone() gets fired twice, it's not a big deal.
+ var onBodyDoneTimerId = setInterval(function() {
+ if (/loaded|complete/.test($doc.readyState)) {
+ onBodyDone();
+ }
+ }, 50);
+
+ $stats && $stats({
+ moduleName:'__MODULE_NAME__',
+ sessionId: $sessionId,
+ subSystem:'startup',
+ evtGroup: 'bootstrap',
+ millis:(new Date()).getTime(),
+ type: 'end'
+ });
+
+ $stats && $stats({
+ moduleName:'__MODULE_NAME__',
+ sessionId: $sessionId,
+ subSystem:'startup',
+ evtGroup: 'loadExternalRefs',
+ millis:(new Date()).getTime(),
+ type: 'begin'
+ });
+
+// __MODULE_SCRIPTS_BEGIN__
+ // Script resources are injected here
+// __MODULE_SCRIPTS_END__
+
+ // This is a bit ugly, but serves a purpose. We need to ensure that the
stats
+ // script runs before the compiled script. If they are both doc.write()n
in
+ // sequence, that should be the effect. Except on IE it turns out that a
+ // script injected via doc.write() can execute immediately!
Adding 'defer'
+ // attributes to both seemed to fix this, but caused startup problems for
+ // some apps. The final solution was simply to inject the compiled script
+ // from *within* the stats script, guaranteeing order at the expense of
near
+ // total inscrutability :(
+ var compiledScriptTag = '"<script src=\\"' + base + strongName
+ '.cache.js\\"></scr" + "ipt>"';
+
+ $doc.write('<scr' + 'ipt><!-' + '-\n'
+ + 'window.__gwtStatsEvent && window.__gwtStatsEvent({'
+ + 'moduleName:"__MODULE_NAME__", sessionId:window.__gwtStatsSessionId,
subSystem:"startup",'
+ + 'evtGroup: "loadExternalRefs", millis:(new Date()).getTime(),'
+ + 'type: "end"});'
+ + 'window.__gwtStatsEvent && window.__gwtStatsEvent({'
+ + 'moduleName:"__MODULE_NAME__", sessionId:window.__gwtStatsSessionId,
subSystem:"startup",'
+ + 'evtGroup: "moduleStartup", millis:(new Date()).getTime(),'
+ + 'type: "moduleRequested"});'
+ + 'document.write(' + compiledScriptTag + ');'
+ + '\n-' + '-></scr' + 'ipt>');
+}
+
+__MODULE_FUNC__();
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/core/CrossSiteIframeLinker.gwt.xml Tue
Aug 10 07:06:57 2010
@@ -0,0 +1,24 @@
+<!--
-->
+<!-- Copyright 2010 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. -->
+
+<!-- Defines the cross-site iframe linker -->
+<module>
+ <define-linker name="xsiframe"
class="com.google.gwt.core.linker.CrossSiteIframeLinker" />
+
+ <replace-with
class="com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy">
+ <when-type-is
+
class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy"
/>
+ <when-linker-added name="xsiframe" />
+ </replace-with>
+</module>
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/core/client/impl/CrossSiteIframeLoadingStrategy.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2010 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.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import
com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadTerminatedHandler;
+import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy;
+
+/**
+ * Load runAsync code using a script tag. Intended for use with the
+ * {...@link com.google.gwt.core.linker.CrossSiteIframeLinker}.
+ *
+ * <p>
+ * The linker wraps its selection script code with a function refered to by
+ * <code>__gwtModuleFunction</code>. On that function is a property
+ * <code>installCode</code> that can be invoked to eval more code in a
scope
+ * nested somewhere within that function. The loaded script for fragment
123 is
+ * expected to invoke <code>__gwtModuleFunction.runAsyncCallback123</code>
+ * as the final thing it does.
+ */
+public class CrossSiteIframeLoadingStrategy implements LoadingStrategy {
+ /**
+ * A trivial JavaScript map from ints to ints.
+ */
+ private static final class IntToIntMap extends JavaScriptObject {
+ public static IntToIntMap create() {
+ return (IntToIntMap) JavaScriptObject.createArray();
+ }
+
+ @SuppressWarnings("unused")
+ protected IntToIntMap() {
+ }
+
+ /**
+ * Get an entry. If there is no such entry, return 0.
+ */
+ public native int get(int x) /*-{
+ return this[x] ? this[x] : 0;
+ }-*/;
+
+ public native void put(int x, int y) /*-{
+ this[x] = y;
+ }-*/;
+ }
+
+ @SuppressWarnings("unused")
+ private static RuntimeException LoadTerminated = new RuntimeException(
+ "Code download terminated");
+
+ /**
+ * Clear callbacks on script objects. This is important on IE 6 and 7 to
+ * prevent a memory leak. If the callbacks aren't cleared, there is a
cyclical
+ * chain of references between the script tag and the function callback,
and
+ * IE 6/7 can't garbage collect them.
+ */
+ @SuppressWarnings("unused")
+ private static native void clearCallbacks(JavaScriptObject script) /*-{
+ var nop = new Function('');
+ script.onerror = script.onload = script.onreadystatechange = nop;
+ }-*/;
+
+ /**
+ * Clear the success callback for fragment <code>fragment</code>.
+ */
+ @SuppressWarnings("unused")
+ private static native void clearOnSuccess(int fragment) /*-{
+ delete __gwtModuleFunction['runAsyncCallback'+fragment];
+ }-*/;
+
+ private static native JavaScriptObject createScriptTag(String url) /*-{
+ var head = document.getElementsByTagName('head').item(0);
+ var script = document.createElement('script');
+ script.src = url;
+ return script;
+ }-*/;
+
+ private static native void installScriptTag(JavaScriptObject script) /*-{
+ var head = document.getElementsByTagName('head').item(0);
+ head.appendChild(script);
+ }-*/;
+
+ private static native JavaScriptObject removeTagAndCallErrorHandler(
+ int fragment, JavaScriptObject tag,
+ LoadTerminatedHandler loadFinishedHandler) /*-{
+ return function(exception) {
+ if (tag.parentNode == null) {
+ // onSuccess or onFailure must have already been called.
+ return;
+ }
+ var head = document.getElementsByTagName('head').item(0);
+
@com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy::clearOnSuccess(*)(fragment);
+
@com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy::clearCallbacks(*)(tag);
+ head.removeChild(tag);
+ function callLoadTerminated() {
+
loadfinishedhandl...@com.google.gwt.core.client.impl.asyncfragmentloader.loadterminatedhandler::loadTerminated(*)(exception);
+ }
+ $entry(callLoadTerminated)();
+ }
+ }-*/;
+
+ private static native JavaScriptObject removeTagAndEvalCode(int fragment,
+ JavaScriptObject tag) /*-{
+ return function(code) {
+ var head = document.getElementsByTagName('head').item(0);
+
@com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy::clearOnSuccess(*)(fragment);
+
@com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy::clearCallbacks(*)(tag);
+ head.removeChild(tag);
+ __gwtModuleFunction.installCode(code);
+ }
+ }-*/;
+
+ private static native void setOnFailure(JavaScriptObject script,
+ JavaScriptObject callback) /*-{
+ var exception =
@com.google.gwt.core.client.impl.CrossSiteIframeLoadingStrategy::LoadTerminated;
+ script.onerror = function() {
+ callback(exception);
+ }
+ script.onload = function() {
+ callback(exception);
+ }
+ script.onreadystatechange = function () {
+ if (script.readyState == 'loaded' || script.readyState
== 'complete') {
+ script.onreadystatechange = function () { }
+ callback(exception);
+ }
+ }
+ }-*/;
+
+ /**
+ * Set the success callback for fragment <code>fragment</code>
+ * to the supplied JavaScript function.
+ */
+ private static native void setOnSuccess(int fragment, JavaScriptObject
callback) /*-{
+ __gwtModuleFunction['runAsyncCallback'+fragment] = callback;
+ }-*/;
+
+ private final IntToIntMap serialNumbers = IntToIntMap.create();
+
+ public void startLoadingFragment(int fragment,
+ LoadTerminatedHandler loadFinishedHandler) {
+ JavaScriptObject tag = createScriptTag(getUrl(fragment));
+ setOnSuccess(fragment, removeTagAndEvalCode(fragment, tag));
+ setOnFailure(tag, removeTagAndCallErrorHandler(fragment, tag,
+ loadFinishedHandler));
+ installScriptTag(tag);
+ }
+
+ protected String getDeferredJavaScriptDirectory() {
+ return "deferredjs/";
+ }
+
+ private int getSerial(int fragment) {
+ int ser = serialNumbers.get(fragment);
+ serialNumbers.put(fragment, ser + 1);
+ return ser;
+ }
+
+ /**
+ * The URL to retrieve a fragment of code from. NOTE: this function is
not
+ * stable. It tweaks the URL with each call so that browsers are not
tempted
+ * to cache a download failure.
+ */
+ private String getUrl(int fragment) {
+ return GWT.getModuleBaseURL() + getDeferredJavaScriptDirectory()
+ + GWT.getPermutationStrongName() + "/" + fragment
+ ".cache.js?serial="
+ + getSerial(fragment);
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/core/ext/CrossSiteIframeLinkerTest.gwt.xml
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,18 @@
+<!--
-->
+<!-- Copyright 2010 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.core.ext.LinkerTest' />
+ <add-linker name='xsiframe' />
+</module>
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/core/ext/test/CrossSiteIframeLinkerTest.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010 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.ext.test;
+
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Integration test of the cross-site iframe linker.
+ */
+...@donotrunwith(Platform.Devel)
+public class CrossSiteIframeLinkerTest extends LinkerTest {
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.core.ext.CrossSiteIframeLinkerTest";
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/CompilerSuiteCrossSiteIframe.gwt.xml
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,18 @@
+<!--
-->
+<!-- Copyright 2010 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.dev.jjs.CompilerSuite"/>
+ <add-linker name="xsiframe"/>
+</module>
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncFailure.gwt.xml
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,25 @@
+<!--
-->
+<!-- Copyright 2010 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.dev.jjs.RunAsyncFailure" />
+
+ <add-linker name="xsiframe" />
+
+ <replace-with
+
class="com.google.gwt.user.client.runasync.CrossSiteLoadingStrategyForRunAsyncFailureTest">
+ <when-type-is
+
class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy"
/>
+ </replace-with>
+</module>
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,20 @@
+<!--
-->
+<!-- Copyright 2010 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.dev.jjs.RunAsyncMetricsIntegrationTest" />
+ <add-linker name="xsiframe" />
+
+ <public path="public" />
+</module>
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncSuite.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010 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.dev.jjs;
+
+import com.google.gwt.dev.jjs.test.CrossSiteIframeRunAsyncFailureTest;
+import com.google.gwt.dev.jjs.test.CrossSiteIframeRunAsyncMetricsTest;
+import com.google.gwt.dev.jjs.test.CrossSiteIframeRunAsyncTest;
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * Tests that GWT.runAsync works with the cross-site iframe linker.
+ */
+public class CrossSiteIframeRunAsyncSuite {
+ public static Test suite() {
+ GWTTestSuite suite = new GWTTestSuite("Test for runAsync with the
cross-site iframe linker");
+
+ suite.addTestSuite(CrossSiteIframeRunAsyncMetricsTest.class);
+ suite.addTestSuite(CrossSiteIframeRunAsyncFailureTest.class);
+ suite.addTestSuite(CrossSiteIframeRunAsyncTest.class);
+
+ return suite;
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncFailureTest.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 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.dev.jjs.test;
+
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests that the cross-site iframe linker handles download failures.
+ */
+...@donotrunwith(Platform.Devel)
+public class CrossSiteIframeRunAsyncFailureTest extends
RunAsyncFailureTest {
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.dev.jjs.CrossSiteIframeRunAsyncFailure";
+ }
+
+ @Override
+ public void testHttpFailureRetries() {
+ super.testHttpFailureRetries();
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncMetricsTest.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010 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.dev.jjs.test;
+
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests runAsync lightweight metrics when the cross-site iframe linker is
used.
+ */
+...@donotrunwith(Platform.Devel)
+public class CrossSiteIframeRunAsyncMetricsTest extends
+ RunAsyncMetricsIntegrationTest {
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.dev.jjs.CrossSiteIframeRunAsyncMetrics";
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/test/com/google/gwt/dev/jjs/test/CrossSiteIframeRunAsyncTest.java
Tue Aug 10 07:06:57 2010
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010 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.dev.jjs.test;
+
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests GWT.runAsync when used with the cross-site linker.
+ */
+...@donotrunwith(Platform.Devel)
+public class CrossSiteIframeRunAsyncTest extends RunAsyncTest {
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.dev.jjs.CompilerSuiteCrossSiteIframe";
+ }
+}
=======================================
--- /trunk/user/src/com/google/gwt/core/Core.gwt.xml Mon Mar 29 05:13:55
2010
+++ /trunk/user/src/com/google/gwt/core/Core.gwt.xml Tue Aug 10 07:06:57
2010
@@ -30,6 +30,7 @@
<define-linker name="sso"
class="com.google.gwt.core.linker.SingleScriptLinker" />
<define-linker name="std"
class="com.google.gwt.core.linker.IFrameLinker" />
<inherits name="com.google.gwt.core.XSLinker" />
+ <inherits name="com.google.gwt.core.CrossSiteIframeLinker" />
<define-linker name="soycReport"
class="com.google.gwt.core.linker.SoycReportLinker" />
<define-linker name="symbolMaps"
class="com.google.gwt.core.linker.SymbolMapsLinker" />
=======================================
--- /trunk/user/test/com/google/gwt/core/ext/LinkerSuite.java Tue Mar 23
09:32:47 2010
+++ /trunk/user/test/com/google/gwt/core/ext/LinkerSuite.java Tue Aug 10
07:06:57 2010
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.linker.impl.SelectionScriptJavaScriptTest;
import com.google.gwt.core.ext.linker.impl.SelectionScriptLinkerUnitTest;
+import com.google.gwt.core.ext.test.CrossSiteIframeLinkerTest;
import com.google.gwt.core.ext.test.IFrameLinkerTest;
import com.google.gwt.core.ext.test.XSLinkerTest;
import com.google.gwt.junit.tools.GWTTestSuite;
@@ -33,7 +34,9 @@
GWTTestSuite suite = new GWTTestSuite("Smoke test for linkers");
// $JUnit-BEGIN$
+ suite.addTestSuite(CrossSiteIframeLinkerTest.class);
suite.addTestSuite(IFrameLinkerTest.class);
+ suite.addTestSuite(LinkerUnitTest.class);
suite.addTestSuite(SelectionScriptJavaScriptTest.class);
suite.addTestSuite(SelectionScriptLinkerUnitTest.class);
suite.addTestSuite(XSLinkerTest.class);
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors