Add Support for server side script selection in linker
Review at http://gwt-code-reviews.appspot.com/941802
Review by: jgw@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9038 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/PermutationsUtil.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/PermutationsUtil.java
new file mode 100644
index 0000000..c4de9d6
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/PermutationsUtil.java
@@ -0,0 +1,180 @@
+/*
+ * 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.linker.impl;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.SelectionProperty;
+import com.google.gwt.dev.util.StringKey;
+import com.google.gwt.dev.util.collect.HashSet;
+import com.google.gwt.dev.util.collect.Lists;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * A utility class to help linkers generate a list of permutation mappings and
+ * and then either output them to javascript code which selects the correct
+ * permutation, or to a file which can be parsed by server side code.
+ */
+public class PermutationsUtil {
+
+ /**
+ * This represents the combination of a unique content hash (i.e. the MD5 of
+ * the bytes to be written into the cache.html file) and a soft permutation
+ * id.
+ */
+ protected static class PermutationId extends StringKey {
+ private final int softPermutationId;
+ private final String strongName;
+
+ public PermutationId(String strongName, int softPermutationId) {
+ super(strongName + ":" + softPermutationId);
+ this.strongName = strongName;
+ this.softPermutationId = softPermutationId;
+ }
+
+ public int getSoftPermutationId() {
+ return softPermutationId;
+ }
+
+ public String getStrongName() {
+ return strongName;
+ }
+ }
+
+ /**
+ * This maps each unique permutation to the property settings for that
+ * compilation. A single compilation can have multiple property settings if
+ * the compiles for those settings yielded the exact same compiled output.
+ */
+ protected SortedMap<PermutationId, List<Map<String, String>>> propMapsByPermutation = null;
+
+ /**
+ * Uses the internal map to insert JS to select a permutation into the
+ * selection script.
+ */
+ public StringBuffer addPermutationsJs(StringBuffer selectionScript,
+ TreeLogger logger, LinkerContext context) {
+ int startPos;
+
+ PropertiesUtil.addPropertiesJs(selectionScript, logger, context);
+
+ // Possibly add permutations
+ startPos = selectionScript.indexOf("// __PERMUTATIONS_END__");
+ if (startPos != -1) {
+ StringBuffer text = new StringBuffer();
+ if (propMapsByPermutation.size() == 0) {
+ // Hosted mode link.
+ text.append("alert(\"GWT module '" + context.getModuleName()
+ + "' may need to be (re)compiled\");");
+ text.append("return;");
+
+ } else if (propMapsByPermutation.size() == 1) {
+ // Just one distinct compilation; no need to evaluate properties
+ text.append("strongName = '"
+ + propMapsByPermutation.keySet().iterator().next().getStrongName()
+ + "';");
+ } else {
+ Set<String> propertiesUsed = new HashSet<String>();
+ for (PermutationId permutationId : propMapsByPermutation.keySet()) {
+ for (Map<String, String> propertyMap : propMapsByPermutation.get(permutationId)) {
+ // unflatten([v1, v2, v3], 'strongName' + ':softPermId');
+ // The soft perm ID is concatenated to improve string interning
+ text.append("unflattenKeylistIntoAnswers([");
+ boolean needsComma = false;
+ for (SelectionProperty p : context.getProperties()) {
+ if (p.tryGetValue() != null) {
+ continue;
+ } else if (p.isDerived()) {
+ continue;
+ }
+
+ if (needsComma) {
+ text.append(",");
+ } else {
+ needsComma = true;
+ }
+ text.append("'" + propertyMap.get(p.getName()) + "'");
+ propertiesUsed.add(p.getName());
+ }
+
+ text.append("], '").append(permutationId.getStrongName()).append(
+ "'");
+ /*
+ * For compatibility with older linkers, skip the soft permutation
+ * if it's 0
+ */
+ if (permutationId.getSoftPermutationId() != 0) {
+ text.append(" + ':").append(permutationId.getSoftPermutationId()).append(
+ "'");
+ }
+ text.append(");\n");
+ }
+ }
+
+ // strongName = answers[compute('p1')][compute('p2')];
+ text.append("strongName = answers[");
+ boolean needsIndexMarkers = false;
+ for (SelectionProperty p : context.getProperties()) {
+ if (!propertiesUsed.contains(p.getName())) {
+ continue;
+ }
+ if (needsIndexMarkers) {
+ text.append("][");
+ } else {
+ needsIndexMarkers = true;
+ }
+ text.append("computePropValue('" + p.getName() + "')");
+ }
+ text.append("];");
+ }
+ selectionScript.insert(startPos, text);
+ }
+
+ return selectionScript;
+ }
+
+ public SortedMap<PermutationId, List<Map<String, String>>> getPermutationsMap() {
+ return propMapsByPermutation;
+ }
+
+ /**
+ * Find all instances of {@link SelectionInformation} and add them to the
+ * internal map of selection information.
+ */
+ public void setupPermutationsMap(ArtifactSet artifacts) {
+ propMapsByPermutation =
+ new TreeMap<PermutationId, List<Map<String, String>>>();
+ for (SelectionInformation selInfo : artifacts.find(SelectionInformation.class)) {
+ TreeMap<String, String> entries = selInfo.getPropMap();
+ PermutationId permutationId = new PermutationId(selInfo.getStrongName(),
+ selInfo.getSoftPermutationId());
+ if (!propMapsByPermutation.containsKey(permutationId)) {
+ propMapsByPermutation.put(permutationId,
+ Lists.<Map<String, String>> create(entries));
+ } else {
+ propMapsByPermutation.put(permutationId, Lists.add(
+ propMapsByPermutation.get(permutationId), entries));
+ }
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/PropertiesMappingArtifact.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/PropertiesMappingArtifact.java
new file mode 100644
index 0000000..a7b4caf
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/PropertiesMappingArtifact.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may 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.linker.impl;
+
+import com.google.gwt.core.ext.Linker;
+import com.google.gwt.core.ext.linker.Artifact;
+import com.google.gwt.core.ext.linker.Transferable;
+import com.google.gwt.core.ext.linker.impl.PermutationsUtil.PermutationId;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Artifact that contains a mapping from deferred binding properties to a string (which typically
+ * represents another artifact). These are typically paired with an emitted
+ * artifact containing the results of {@link #getSerialized()}.
+ */
+@Transferable
+public class PropertiesMappingArtifact extends Artifact<PropertiesMappingArtifact> {
+
+ private final Map<PermutationId, List<Map<String, String>>> mappings;
+ private final String serialized;
+
+ public PropertiesMappingArtifact(Class<? extends Linker> linker,
+ Map<PermutationId, List<Map<String, String>>> mappings) {
+ super(linker);
+ this.mappings = mappings;
+
+ StringBuilder sb = new StringBuilder();
+ for (Entry<PermutationId, List<Map<String, String>>> mapping : mappings.entrySet()) {
+ for (Map<String, String> deferredBindings : mapping.getValue()) {
+ sb.append(mapping.getKey().getStrongName() + ".cache.js");
+ sb.append('\n');
+ for (Entry<String, String> oneBinding : deferredBindings.entrySet()) {
+ sb.append(oneBinding.getKey());
+ sb.append(' ');
+ sb.append(oneBinding.getValue());
+ sb.append('\n');
+ }
+ sb.append('\n');
+ }
+ }
+
+ serialized = sb.toString();
+ }
+
+ @Override
+ public int compareToComparableArtifact(PropertiesMappingArtifact that) {
+ return serialized.compareTo(that.getSerialized());
+ }
+
+ @Override
+ public Class<PropertiesMappingArtifact> getComparableArtifactType() {
+ return PropertiesMappingArtifact.class;
+ }
+
+ public String getSerialized() {
+ return serialized;
+ }
+
+ @Override
+ public int hashCode() {
+ return serialized.hashCode();
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/PropertiesUtil.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/PropertiesUtil.java
new file mode 100644
index 0000000..3ee5355
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/PropertiesUtil.java
@@ -0,0 +1,68 @@
+/*
+ * 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.linker.impl;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.linker.SelectionProperty;
+
+/**
+ * A utility class to fill in the properties javascript in linker templates.
+ */
+public class PropertiesUtil {
+ public static StringBuffer addPropertiesJs(StringBuffer selectionScript,
+ TreeLogger logger, LinkerContext context) {
+ int startPos;
+
+ // Add property providers
+ startPos = selectionScript.indexOf("// __PROPERTIES_END__");
+ if (startPos != -1) {
+ for (SelectionProperty p : context.getProperties()) {
+ String text = generatePropertyProvider(p);
+ selectionScript.insert(startPos, text);
+ startPos += text.length();
+ }
+ }
+ return selectionScript;
+ }
+
+ private static String generatePropertyProvider(SelectionProperty prop) {
+ StringBuffer toReturn = new StringBuffer();
+
+ if (prop.tryGetValue() == null && !prop.isDerived()) {
+ toReturn.append("providers['" + prop.getName() + "'] = function()");
+ toReturn.append(prop.getPropertyProvider());
+ toReturn.append(";");
+
+ toReturn.append("values['" + prop.getName() + "'] = {");
+ boolean needsComma = false;
+ int counter = 0;
+ for (String value : prop.getPossibleValues()) {
+ if (needsComma) {
+ toReturn.append(",");
+ } else {
+ needsComma = true;
+ }
+ toReturn.append("'" + value + "':");
+ toReturn.append(counter++);
+ }
+ toReturn.append("};");
+ }
+
+ return toReturn.toString();
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/ResourceInjectionUtil.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/ResourceInjectionUtil.java
new file mode 100644
index 0000000..0986ee4
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/ResourceInjectionUtil.java
@@ -0,0 +1,126 @@
+/*
+ * 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.linker.impl;
+
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.ScriptReference;
+import com.google.gwt.core.ext.linker.StylesheetReference;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Utility class to help linkers do resource injection.
+ */
+public class ResourceInjectionUtil {
+ public static StringBuffer injectResources(StringBuffer selectionScript,
+ ArtifactSet artifacts) {
+ // Add external dependencies
+ int startPos = selectionScript.indexOf("// __MODULE_STYLES_END__");
+ if (startPos != -1) {
+ for (StylesheetReference resource : artifacts.find(StylesheetReference.class)) {
+ String text = generateStylesheetInjector(resource.getSrc());
+ selectionScript.insert(startPos, text);
+ startPos += text.length();
+ }
+ }
+
+ startPos = selectionScript.indexOf("// __MODULE_SCRIPTS_END__");
+ if (startPos != -1) {
+ for (ScriptReference resource : artifacts.find(ScriptReference.class)) {
+ String text = generateScriptInjector(resource.getSrc());
+ selectionScript.insert(startPos, text);
+ startPos += text.length();
+ }
+ }
+ return selectionScript;
+ }
+
+ private static String generateScriptInjector(String scriptUrl) {
+ if (isRelativeURL(scriptUrl)) {
+ return " if (!__gwt_scriptsLoaded['"
+ + scriptUrl
+ + "']) {\n"
+ + " __gwt_scriptsLoaded['"
+ + scriptUrl
+ + "'] = true;\n"
+ + " document.write('<script language=\\\"javascript\\\" src=\\\"'+base+'"
+ + scriptUrl + "\\\"></script>');\n" + " }\n";
+ } else {
+ return " if (!__gwt_scriptsLoaded['" + scriptUrl + "']) {\n"
+ + " __gwt_scriptsLoaded['" + scriptUrl + "'] = true;\n"
+ + " document.write('<script language=\\\"javascript\\\" src=\\\""
+ + scriptUrl + "\\\"></script>');\n" + " }\n";
+ }
+ }
+
+ /**
+ * Generate a Snippet of JavaScript to inject an external stylesheet.
+ *
+ * <pre>
+ * if (!__gwt_stylesLoaded['URL']) {
+ * var l = $doc.createElement('link');
+ * __gwt_styleLoaded['URL'] = l;
+ * l.setAttribute('rel', 'stylesheet');
+ * l.setAttribute('href', HREF_EXPR);
+ * $doc.getElementsByTagName('head')[0].appendChild(l);
+ * }
+ * </pre>
+ */
+ private static String generateStylesheetInjector(String stylesheetUrl) {
+ String hrefExpr = "'" + stylesheetUrl + "'";
+ if (isRelativeURL(stylesheetUrl)) {
+ hrefExpr = "base + " + hrefExpr;
+ }
+
+ return "if (!__gwt_stylesLoaded['" + stylesheetUrl + "']) {\n "
+ + " var l = $doc.createElement('link');\n "
+ + " __gwt_stylesLoaded['" + stylesheetUrl + "'] = l;\n "
+ + " l.setAttribute('rel', 'stylesheet');\n "
+ + " l.setAttribute('href', " + hrefExpr + ");\n "
+ + " $doc.getElementsByTagName('head')[0].appendChild(l);\n "
+ + "}\n";
+ }
+
+ /**
+ * Determines whether or not the URL is relative.
+ *
+ * @param src the test url
+ * @return <code>true</code> if the URL is relative, <code>false</code> if not
+ */
+ private static boolean isRelativeURL(String src) {
+ // A straight absolute url for the same domain, server, and protocol.
+ if (src.startsWith("/")) {
+ return false;
+ }
+
+ // If it can be parsed as a URL, then it's probably absolute.
+ try {
+ // Just check to see if it can be parsed, no need to store the result.
+ new URL(src);
+
+ // Let's guess that it is absolute (thus, not relative).
+ return false;
+ } catch (MalformedURLException e) {
+ // Do nothing, since it was a speculative parse.
+ }
+
+ // Since none of the above matched, let's guess that it's relative.
+ return true;
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 055c04a..3e9003c 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -23,27 +23,22 @@
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.EmittedArtifact;
-import com.google.gwt.core.ext.linker.ScriptReference;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.SoftPermutation;
-import com.google.gwt.core.ext.linker.StylesheetReference;
-import com.google.gwt.dev.util.StringKey;
+import com.google.gwt.dev.util.DefaultTextOutput;
+import com.google.gwt.dev.util.TextOutput;
import com.google.gwt.dev.util.Util;
-import com.google.gwt.dev.util.collect.HashSet;
-import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.util.tools.Utility;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -58,29 +53,6 @@
* goes away?
*/
- /**
- * This represents the combination of a unique content hash (i.e. the MD5 of
- * the bytes to be written into the cache.html file) and a soft permutation
- * id.
- */
- protected static class PermutationId extends StringKey {
- private final int softPermutationId;
- private final String strongName;
-
- public PermutationId(String strongName, int softPermutationId) {
- super(strongName + ":" + softPermutationId);
- this.strongName = strongName;
- this.softPermutationId = softPermutationId;
- }
-
- public int getSoftPermutationId() {
- return softPermutationId;
- }
-
- public String getStrongName() {
- return strongName;
- }
- }
/**
* File name for computeScriptBase.js.
@@ -98,41 +70,15 @@
protected static final String FRAGMENT_SUBDIR = "deferredjs";
/**
- * File name for permutations.js.
+ * Utility class to handle insertion of permutations code.
*/
- protected static final String PERMUTATIONS_JS = "com/google/gwt/core/ext/linker/impl/permutations.js";
-
+ protected static PermutationsUtil permutationsUtil = new PermutationsUtil();
+
/**
* File name for processMetas.js.
*/
- protected static final String PROCESS_METAS_JS = "com/google/gwt/core/ext/linker/impl/processMetas.js";
+ protected static final String PROCESS_METAS_JS = "com/google/gwt/core/ext/linker/impl/processMetasOld.js";
- /**
- * Determines whether or not the URL is relative.
- *
- * @param src the test url
- * @return <code>true</code> if the URL is relative, <code>false</code> if not
- */
- protected static boolean isRelativeURL(String src) {
- // A straight absolute url for the same domain, server, and protocol.
- if (src.startsWith("/")) {
- return false;
- }
-
- // If it can be parsed as a URL, then it's probably absolute.
- try {
- // Just check to see if it can be parsed, no need to store the result.
- new URL(src);
-
- // Let's guess that it is absolute (thus, not relative).
- return false;
- } catch (MalformedURLException e) {
- // Do nothing, since it was a speculative parse.
- }
-
- // Since none of the above matched, let's guess that it's relative.
- return true;
- }
protected static void replaceAll(StringBuffer buf, String search,
String replace) {
@@ -143,12 +89,7 @@
}
}
- /**
- * This maps each unique permutation to the property settings for that
- * compilation. A single compilation can have multiple property settings if
- * the compiles for those settings yielded the exact same compiled output.
- */
- private final SortedMap<PermutationId, List<Map<String, String>>> propMapsByPermutation = new TreeMap<PermutationId, List<Map<String, String>>>();
+ private String selectionScriptText = null;
/**
* This method is left in place for existing subclasses of
@@ -178,11 +119,14 @@
}
return toReturn;
} else {
- processSelectionInformation(artifacts);
-
+ permutationsUtil.setupPermutationsMap(artifacts);
ArtifactSet toReturn = new ArtifactSet(artifacts);
- toReturn.add(emitSelectionScript(logger, context, artifacts));
- maybeAddHostedModeFile(logger, toReturn);
+ EmittedArtifact art = emitSelectionScript(logger, context, artifacts);
+ if (art != null) {
+ toReturn.add(art);
+ }
+ maybeOutputPropertyMap(logger, context, toReturn);
+ maybeAddHostedModeFile(logger, context, toReturn);
return toReturn;
}
}
@@ -219,9 +163,6 @@
protected EmittedArtifact emitSelectionScript(TreeLogger logger,
LinkerContext context, ArtifactSet artifacts)
throws UnableToCompleteException {
- String selectionScript = generateSelectionScript(logger, context, artifacts);
- selectionScript = context.optimizeJavaScript(logger, selectionScript);
-
/*
* Last modified is important to keep hosted mode refreses from clobbering
* web mode compiles. We set the timestamp on the hosted mode selection
@@ -229,16 +170,47 @@
* mode, we just set it to now.
*/
long lastModified;
- if (propMapsByPermutation.isEmpty()) {
+ if (permutationsUtil.getPermutationsMap().isEmpty()) {
lastModified = context.getModuleLastModified();
} else {
lastModified = System.currentTimeMillis();
}
-
- return emitString(logger, selectionScript, context.getModuleName()
+ String ss = generateSelectionScript(logger, context, artifacts);
+ return emitString(logger, ss, context.getModuleName()
+ ".nocache.js", lastModified);
}
+
+ /**
+ * Generate a selection script. The selection information should previously
+ * have been scanned using {@link #setupPermutationsMap(ArtifactSet)}.
+ */
+ protected String fillSelectionScriptTemplate(StringBuffer selectionScript,
+ TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws
+ UnableToCompleteException {
+ String computeScriptBase;
+ String processMetas;
+ try {
+ computeScriptBase = Utility.getFileFromClassPath(COMPUTE_SCRIPT_BASE_JS);
+ processMetas = Utility.getFileFromClassPath(PROCESS_METAS_JS);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to read selection script template",
+ e);
+ throw new UnableToCompleteException();
+ }
+ replaceAll(selectionScript, "__COMPUTE_SCRIPT_BASE__", computeScriptBase);
+ replaceAll(selectionScript, "__PROCESS_METAS__", processMetas);
+
+ selectionScript = ResourceInjectionUtil.injectResources(selectionScript, artifacts);
+ permutationsUtil.addPermutationsJs(selectionScript, logger, context);
+
+ replaceAll(selectionScript, "__MODULE_FUNC__",
+ context.getModuleFunctionName());
+ replaceAll(selectionScript, "__MODULE_NAME__", context.getModuleName());
+ replaceAll(selectionScript, "__HOSTED_FILENAME__", getHostedFilename());
+ return selectionScript.toString();
+ }
+
/**
* @param logger a TreeLogger
* @param context a LinkerContext
@@ -257,132 +229,34 @@
protected byte[] generatePrimaryFragment(TreeLogger logger,
LinkerContext context, CompilationResult result, String[] js)
throws UnableToCompleteException {
- StringBuffer b = new StringBuffer();
- b.append(getModulePrefix(logger, context, result.getStrongName(), js.length));
- b.append(js[0]);
- b.append(getModuleSuffix(logger, context));
- return Util.getBytes(b.toString());
- }
-
- protected String generatePropertyProvider(SelectionProperty prop) {
- StringBuffer toReturn = new StringBuffer();
-
- if (prop.tryGetValue() == null && !prop.isDerived()) {
- toReturn.append("providers['" + prop.getName() + "'] = function()");
- toReturn.append(prop.getPropertyProvider());
- toReturn.append(";");
-
- toReturn.append("values['" + prop.getName() + "'] = {");
- boolean needsComma = false;
- int counter = 0;
- for (String value : prop.getPossibleValues()) {
- if (needsComma) {
- toReturn.append(",");
- } else {
- needsComma = true;
- }
- toReturn.append("'" + value + "':");
- toReturn.append(counter++);
- }
- toReturn.append("};");
- }
-
- return toReturn.toString();
- }
-
- protected String generateScriptInjector(String scriptUrl) {
- if (isRelativeURL(scriptUrl)) {
- return " if (!__gwt_scriptsLoaded['"
- + scriptUrl
- + "']) {\n"
- + " __gwt_scriptsLoaded['"
- + scriptUrl
- + "'] = true;\n"
- + " document.write('<script language=\\\"javascript\\\" src=\\\"'+base+'"
- + scriptUrl + "\\\"></script>');\n" + " }\n";
- } else {
- return " if (!__gwt_scriptsLoaded['" + scriptUrl + "']) {\n"
- + " __gwt_scriptsLoaded['" + scriptUrl + "'] = true;\n"
- + " document.write('<script language=\\\"javascript\\\" src=\\\""
- + scriptUrl + "\\\"></script>');\n" + " }\n";
- }
- }
-
- /**
- * Generate a selection script. The selection information should previously
- * have been scanned using {@link #processSelectionInformation(ArtifactSet)}.
- */
- protected String generateSelectionScript(TreeLogger logger,
- LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
- StringBuffer selectionScript = getSelectionScriptStringBuffer(logger, context);
-
- String computeScriptBase;
- String processMetas;
- try {
- computeScriptBase = Utility.getFileFromClassPath(COMPUTE_SCRIPT_BASE_JS);
- processMetas = Utility.getFileFromClassPath(PROCESS_METAS_JS);
- } catch (IOException e) {
- logger.log(TreeLogger.ERROR, "Unable to read selection script template",
- e);
- throw new UnableToCompleteException();
- }
- replaceAll(selectionScript, "__COMPUTE_SCRIPT_BASE__", computeScriptBase);
- replaceAll(selectionScript, "__PROCESS_METAS__", processMetas);
-
- // Add external dependencies
- int startPos = selectionScript.indexOf("// __MODULE_STYLES_END__");
- if (startPos != -1) {
- for (StylesheetReference resource : artifacts.find(StylesheetReference.class)) {
- String text = generateStylesheetInjector(resource.getSrc());
- selectionScript.insert(startPos, text);
- startPos += text.length();
- }
- }
-
- startPos = selectionScript.indexOf("// __MODULE_SCRIPTS_END__");
- if (startPos != -1) {
- for (ScriptReference resource : artifacts.find(ScriptReference.class)) {
- String text = generateScriptInjector(resource.getSrc());
- selectionScript.insert(startPos, text);
- startPos += text.length();
- }
- }
-
- // This method needs to be called after all of the .js files have been
- // swapped into the selectionScript since it will fill in __MODULE_NAME__
- // and many of the .js files contain that template variable
- selectionScript =
- processSelectionScriptCommon(selectionScript, logger, context);
-
- return selectionScript.toString();
+ TextOutput to = new DefaultTextOutput(context.isOutputCompact());
+ to.print(generatePrimaryFragmentString(
+ logger, context, result.getStrongName(), js[0], js.length));
+ return Util.getBytes(to.toString());
}
- /**
- * Generate a Snippet of JavaScript to inject an external stylesheet.
- *
- * <pre>
- * if (!__gwt_stylesLoaded['URL']) {
- * var l = $doc.createElement('link');
- * __gwt_styleLoaded['URL'] = l;
- * l.setAttribute('rel', 'stylesheet');
- * l.setAttribute('href', HREF_EXPR);
- * $doc.getElementsByTagName('head')[0].appendChild(l);
- * }
- * </pre>
- */
- protected String generateStylesheetInjector(String stylesheetUrl) {
- String hrefExpr = "'" + stylesheetUrl + "'";
- if (isRelativeURL(stylesheetUrl)) {
- hrefExpr = "base + " + hrefExpr;
+ protected String generatePrimaryFragmentString(TreeLogger logger,
+ LinkerContext context, String strongName, String js, int length)
+ throws UnableToCompleteException {
+ StringBuffer b = new StringBuffer();
+ b.append(getModulePrefix(logger, context, strongName, length));
+ b.append(js);
+ b.append(getModuleSuffix(logger, context));
+ return wrapPrimaryFragment(logger, context, b.toString());
+ }
+
+ protected String generateSelectionScript(TreeLogger logger,
+ LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
+ if (selectionScriptText != null) {
+ return selectionScriptText;
}
-
- return "if (!__gwt_stylesLoaded['" + stylesheetUrl + "']) {\n "
- + " var l = $doc.createElement('link');\n "
- + " __gwt_stylesLoaded['" + stylesheetUrl + "'] = l;\n "
- + " l.setAttribute('rel', 'stylesheet');\n "
- + " l.setAttribute('href', " + hrefExpr + ");\n "
- + " $doc.getElementsByTagName('head')[0].appendChild(l);\n "
- + "}\n";
+ StringBuffer buffer = readFileToStringBuffer(
+ getSelectionScriptTemplate(logger,context), logger);
+ selectionScriptText = fillSelectionScriptTemplate(
+ buffer, logger, context, artifacts);
+ selectionScriptText =
+ context.optimizeJavaScript(logger, selectionScriptText);
+ return selectionScriptText;
}
protected abstract String getCompilationExtension(TreeLogger logger,
@@ -391,7 +265,7 @@
protected String getHostedFilename() {
return "";
}
-
+
/**
* Compute the beginning of a JavaScript file that will hold the main module
* implementation.
@@ -399,7 +273,7 @@
protected abstract String getModulePrefix(TreeLogger logger,
LinkerContext context, String strongName)
throws UnableToCompleteException;
-
+
/**
* Compute the beginning of a JavaScript file that will hold the main module
* implementation. By default, calls
@@ -416,29 +290,16 @@
protected abstract String getModuleSuffix(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
-
- protected StringBuffer getSelectionScriptStringBuffer(TreeLogger logger,
- LinkerContext context) throws UnableToCompleteException {
- StringBuffer selectionScript;
- try {
- selectionScript = new StringBuffer(
- Utility.getFileFromClassPath(getSelectionScriptTemplate(logger,
- context)));
- } catch (IOException e) {
- logger.log(TreeLogger.ERROR, "Unable to read selection script template",
- e);
- throw new UnableToCompleteException();
- }
- return selectionScript;
- }
-
+
protected abstract String getSelectionScriptTemplate(TreeLogger logger,
LinkerContext context) throws UnableToCompleteException;
+
/**
* Add the hosted file to the artifact set.
*/
- protected void maybeAddHostedModeFile(TreeLogger logger, ArtifactSet artifacts)
+ protected void maybeAddHostedModeFile(TreeLogger logger,
+ LinkerContext context, ArtifactSet artifacts)
throws UnableToCompleteException {
String hostedFilename = getHostedFilename();
if ("".equals(hostedFilename)) {
@@ -479,119 +340,26 @@
}
}
- /**
- * Find all instances of {@link SelectionInformation} and add them to the
- * internal map of selection information.
- */
- protected void processSelectionInformation(ArtifactSet artifacts) {
- for (SelectionInformation selInfo : artifacts.find(SelectionInformation.class)) {
- processSelectionInformation(selInfo);
- }
+ protected void maybeOutputPropertyMap(TreeLogger logger,
+ LinkerContext context, ArtifactSet toReturn) {
+ return;
}
- protected StringBuffer processSelectionScriptCommon(StringBuffer selectionScript,
- TreeLogger logger, LinkerContext context)
- throws UnableToCompleteException {
- String permutations;
+ protected StringBuffer readFileToStringBuffer(String filename,
+ TreeLogger logger) throws UnableToCompleteException {
+ StringBuffer buffer;
try {
- permutations = Utility.getFileFromClassPath(PERMUTATIONS_JS);
+ buffer = new StringBuffer(Utility.getFileFromClassPath(filename));
} catch (IOException e) {
- logger.log(TreeLogger.ERROR, "Unable to read selection script template",
- e);
+ logger.log(TreeLogger.ERROR, "Unable to read file: " + filename, e);
throw new UnableToCompleteException();
}
+ return buffer;
+ }
- replaceAll(selectionScript, "__PERMUTATIONS__", permutations);
- replaceAll(selectionScript, "__MODULE_FUNC__",
- context.getModuleFunctionName());
- replaceAll(selectionScript, "__MODULE_NAME__", context.getModuleName());
- replaceAll(selectionScript, "__HOSTED_FILENAME__", getHostedFilename());
-
- int startPos;
-
- // Add property providers
- startPos = selectionScript.indexOf("// __PROPERTIES_END__");
- if (startPos != -1) {
- for (SelectionProperty p : context.getProperties()) {
- String text = generatePropertyProvider(p);
- selectionScript.insert(startPos, text);
- startPos += text.length();
- }
- }
-
- // Possibly add permutations
- startPos = selectionScript.indexOf("// __PERMUTATIONS_END__");
- if (startPos != -1) {
- StringBuffer text = new StringBuffer();
- if (propMapsByPermutation.size() == 0) {
- // Hosted mode link.
- text.append("alert(\"GWT module '" + context.getModuleName()
- + "' may need to be (re)compiled\");");
- text.append("return;");
-
- } else if (propMapsByPermutation.size() == 1) {
- // Just one distinct compilation; no need to evaluate properties
- text.append("strongName = '"
- + propMapsByPermutation.keySet().iterator().next().getStrongName()
- + "';");
- } else {
- Set<String> propertiesUsed = new HashSet<String>();
- for (PermutationId permutationId : propMapsByPermutation.keySet()) {
- for (Map<String, String> propertyMap : propMapsByPermutation.get(permutationId)) {
- // unflatten([v1, v2, v3], 'strongName' + ':softPermId');
- // The soft perm ID is concatenated to improve string interning
- text.append("unflattenKeylistIntoAnswers([");
- boolean needsComma = false;
- for (SelectionProperty p : context.getProperties()) {
- if (p.tryGetValue() != null) {
- continue;
- } else if (p.isDerived()) {
- continue;
- }
-
- if (needsComma) {
- text.append(",");
- } else {
- needsComma = true;
- }
- text.append("'" + propertyMap.get(p.getName()) + "'");
- propertiesUsed.add(p.getName());
- }
-
- text.append("], '").append(permutationId.getStrongName()).append(
- "'");
- /*
- * For compatibility with older linkers, skip the soft permutation
- * if it's 0
- */
- if (permutationId.getSoftPermutationId() != 0) {
- text.append(" + ':").append(permutationId.getSoftPermutationId()).append(
- "'");
- }
- text.append(");\n");
- }
- }
-
- // strongName = answers[compute('p1')][compute('p2')];
- text.append("strongName = answers[");
- boolean needsIndexMarkers = false;
- for (SelectionProperty p : context.getProperties()) {
- if (!propertiesUsed.contains(p.getName())) {
- continue;
- }
- if (needsIndexMarkers) {
- text.append("][");
- } else {
- needsIndexMarkers = true;
- }
- text.append("computePropValue('" + p.getName() + "')");
- }
- text.append("];");
- }
- selectionScript.insert(startPos, text);
- }
-
- return selectionScript;
+ protected String wrapPrimaryFragment(TreeLogger logger,
+ LinkerContext context, String script) {
+ return script;
}
private List<Artifact<?>> emitSelectionInformation(String strongName,
@@ -618,19 +386,5 @@
return emitted;
}
-
- private Map<String, String> processSelectionInformation(
- SelectionInformation selInfo) {
- TreeMap<String, String> entries = selInfo.getPropMap();
- PermutationId permutationId = new PermutationId(selInfo.getStrongName(),
- selInfo.getSoftPermutationId());
- if (!propMapsByPermutation.containsKey(permutationId)) {
- propMapsByPermutation.put(permutationId,
- Lists.<Map<String, String>> create(entries));
- } else {
- propMapsByPermutation.put(permutationId, Lists.add(
- propMapsByPermutation.get(permutationId), entries));
- }
- return entries;
- }
+
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/computeScriptBase.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/computeScriptBase.js
index 016bb4f..066ce99 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/computeScriptBase.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/computeScriptBase.js
@@ -25,10 +25,16 @@
* wherever COMPUTE_SCRIPT_BASE appears with underlines
* on each side.
*/
+
function computeScriptBase() {
- //First, check if the meta properties give the baseUrl
- if (metaProps['baseUrl']) {
- base = metaProps['baseUrl'];
+ // First, check if the meta properties give the baseUrl
+ var metaVal = __gwt_getMetaProperty('baseUrl');
+ // Note: the base variable should not be defined in this function because in
+ // the older templates (like IFrameTemplate.js), base is defined outside this
+ // function, and they rely on the fact that calling computeScriptBase will set
+ // that base variable rather than using the return value.
+ if (metaVal != null) {
+ base = metaVal;
return base;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/devmode.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/devmode.js
index 6c4a179..7a27bef 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/devmode.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/devmode.js
@@ -4,33 +4,10 @@
var $entry;
var $hostedHtmlVersion="2.1";
-// We assume that the following variables have been set in the same scope as
-// this code is executing in:
-// $wnd - the main window which the GWT code is affecting. in iframe based linkers
-// $wnd = window.parent; Usually, in others, $wnd = window;
-// __gwtModuleFunction - the bootstrap function, which exports needed variables.
-// usually this is set to something like $wnd.hello
-
-// Pull some variables from the bootstrapping code. If this file got the prefix
-// and suffix that the regular md5.js files did, then we wouldn't need this,
-// although we would need to give this file a gwtOnLoad() entry point. If we
-// did this, we also would not need the __gwtModuleFunction and $moduleName
-// variables to be set.
-var $sendStats = __gwtModuleFunction.__sendStats;
-var $doc = $wnd.document
-var $errFn = __gwtModuleFunction.__errFn;
-var $moduleName = __gwtModuleFunction.__moduleName;
-var $moduleBase = __gwtModuleFunction.__moduleBase;
-var __gwt_getProperty = __gwtModuleFunction.__computePropValue;
-
-// Even though we call the $sendStats function in the code written in this
-// file, some of the compilation code still needs the $stats and $sessionId
-// variables to be available.
-var $stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null;
-var $sessionId = $wnd.__gwtStatsSessionId ? $wnd.__gwtStatsSessionId : null;
-
-$sendStats("moduleStartup", "moduleEvalStart");
-
+var $errFn;
+var $moduleName;
+var $moduleBase;
+var __gwt_getProperty;
/******************************************************************************
* WRITE ME - what does this invokes stuff do??? Probably related to invoking
@@ -340,18 +317,24 @@
var pluginFinders = [findPluginXPCOM, findPluginObject, findPluginEmbed];
var codeServer = getCodeServer();
var plugin;
- for (var i = 0; i < pluginFinders.length; ++i) {
- try {
- plugin = pluginFinders[i]();
- if (plugin != null && plugin.init(window)) {
- if (!plugin.connect(url, sessionId, codeServer, $moduleName,
- $hostedHtmlVersion)) {
- pluginConnectionError(codeServer);
- return null;
- }
- return plugin;
+ for (var i = 0; i < pluginFinders.length; ++i) {
+ try {
+ plugin = pluginFinders[i]();
+ if (plugin != null) {
+ if (!plugin.init(window)) {
+ pluginConnectionError(codeServer);
+ return null;
}
- } catch (e) {
+ if (!plugin.connect(url, sessionId, codeServer, $moduleName,
+ $hostedHtmlVersion)) {
+ pluginConnectionError(codeServer);
+ return null;
+ }
+ return plugin;
+ }
+ } catch (e) {
+ pluginConnectionError(codeServer);
+ return null;
}
}
return null;
@@ -361,27 +344,29 @@
/******************************************************************************
* DevMode startup code
*****************************************************************************/
-window.onunload = function() { };
+function gwtOnLoad(errFn, moduleName, moduleBase, softPermutationId, computePropValue) {
+ $errFn = errFn;
+ $moduleName = moduleName;
+ $moduleBase = moduleBase;
+ __gwt_getProperty = computePropValue;
+
+ window.onunload = function() { };
+ doBrowserSpecificFixes();
-$sendStats("moduleStartup", "moduleEvalEnd");
-doBrowserSpecificFixes();
+ if (!findPluginXPCOM()) {
+ embedPlugin();
+ }
-if (!findPluginXPCOM()) {
- embedPlugin();
+ var topWin = window.top;
+ if (!topWin.__gwt_SessionID) {
+ topWin.__gwt_SessionID = generateSessionId();
+ }
+
+ var plugin = tryConnectingToPlugin(topWin.__gwt_SessionID, topWin.location.href);
+ if (plugin == null) {
+ loadIframe("http://gwt.google.com/missing-plugin/");
+ } else {
+ window.onUnload = disconnectPlugin();
+ }
}
-var topWin = window.top;
-if (!topWin.__gwt_SessionID) {
- topWin.__gwt_SessionID = generateSessionId();
-}
-
-var plugin = tryConnectingToPlugin(topWin.__gwt_SessionID, topWin.location.href);
-if (plugin == null) {
- loadIframe("http://gwt.google.com/missing-plugin/");
-} else {
- window.onUnload = disconnectPlugin();
-}
-
-// It looks like we never send the "moduleStartup", "end" stat in dev mode.
-// Is that on purpose, or should we send it here?
-
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js
index bae2386..44226d3 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installLocationIframe.js
@@ -16,20 +16,17 @@
var frameDoc;
function getInstallLocationDoc() {
- if (!frameDoc) {
- setupInstallLocation();
- }
+ setupInstallLocation();
return frameDoc;
}
function getInstallLocation() {
- if (!frameDoc) {
- setupInstallLocation();
- }
+ setupInstallLocation();
return frameDoc.getElementsByTagName('body')[0];
}
function setupInstallLocation() {
+ if (frameDoc) { return; }
// Create the script frame, making sure it's invisible, but not
// "display:none", which keeps some browsers from running code in it.
var scriptFrame = $doc.createElement('iframe');
@@ -56,9 +53,6 @@
var script = frameDoc.createElement('script');
script.language='javascript';
var temp = "var $wnd = window.parent;";
- if (isHostedMode) {
- temp += "var __gwtModuleFunction = $wnd.__MODULE_FUNC__;";
- }
script.text = temp;
frameDocbody.appendChild(script);
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js
new file mode 100644
index 0000000..43d4b0a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptAlreadyIncluded.js
@@ -0,0 +1,27 @@
+function installScript(filename) {
+ // Provides the getInstallLocation() and getInstallLocationDoc() functions
+ __INSTALL_LOCATION__
+
+ // Provides the setupWaitForBodyLoad() and isBodyLoaded() functions
+ __WAIT_FOR_BODY_LOADED__
+
+ function installCode(code) {
+ var docbody = getInstallLocation();
+ var script = getInstallLocationDoc().createElement('script');
+ script.language='javascript';
+ script.text = code;
+ docbody.appendChild(script);
+
+ // Remove the tags to shrink the DOM a little.
+ // It should have installed its code immediately after being added.
+ docbody.removeChild(script);
+ }
+
+ // Set up a script tag to start downloading immediately, as well as a
+ // callback to install the code once it is downloaded and the body is loaded.
+ __MODULE_FUNC__.onScriptDownloaded = function(code) {
+ setupWaitForBodyLoad(function() {
+ installCode(code);
+ });
+ };
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js
new file mode 100644
index 0000000..16c19aa
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptDirect.js
@@ -0,0 +1,27 @@
+function installScript(filename) {
+ // Provides the getInstallLocation() and getInstallLocationDoc() functions
+ __INSTALL_LOCATION__
+
+ // Provides the setupWaitForBodyLoad() and isBodyLoaded() functions
+ __WAIT_FOR_BODY_LOADED__
+
+ function installCode(code) {
+ var docbody = getInstallLocation();
+ var script = getInstallLocationDoc().createElement('script');
+ script.language='javascript';
+ script.src = code;
+ sendStats('moduleStartup', 'moduleRequested');
+ docbody.appendChild(script);
+
+ // Remove the tags to shrink the DOM a little.
+ // It should have installed its code immediately after being added.
+ docbody.removeChild(script);
+ }
+
+ // Just pass along the filename so that a script tag can be installed in the
+ // iframe to download it. Since we will be adding the iframe to the body,
+ // we still need to wait for the body to load before going forward.
+ setupWaitForBodyLoad(function() {
+ installCode(filename);
+ });
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js
new file mode 100644
index 0000000..877e4e2
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js
@@ -0,0 +1,41 @@
+function installScript(filename) {
+ // Provides the getInstallLocation() and getInstallLocationDoc() functions
+ __INSTALL_LOCATION__
+
+ // Provides the setupWaitForBodyLoad() and isBodyLoaded() functions
+ __WAIT_FOR_BODY_LOADED__
+
+ function installCode(code) {
+ var docbody = getInstallLocation();
+ var script = getInstallLocationDoc().createElement('script');
+ script.language='javascript';
+ script.text = code;
+ docbody.appendChild(script);
+
+ // Remove the tags to shrink the DOM a little.
+ // It should have installed its code immediately after being added.
+ docbody.removeChild(script);
+ }
+
+ // Set up a script tag to start downloading immediately, as well as a
+ // callback to install the code once it is downloaded and the body is loaded.
+ __MODULE_FUNC__.onScriptDownloaded = function(code) {
+ setupWaitForBodyLoad(function() {
+ installCode(code);
+ });
+ };
+ sendStats('moduleStartup', 'moduleRequested');
+ if (isBodyLoaded()) {
+ // if the body is loaded, then the tag to download the script can be added
+ // in a non-destructive manner
+ var script = document.createElement('script');
+ script.src = filename;
+ $doc.getElementsByTagName('head')[0].appendChild(script);
+ } else {
+ // if the doc has not yet loaded, go ahead and do a destructive
+ // document.write since we want to immediately start the download.
+ // Note that we cannot append an element to the doc if it is still loading
+ // since this would cause problems in IE.
+ $doc.write("<script src='" + filename + "'></scr" + "ipt>");
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptNull.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptNull.js
new file mode 100644
index 0000000..e3787b9
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/installScriptNull.js
@@ -0,0 +1,3 @@
+function installScript(filename) {
+ // Does nothing
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/permutations.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/permutations.js
index 2c7372c..a36ce80 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/permutations.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/permutations.js
@@ -14,11 +14,18 @@
* the License.
*/
+function getCompiledCodeFilename() {
+
+ // A multi-tier lookup map that uses actual property values to quickly find
+ // the strong name of the cache.js file to load.
+ var answers = [];
+
+ var softPermutationId;
+
// 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) {
@@ -29,20 +36,28 @@
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;
+ // Provides the computePropvalue() function and sets the
+ // __gwt_isKnownPropertyValue and MODULE_FUNC__.__computePropValue variables
+ __PROPERTIES__
+
+ sendStats('bootstrap', 'selectingPermutation');
+ if (isHostedMode()) {
+ return __MODULE_FUNC__.__moduleBase + "__HOSTED_FILENAME__";
}
+ var strongName;
+ try {
+ // __PERMUTATIONS_BEGIN__
+ // Permutation logic is injected here. this code populates the
+ // answers variable.
+ // __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
+ }
+ __MODULE_FUNC__.__softPermutationId = softPermutationId;
+ return __MODULE_FUNC__.__moduleBase + strongName + '.cache.js';
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/permutationsNull.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/permutationsNull.js
new file mode 100644
index 0000000..5f5cc3b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/permutationsNull.js
@@ -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 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 getCompiledCodeFilename() {
+ // Provides the computePropvalue() function and sets the
+ // __gwt_isKnownPropertyValue and MODULE_FUNC__.__computePropValue variables
+ __PROPERTIES__
+
+ __MODULE_FUNC__.__softPermutationId = 0;
+ return null;
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetas.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetas.js
index 5b13dd6..2921189 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetas.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetas.js
@@ -22,6 +22,10 @@
* on each side.
*/
function processMetas() {
+ var metaProps = {}
+ var propertyErrorFunc;
+ var onLoadErrorFunc;
+
var metas = document.getElementsByTagName('meta');
for (var i = 0, n = metas.length; i < n; ++i) {
var meta = metas[i]
@@ -52,22 +56,30 @@
content = meta.getAttribute('content');
if (content) {
try {
- propertyErrorFunc = eval(content);
+ propertyErrorFunc = eval(content);
} catch (e) {
- alert('Bad handler \"' + content +
- '\" for \"gwt:onPropertyErrorFn\"');
+ alert('Bad handler \"' + content +
+ '\" for \"gwt:onPropertyErrorFn\"');
}
- }
+ }
} else if (name == 'gwt:onLoadErrorFn') {
content = meta.getAttribute('content');
if (content) {
- try {
- onLoadErrorFunc = eval(content);
- } catch (e) {
+ try {
+ onLoadErrorFunc = eval(content);
+ } catch (e) {
alert('Bad handler \"' + content + '\" for \"gwt:onLoadErrorFn\"');
}
}
}
}
}
+
+ // Set some of the variables in the main script
+ __gwt_getMetaProperty = function(name) {
+ var value = metaProps[name];
+ return (value == null) ? null : value;
+ }
+ __propertyErrorFunction = propertyErrorFunc;
+ __MODULE_FUNC__.__errFn = onLoadErrorFunc;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetasNull.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetasNull.js
new file mode 100644
index 0000000..ca99de4
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetasNull.js
@@ -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.
+ */
+
+/** Called to slurp up all <meta> tags:
+ * gwt:property, gwt:onPropertyErrorFn, gwt:onLoadErrorFn
+ *
+ * This is included into the selection scripts
+ * wherever PROCESS_METAS appears with underlines
+ * on each side.
+ */
+function processMetas() {
+ // Set some of the variables in the main script
+ __gwt_getMetaProperty = function(name) {
+ return null;
+ }
+ __propertyErrorFunction = null;
+ __MODULE_FUNC__.__errFn = null;
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetasOld.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetasOld.js
new file mode 100644
index 0000000..83223be
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/processMetasOld.js
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+/** Called to slurp up all <meta> tags:
+ * gwt:property, gwt:onPropertyErrorFn, gwt:onLoadErrorFn
+ *
+ * This is included into the selection scripts
+ * wherever PROCESS_METAS appears with underlines
+ * on each side.
+ */
+function processMetas() {
+ var metas = document.getElementsByTagName('meta');
+ for (var i = 0, n = metas.length; i < n; ++i) {
+ var meta = metas[i]
+ , name = meta.getAttribute('name')
+ , content;
+
+ if (name) {
+ name = name.replace('__MODULE_NAME__::', '');
+ if (name.indexOf('::') >= 0) {
+ // It's for a different module
+ continue;
+ }
+
+ if (name == 'gwt:property') {
+ content = meta.getAttribute('content');
+ if (content) {
+ var value, eq = content.indexOf('=');
+ if (eq >= 0) {
+ name = content.substring(0, eq);
+ value = content.substring(eq + 1);
+ } else {
+ name = content;
+ value = '';
+ }
+ metaProps[name] = value;
+ }
+ } else if (name == 'gwt:onPropertyErrorFn') {
+ content = meta.getAttribute('content');
+ if (content) {
+ try {
+ propertyErrorFunc = eval(content);
+ } catch (e) {
+ alert('Bad handler \"' + content +
+ '\" for \"gwt:onPropertyErrorFn\"');
+ }
+ }
+ } else if (name == 'gwt:onLoadErrorFn') {
+ content = meta.getAttribute('content');
+ if (content) {
+ try {
+ onLoadErrorFunc = eval(content);
+ } catch (e) {
+ alert('Bad handler \"' + content + '\" for \"gwt:onLoadErrorFn\"');
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/properties.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/properties.js
new file mode 100644
index 0000000..7228358
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/properties.js
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+ // Maps property names onto sets of legal values for that property.
+ var values = [];
+
+ // Maps property names onto a function to compute that property.
+ var providers = [];
+
+ // 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;
+ }
+
+ // __PROPERTIES_BEGIN__
+ // Properties logic is injected here. This code populates the values and
+ // providers variables
+ // __PROPERTIES_END__
+
+ // Determines whether or not a particular property value is allowed. Called by
+ // property providers.
+ __gwt_isKnownPropertyValue = function(propName, propValue) {
+ return propValue in values[propName];
+ }
+ __MODULE_FUNC__.__computePropValue = computePropValue;
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesNull.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesNull.js
new file mode 100644
index 0000000..393ce1a
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesNull.js
@@ -0,0 +1,4 @@
+ __gwt_isKnownPropertyValue = function(propName, propValue) {
+ return false;
+ }
+ __MODULE_FUNC__.__computePropValue = null;
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js
index 35ed95d..839a68d 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js
@@ -1,3 +1,8 @@
+// Check whether the body is loaded.
+function isBodyLoaded() {
+ return (/loaded|complete/.test($doc.readyState));
+}
+
// Setup code which waits for the body to be loaded and then calls the
// callback function
function setupWaitForBodyLoad(callback) {
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoadedNull.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoadedNull.js
new file mode 100644
index 0000000..3abdcb7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/waitForBodyLoadedNull.js
@@ -0,0 +1,10 @@
+// Check whether the body is loaded.
+function isBodyLoaded() {
+ return true;
+}
+
+// Setup code which waits for the body to be loaded and then calls the
+// callback function
+function setupWaitForBodyLoad(callback) {
+ callback();
+}
diff --git a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
index eeea37e..23d8b9f 100644
--- a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
@@ -20,20 +20,21 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
-import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.EmittedArtifact;
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.Shardable;
+import com.google.gwt.core.ext.linker.impl.PropertiesMappingArtifact;
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;
import com.google.gwt.util.tools.Utility;
import java.io.IOException;
+
/**
* 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
@@ -43,109 +44,35 @@
@LinkerOrder(Order.PRIMARY)
@Shardable
public class CrossSiteIframeLinker extends SelectionScriptLinker {
- // TODO(unnurg): For each of the following properties, decide whether to make
- // it a gwt.xml configuration property, a constant which can be overridden
- // by subclasses, or not configurable at all.
- private static final String installLocationJsProperty =
- "com/google/gwt/core/ext/linker/impl/installLocationIframe.js";
- private static final boolean processMetasProperty = true;
- private static final String scriptBaseProperty = "";
- private static final boolean startDownloadImmediatelyProperty = true;
- private static final String WAIT_FOR_BODY_LOADED_JS =
- "com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.js";
-
- private static final boolean waitForBodyLoadedProperty = true;
-
@Override
public String getDescription() {
return "Cross-Site-Iframe";
}
-
- @Override
- protected byte[] generatePrimaryFragment(TreeLogger logger,
- LinkerContext context, CompilationResult result, String[] js) {
- TextOutput script = new DefaultTextOutput(context.isOutputCompact());
- script.print(getModulePrefix(context, result.getStrongName()));
- script.print(js[0]);
- script.print(getModuleSuffix(logger, context));
- StringBuffer out = new StringBuffer();
-
- if (startDownloadImmediatelyProperty) {
- // Rewrite the code so it can be installed with
- // __MODULE_FUNC__.onScriptDownloaded
- out.append(context.getModuleFunctionName());
- out.append(".onScriptDownloaded(");
- out.append(JsToStringGenerationVisitor.javaScriptString(script.toString()));
- out.append(")");
- } else {
- out.append(script.toString());
- }
-
- return Util.getBytes(out.toString());
- }
@Override
- protected String generateSelectionScript(TreeLogger logger,
- LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
- StringBuffer selectionScript = getSelectionScriptStringBuffer(logger, context);
+ protected String fillSelectionScriptTemplate(StringBuffer ss,
+ TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws
+ UnableToCompleteException {
- String waitForBodyLoadedJs;
- String installLocationJs;
- String computeScriptBase;
- String processMetas;
-
- try {
- waitForBodyLoadedJs = Utility.getFileFromClassPath(WAIT_FOR_BODY_LOADED_JS);
- processMetas = Utility.getFileFromClassPath(PROCESS_METAS_JS);
- installLocationJs = Utility.getFileFromClassPath(installLocationJsProperty);
- computeScriptBase = Utility.getFileFromClassPath(COMPUTE_SCRIPT_BASE_JS);
- } catch (IOException e) {
- logger.log(TreeLogger.ERROR, "Unable to read selection script template",
- e);
- throw new UnableToCompleteException();
- }
+ // Must do installScript before installLocation and waitForBodyLoaded
+ includeJs(ss, logger, getJsInstallScript(context), "__INSTALL_SCRIPT__");
+ includeJs(ss, logger, getJsInstallLocation(context), "__INSTALL_LOCATION__");
+ includeJs(ss, logger, getJsWaitForBodyLoaded(context), "__WAIT_FOR_BODY_LOADED__");
- replaceAll(selectionScript, "__INSTALL_LOCATION__", installLocationJs);
-
- if (waitForBodyLoadedProperty) {
- replaceAll(selectionScript, "__WAIT_FOR_BODY_LOADED__", waitForBodyLoadedJs);
- } else {
- String waitForBodyLoadedNullImpl =
- "function setupWaitForBodyLoad(callback) { callback(); }";
- replaceAll(selectionScript, "__WAIT_FOR_BODY_LOADED__", waitForBodyLoadedNullImpl);
- }
-
- if (startDownloadImmediatelyProperty) {
- replaceAll(selectionScript, "__START_DOWNLOAD_IMMEDIATELY__", "true");
- } else {
- replaceAll(selectionScript, "__START_DOWNLOAD_IMMEDIATELY__", "false");
- }
+ // Must do permutations before providers
+ includeJs(ss, logger, getJsPermutations(context), "__PERMUTATIONS__");
+ includeJs(ss, logger, getJsProperties(context), "__PROPERTIES__");
+ includeJs(ss, logger, getJsProcessMetas(context), "__PROCESS_METAS__");
+ includeJs(ss, logger, getJsComputeScriptBase(context), "__COMPUTE_SCRIPT_BASE__");
- if (processMetasProperty) {
- replaceAll(selectionScript, "__PROCESS_METAS__", processMetas);
- } else {
- String processMetasNullImpl =
- "function processMetas() { }";
- replaceAll(selectionScript, "__PROCESS_METAS__", processMetasNullImpl);
- }
-
- String scriptBase = scriptBaseProperty;
- if ("".equals(scriptBase)) {
- replaceAll(selectionScript, "__COMPUTE_SCRIPT_BASE__", computeScriptBase);
- } else {
- String computeScriptBaseNullImpl =
- "function computeScriptBase() { return '" + scriptBase + "';}";
- replaceAll(selectionScript, "__COMPUTE_SCRIPT_BASE__", computeScriptBaseNullImpl);
- }
+ replaceAll(ss, "__MODULE_FUNC__", context.getModuleFunctionName());
+ replaceAll(ss, "__MODULE_NAME__", context.getModuleName());
+ replaceAll(ss, "__HOSTED_FILENAME__", getHostedFilename());
- // This method needs to be called after all of the .js files have been
- // swapped into the selectionScript since it will fill in __MODULE_NAME__
- // and many of the .js files contain that template variable
- selectionScript =
- processSelectionScriptCommon(selectionScript, logger, context);
+ permutationsUtil.addPermutationsJs(ss, logger, context);
- return selectionScript.toString();
+ return ss.toString();
}
@Override
@@ -153,49 +80,53 @@
LinkerContext context) {
return ".cache.js";
}
-
+
@Override
protected String getHostedFilename() {
return "devmode.js";
}
+ protected String getJsComputeScriptBase(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/computeScriptBase.js";
+ }
+
+ protected String getJsInstallLocation(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/installLocationIframe.js";
+ }
+
+ // If you override this to return installScriptDirect.js, then you should
+ // also override shouldInstallCode() to return false
+ protected String getJsInstallScript(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/installScriptEarlyDownload.js";
+ }
+
+ protected String getJsPermutations(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/permutations.js";
+ }
+
+ protected String getJsProcessMetas(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/processMetas.js";
+ }
+
+ protected String getJsProperties(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/properties.js";
+ }
+
+ protected String getJsWaitForBodyLoaded(LinkerContext context) {
+ return "com/google/gwt/core/ext/linker/impl/waitForBodyLoaded.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("$sendStats('moduleStartup', 'moduleEvalEnd');");
- out.newlineOpt();
- out.print("gwtOnLoad("
- + "__gwtModuleFunction.__errFn, "
- + "__gwtModuleFunction.__moduleName, "
- + "__gwtModuleFunction.__moduleBase, "
- + "__gwtModuleFunction.__softPermutationId);");
- out.newlineOpt();
- out.print("$sendStats('moduleStartup', 'end');");
-
- 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) {
TextOutput out = new DefaultTextOutput(context.isOutputCompact());
+
+ // We assume that the $wnd has been set in the same scope as this code is
+ // executing in. $wnd is the main window which the GWT code is affecting. It
+ // is also usually the location the bootstrap function was defined in.
+ // In iframe based linkers, $wnd = window.parent;
+ // Usually, in others, $wnd = window;
+
out.print("var __gwtModuleFunction = $wnd." + context.getModuleFunctionName() + ";");
out.newlineOpt();
out.print("var $sendStats = __gwtModuleFunction.__sendStats;");
@@ -219,4 +150,132 @@
return out.toString();
}
+ @Override
+ protected String getModuleSuffix(TreeLogger logger, LinkerContext context) {
+ DefaultTextOutput out = new DefaultTextOutput(context.isOutputCompact());
+
+ out.print("$sendStats('moduleStartup', 'moduleEvalEnd');");
+ out.newlineOpt();
+ out.print("gwtOnLoad("
+ + "__gwtModuleFunction.__errFn, "
+ + "__gwtModuleFunction.__moduleName, "
+ + "__gwtModuleFunction.__moduleBase, "
+ + "__gwtModuleFunction.__softPermutationId,"
+ + "__gwtModuleFunction.__computePropValue);");
+ out.newlineOpt();
+ out.print("$sendStats('moduleStartup', 'end');");
+
+ return out.toString();
+ }
+
+ @Override
+ protected String getSelectionScriptTemplate(TreeLogger logger,
+ LinkerContext context) {
+ return "com/google/gwt/core/linker/CrossSiteIframeTemplate.js";
+ }
+
+ protected void includeJs(StringBuffer selectionScript, TreeLogger logger,
+ String jsSource, String templateVar) throws UnableToCompleteException {
+ String js;
+ if (jsSource.endsWith(".js")) {
+ try {
+ js = Utility.getFileFromClassPath(jsSource);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to read file: " + jsSource, e);
+ throw new UnableToCompleteException();
+ }
+ } else {
+ js = jsSource;
+ }
+ replaceAll(selectionScript, templateVar, js);
+ }
+
+ @Override
+ protected void maybeAddHostedModeFile(TreeLogger logger,
+ LinkerContext context, ArtifactSet artifacts)
+ throws UnableToCompleteException {
+ String filename = getHostedFilename();
+ if ("".equals(filename)) {
+ return;
+ }
+
+ long lastModified = System.currentTimeMillis();
+ StringBuffer buffer = readFileToStringBuffer(
+ "com/google/gwt/core/ext/linker/impl/" + filename, logger);
+
+ String script = generatePrimaryFragmentString(
+ logger, context, "", buffer.toString(), 1);
+
+ EmittedArtifact devArtifact =
+ emitString(logger, script, filename, lastModified);
+ artifacts.add(devArtifact);
+ }
+
+ // Output compilation-mappings.txt
+ @Override
+ protected void maybeOutputPropertyMap(TreeLogger logger, LinkerContext context,
+ ArtifactSet toReturn) {
+ if (!shouldOutputPropertyMap(context) ||
+ permutationsUtil.getPermutationsMap() == null ||
+ permutationsUtil.getPermutationsMap().isEmpty()) {
+ return;
+ }
+
+ PropertiesMappingArtifact mappingArtifact =
+ new PropertiesMappingArtifact(CrossSiteIframeLinker.class,
+ permutationsUtil.getPermutationsMap());
+
+ toReturn.add(mappingArtifact);
+ EmittedArtifact serializedMap;
+ try {
+ serializedMap = emitString(logger, mappingArtifact.getSerialized(),
+ "compilation-mappings.txt");
+ serializedMap.setPrivate(false);
+ toReturn.add(serializedMap);
+ } catch (UnableToCompleteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // If you set this to return true, you should also override
+ // getJsPermutations() to return permutationsNull.js and
+ // getJsInstallScript() to return installScriptAlreadyIncluded.js
+ protected boolean shouldIncludeBootstrapInPrimaryFragment(LinkerContext context) {
+ return false;
+ }
+
+ protected boolean shouldInstallCode(LinkerContext context) {
+ return true;
+ }
+
+ protected boolean shouldOutputPropertyMap(LinkerContext context) {
+ return false;
+ }
+
+ @Override
+ protected String wrapPrimaryFragment(TreeLogger logger,
+ LinkerContext context, String script) {
+ StringBuffer out = new StringBuffer();
+ if (shouldIncludeBootstrapInPrimaryFragment(context)) {
+ try {
+ out.append(generateSelectionScript(logger, context, null));
+ } catch (UnableToCompleteException e) {
+ logger.log(TreeLogger.ERROR, "Problem setting up selection script", e);
+ e.printStackTrace();
+ }
+ }
+
+ if (shouldInstallCode(context)) {
+ // Rewrite the code so it can be installed with
+ // __MODULE_FUNC__.onScriptDownloaded
+ out.append(context.getModuleFunctionName());
+ out.append(".onScriptDownloaded(");
+ out.append(JsToStringGenerationVisitor.javaScriptString(script.toString()));
+ out.append(")");
+ } else {
+ out.append(script.toString());
+ }
+ return out.toString();
+ }
+
}
diff --git a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
index 7b7e8e0..5b458ca 100644
--- a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
+++ b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeTemplate.js
@@ -14,94 +14,14 @@
* the License.
*/
function __MODULE_FUNC__() {
-
- /****************************************************************************
- * Internal Global Variables
- ***************************************************************************/
- // Cache symbols locally for good obfuscation
- var $wnd = window
- ,$doc = document
-
- // 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
-
- ;
-
+ var $wnd = window;
+ var $doc = document;
sendStats('bootstrap', 'begin');
/****************************************************************************
* Internal Helper Functions
***************************************************************************/
- // Get the name of the filename which contains the GWT code (usually something
- // like 12BA3D5...21E.js) or devmode.js if we are in hosted mode.
- // Also sets the softPermutationId variable if appropriate.
- function getCompiledCodeFilename() {
- if (isHostedMode()) {
- return base + "__HOSTED_FILENAME__";
- }
- var strongName;
- try {
- // __PERMUTATIONS_BEGIN__
- // Permutation logic is injected here. this code populates the
- // answers variable.
- // __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 base + strongName + '.cache.js';
- }
-
- // Write a script tag to the element returned by getInstallLocation. We then
- // either set the content of that script tag to be the code, or set the src
- // tag if the code is actually a URL.
- function installCode(code, isUrl) {
- var docbody = getInstallLocation();
-
- // Inject the fetched script into the script frame.
- // The script will call onScriptInstalled.
- var script = getInstallLocationDoc().createElement('script');
- script.language='javascript';
- if (isUrl) {
- script.src = code;
- } else {
- script.text = code;
- }
- docbody.appendChild(script);
-
- // Remove the tags to shrink the DOM a little.
- // It should have installed its code immediately after being added.
- docbody.removeChild(script);
- }
-
- function isBodyLoaded() {
- return (/loaded|complete/.test($doc.readyState));
- }
-
function isHostedMode() {
var query = $wnd.location.search;
return (query.indexOf('gwt.codesvr=') != -1);
@@ -124,122 +44,72 @@
/****************************************************************************
- * Internal Helper functions that have been broken out into their own .js
- * files for readability and for easy sharing between linkers. The linker
- * code will inject these functions in these placeholders.
- ***************************************************************************/
- // Provides the getInstallLocation() function
- __INSTALL_LOCATION__
-
- // Provides the processMetas() function, and sets the metaProps,
- // onLoadErrorFunc and propertyErrorFunc variables
- __PROCESS_METAS__
-
- // Provides the computeScriptBase() function, which sets the base variable
- __COMPUTE_SCRIPT_BASE__
-
- // Provides the setupWaitForBodyLoad() function
- __WAIT_FOR_BODY_LOADED__
-
- // Provides functions used by the generated PERMUTATIONS code.
- __PERMUTATIONS__
-
-
- /****************************************************************************
- * WRITE ME
- ***************************************************************************/
- // __PROPERTIES_BEGIN__
- // Properties logic is injected here. This code populates the values and
- // providers variables
- // __PROPERTIES_END__
-
- // Determines whether or not a particular property value is allowed. Called by
- // property providers.
- 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;
- }
-
-
- /****************************************************************************
* Exposed Functions and Variables
***************************************************************************/
+ // These are set by various parts of the bootstrapping code, but they always
+ // need to exist, so give them all default values here.
+
// Exposed for the convenience of the devmode.js and md5.js files
__MODULE_FUNC__.__sendStats = sendStats;
// Exposed for the call made to gwtOnLoad. Some are not figured out yet, so
// assign them later, once the values are known.
__MODULE_FUNC__.__moduleName = '__MODULE_NAME__';
- __MODULE_FUNC__.__errFn;
- __MODULE_FUNC__.__moduleBase;
- __MODULE_FUNC__.__softPermutationId;
+ __MODULE_FUNC__.__errFn = null;
+ __MODULE_FUNC__.__moduleBase = 'DUMMY';
+ __MODULE_FUNC__.__softPermutationId = 0;
// Exposed for devmode.js
- __MODULE_FUNC__.__computePropValue = computePropValue;
+ __MODULE_FUNC__.__computePropValue = null;
+
+ // Exposed for property provider code
+ var __gwt_isKnownPropertyValue = function() { return false; };
+ var __gwt_getMetaProperty = function() { return null; };
+
+ // Exposed for permutations code
+ __propertyErrorFunction = null;
+
+
+ /****************************************************************************
+ * Internal Helper functions that have been broken out into their own .js
+ * files for readability and for easy sharing between linkers. The linker
+ * code will inject these functions in these placeholders.
+ ***************************************************************************/
+ // Provides the installScript() function.
+ __INSTALL_SCRIPT__
+
+ // Provides the processMetas() function which sets the __gwt_getMetaProperty
+ // __propertyErrorFunction and __MODULE_FUNC__.__errFn variables if needed
+ __PROCESS_METAS__
+
+ // Provides the computeScriptBase() function
+ __COMPUTE_SCRIPT_BASE__
+
+ // Provides the getCompiledCodeFilename() function which sets the
+ // __gwt_isKnownPropertyValue, MODULE_FUNC__.__computePropValue and
+ // __MODULE_FUNC__.__softPermutationId variables if needed
+ __PERMUTATIONS__
/****************************************************************************
* Bootstrap startup code
***************************************************************************/
-
- var startDownloadImmediately = __START_DOWNLOAD_IMMEDIATELY__;
- if (isHostedMode()) {
- // since hosted.js doesn't have the necessary wrappings, we always install
- // a script tag in the iframe rather than using a giant string literal.
- // If the devmode.js file went throught the same processing as the
- // md5.js files, this could go away.
- startDownloadImmediately = false;
- }
-
+ // Must be called before computeScriptBase() and getCompiledFilename()
processMetas();
- base = computeScriptBase();
- __MODULE_FUNC__.__errFn = onLoadErrorFunc;
- __MODULE_FUNC__.__moduleBase = base;
- sendStats('bootstrap', 'selectingPermutation');
+ // Must be set before getCompiledFilename() is called
+ __MODULE_FUNC__.__moduleBase = computeScriptBase();
+
+ // Must be done right before the "bootstrap" "end" stat is sent
var filename = getCompiledCodeFilename();
- __MODULE_FUNC__.__softPermutationId = softPermutationId;
- sendStats('bootstrap', 'end');
+ sendStats('bootstrap', 'end');
// For now, send this dummy statistic since some people are depending on it
// being present. TODO(unnurg): remove this statistic soon
sendStats('loadExternalRefs', 'begin');
sendStats('loadExternalRefs', 'end');
- sendStats('moduleStartup', 'moduleRequested');
- if (startDownloadImmediately) {
- // Set up a script tag to start downloading immediately, as well as a
- // callback to install the code once it is downloaded and the body is loaded.
- __MODULE_FUNC__.onScriptDownloaded = function(code) {
- setupWaitForBodyLoad(function() {
- installCode(code, false);
- });
- };
- if (isBodyLoaded()) {
- // if the body is loaded, then the tag to download the script can be added
- // in a non-destructive manner
- var script = document.createElement('script');
- script.src = filename;
- $doc.getElementsByTagName('head')[0].appendChild(script);
- } else {
- // if the doc has not yet loaded, go ahead and do a destructive
- // document.write since we want to immediately start the download.
- // Note that we cannot append an element to the doc if it is still loading
- // since this would cause problems in IE.
- $doc.write("<script src='" + filename + "'></scr" + "ipt>");
- }
- } else {
- // Just pass along the filename so tha a script tag can be installed in the
- // iframe to download it. Since we will be adding the iframe to the body,
- // we still need to wait for the body to load before going forward.
- setupWaitForBodyLoad(function() {
- installCode(filename, true);
- });
- }
+ installScript(filename);
+
}
__MODULE_FUNC__();
diff --git a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
index 140d90c..d5516bc 100644
--- a/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/SingleScriptLinker.java
@@ -50,7 +50,7 @@
ArtifactSet artifacts, boolean onePermutation)
throws UnableToCompleteException {
if (onePermutation) {
- processSelectionInformation(artifacts);
+ permutationsUtil.setupPermutationsMap(artifacts);
ArtifactSet toReturn = new ArtifactSet(artifacts);
toReturn.add(emitSelectionScript(logger, context, artifacts));
return toReturn;
diff --git a/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java b/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java
index 479d695..f3baebc 100644
--- a/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java
@@ -47,6 +47,9 @@
private static String loadComputeScriptBase() throws IOException {
StringBuffer code = new StringBuffer();
code.append("var base = \"\", $doc=document;\n");
+ code.append("__gwt_getMetaProperty = function(name) { "
+ + "var value = metaProps[name];"
+ + "return (value == null) ? null : value; }");
code.append(Utility.getFileFromClassPath(SelectionScriptLinker.COMPUTE_SCRIPT_BASE_JS));
code.append("computeScriptBase();\n");
return code.toString().replaceAll("__MODULE_NAME__", TEST_MODULE_NAME);