Have separate devmode.js files for each permutation if we are outputting
bootstrap in the primary fragment so we can put the properties info in
them and avoid putting it in the primary fragment.  Also, fail explicitly
when we encounter a script tag in the gwt.xml
rather than failing silently (xsiframe linker does not support this feature)

Review at http://gwt-code-reviews.appspot.com/1208802


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9422 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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
index 3ee5355..915d41d 100644
--- 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
@@ -18,12 +18,37 @@
 
 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.SelectionProperty;
 
+import java.util.Map.Entry;
+
 /**
  * A utility class to fill in the properties javascript in linker templates.
  */
 public class PropertiesUtil {
+  public static String addKnownPropertiesJs(TreeLogger logger,
+      CompilationResult result) {
+    StringBuffer propertiesJs = new StringBuffer();
+    
+    // Multiple values for a property can result in one permutation. For
+    // example, this permutation may be valid for safari and chrome.  However,
+    // we need to pick one, since the computePropValue() needs to return a
+    // single value.  It actually doesn't matter which one we pick. The fact
+    // that safari and chrome compiled into one permutation indicates that
+    // for this module, the behavior is the same.  Therefore, we just pick
+    // the first one.
+    for (Entry<SelectionProperty, String> entry :
+         result.getPropertyMap().first().entrySet()) {
+      propertiesJs.append("properties['");
+      propertiesJs.append(entry.getKey().getName());
+      propertiesJs.append("'] = '");
+      propertiesJs.append(entry.getValue());
+      propertiesJs.append("';");
+    }
+    return propertiesJs.toString();
+  }
+  
   public static StringBuffer addPropertiesJs(StringBuffer selectionScript,
       TreeLogger logger, LinkerContext context) {
     int startPos;
@@ -38,7 +63,7 @@
       }
     }
     return selectionScript;
-  }
+  } 
   
   private static String generatePropertyProvider(SelectionProperty prop) {
     StringBuffer toReturn = new StringBuffer();
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 f3dafb9..63c5a29 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
@@ -89,8 +89,6 @@
     }
   }
 
-  private String selectionScriptText = null;
-
   /**
    * This method is left in place for existing subclasses of
    * SelectionScriptLinker that have not been upgraded for the sharding API.
@@ -116,6 +114,7 @@
        */
       for (CompilationResult compilation : toReturn.find(CompilationResult.class)) {
         toReturn.addAll(doEmitCompilation(logger, context, compilation, artifacts));
+        maybeAddHostedModeFile(logger, context, toReturn, compilation);
       }
       return toReturn;
     } else {
@@ -126,7 +125,7 @@
         toReturn.add(art);
       }
       maybeOutputPropertyMap(logger, context, toReturn);
-      maybeAddHostedModeFile(logger, context, toReturn);
+      maybeAddHostedModeFile(logger, context, toReturn, null);
       return toReturn;
     }
   }
@@ -156,7 +155,7 @@
     }
 
     toReturn.addAll(emitSelectionInformation(result.getStrongName(), result));
-
+ 
     return toReturn;
   }
 
@@ -185,7 +184,8 @@
    * have been scanned using {@link #setupPermutationsMap(ArtifactSet)}.
    */
   protected String fillSelectionScriptTemplate(StringBuffer selectionScript,
-      TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws
+      TreeLogger logger, LinkerContext context, ArtifactSet artifacts,
+      CompilationResult result) throws
       UnableToCompleteException {
     String computeScriptBase;
     String processMetas;
@@ -231,30 +231,35 @@
       ArtifactSet artifacts) throws UnableToCompleteException {
     TextOutput to = new DefaultTextOutput(context.isOutputCompact());
     to.print(generatePrimaryFragmentString(
-        logger, context, result.getStrongName(), js[0], js.length, artifacts));
+        logger, context, result, js[0], js.length, artifacts));
     return Util.getBytes(to.toString());
   }
   
   protected String generatePrimaryFragmentString(TreeLogger logger,
-      LinkerContext context, String strongName, String js, int length,
+      LinkerContext context, CompilationResult result, String js, int length,
       ArtifactSet artifacts) 
   throws UnableToCompleteException {
     StringBuffer b = new StringBuffer();
+    String strongName = result == null ? "" : result.getStrongName();
     b.append(getModulePrefix(logger, context, strongName, length));
     b.append(js);
     b.append(getModuleSuffix(logger, context));
-    return wrapPrimaryFragment(logger, context, b.toString(), artifacts);
+    return wrapPrimaryFragment(logger, context, b.toString(), artifacts, result);
   }
   
   protected String generateSelectionScript(TreeLogger logger,
       LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
-    if (selectionScriptText != null) {
-      return selectionScriptText;
-    }
+    return generateSelectionScript(logger, context, artifacts, null);
+  }
+  
+  protected String generateSelectionScript(TreeLogger logger,
+        LinkerContext context, ArtifactSet artifacts, CompilationResult result)
+        throws UnableToCompleteException {
+    String selectionScriptText;
     StringBuffer buffer = readFileToStringBuffer(
         getSelectionScriptTemplate(logger,context), logger);
     selectionScriptText = fillSelectionScriptTemplate(
-        buffer, logger, context, artifacts);
+        buffer, logger, context, artifacts, result);
     selectionScriptText =
       context.optimizeJavaScript(logger, selectionScriptText);
     return selectionScriptText;
@@ -300,10 +305,10 @@
    * Add the hosted file to the artifact set.
    */
   protected void maybeAddHostedModeFile(TreeLogger logger, 
-      LinkerContext context, ArtifactSet artifacts)
+      LinkerContext context, ArtifactSet artifacts, CompilationResult result)
       throws UnableToCompleteException {
     String hostedFilename = getHostedFilename();
-    if ("".equals(hostedFilename)) {
+    if ("".equals(hostedFilename) || result != null) {
       return;
     }
     try {
@@ -359,7 +364,8 @@
   }
 
   protected String wrapPrimaryFragment(TreeLogger logger,
-      LinkerContext context, String script, ArtifactSet artifacts) {
+      LinkerContext context, String script, ArtifactSet artifacts,
+      CompilationResult result) {
     return script;
   }
 
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
deleted file mode 100644
index 393ce1a..0000000
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesNull.js
+++ /dev/null
@@ -1,4 +0,0 @@
-  __gwt_isKnownPropertyValue = function(propName, propValue) {
-    return false;
-  }
-  __MODULE_FUNC__.__computePropValue = null;
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesServerSide.js b/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesServerSide.js
new file mode 100644
index 0000000..86c6847
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/propertiesServerSide.js
@@ -0,0 +1,27 @@
+// Computes the value of a given property that was determined during the
+// compilation process.  This assumes that the bootstrap is embedded in an MD5
+// specific file, and during the compilation phase, the PermutationsUtil class
+// will take care of filling the __KNOWN_PROPERTIES__ section with the values
+// of the properties for this specific permutation.  Note that if a permutation
+// is valid for more than one property value (for example user.agent = chrome
+// or safari) then one of those values will be chosen at random.  Since the
+// different values resulted in the same MD5 file, we can assume that the
+// behavior of this module is the same for either value.
+
+  var properties = [];
+  
+  function computePropValue(propName) {
+    if (propName in properties) {
+      return properties[propName];
+    }
+    throw null;
+  }
+  
+  __KNOWN_PROPERTIES__
+  
+  // It's unclear how/if this function is applicable in the context of the
+  // properties being selected on the server.  Just return false for now.
+  __gwt_isKnownPropertyValue = function(propName, propValue) {
+    return false;
+  }
+  __MODULE_FUNC__.__computePropValue = computePropValue;
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 7a269c6..ee1447a 100644
--- a/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
+++ b/dev/core/src/com/google/gwt/core/linker/CrossSiteIframeLinker.java
@@ -20,12 +20,15 @@
 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.EmittedArtifact.Visibility;
 import com.google.gwt.core.ext.linker.LinkerOrder;
 import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+import com.google.gwt.core.ext.linker.ScriptReference;
 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.PropertiesUtil;
 import com.google.gwt.core.ext.linker.impl.ResourceInjectionUtil;
 import com.google.gwt.core.ext.linker.impl.SelectionScriptLinker;
 import com.google.gwt.dev.About;
@@ -54,8 +57,8 @@
   
   @Override
   protected String fillSelectionScriptTemplate(StringBuffer ss,
-      TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws
-      UnableToCompleteException {
+      TreeLogger logger, LinkerContext context, ArtifactSet artifacts,
+      CompilationResult result) throws UnableToCompleteException {
     
     // Must do installScript before installLocation and waitForBodyLoaded
     includeJs(ss, logger, getJsInstallScript(context), "__INSTALL_SCRIPT__");
@@ -69,9 +72,20 @@
     includeJs(ss, logger, getJsComputeScriptBase(context), "__COMPUTE_SCRIPT_BASE__");
     includeJs(ss, logger, getJsLoadExternalStylesheets(context), "__LOAD_STYLESHEETS__");
     
+    // This Linker does not support <script> tags in the gwt.xml
+    if (!artifacts.find(ScriptReference.class).isEmpty()) {
+      logger.log(TreeLogger.ERROR, "The " + getDescription() + 
+          " linker does not support <script> tags in the gwt.xml files");
+      throw new UnableToCompleteException();
+    }
+    
     ss = ResourceInjectionUtil.injectStylesheets(ss, artifacts);
     ss = permutationsUtil.addPermutationsJs(ss, logger, context);
-
+    
+    if (result != null) {
+      replaceAll(ss, "__KNOWN_PROPERTIES__",
+          PropertiesUtil.addKnownPropertiesJs(logger, result));
+    }
     replaceAll(ss, "__MODULE_FUNC__", context.getModuleFunctionName());
     replaceAll(ss, "__MODULE_NAME__", context.getModuleName());
     replaceAll(ss, "__HOSTED_FILENAME__", getHostedFilename());
@@ -200,22 +214,36 @@
 
   @Override
   protected void maybeAddHostedModeFile(TreeLogger logger, 
-      LinkerContext context, ArtifactSet artifacts)
+      LinkerContext context, ArtifactSet artifacts, CompilationResult result)
       throws UnableToCompleteException {
     String filename = getHostedFilename();
     if ("".equals(filename)) {
       return;
     }
     
+    // when we're including bootstrap in the primary fragment, we should be
+    // generating devmode files for each permutation. Otherwise, we generate it
+    // only in the final link stage.
+    boolean isSinglePermutation = (result != null);
+    if (isSinglePermutation != 
+      shouldIncludeBootstrapInPrimaryFragment(context)) {
+      return;
+    }
+    
     long lastModified = System.currentTimeMillis();
     StringBuffer buffer = readFileToStringBuffer(
         "com/google/gwt/core/ext/linker/impl/" + filename, logger); 
 
+    String outputFilename = filename;
+    if (result != null) {
+      outputFilename = result.getStrongName() + "." + outputFilename;
+    }
+
     String script = generatePrimaryFragmentString(
-        logger, context, "", buffer.toString(), 1, artifacts);
+        logger, context, result, buffer.toString(), 1, artifacts);
     
     EmittedArtifact devArtifact = 
-      emitString(logger, script, filename, lastModified);
+      emitString(logger, script, outputFilename, lastModified);
     artifacts.add(devArtifact);
   }
 
@@ -264,11 +292,12 @@
   
   @Override
   protected String wrapPrimaryFragment(TreeLogger logger,
-      LinkerContext context, String script, ArtifactSet artifacts) {
+      LinkerContext context, String script, ArtifactSet artifacts,
+      CompilationResult result) {
     StringBuffer out = new StringBuffer();
     if (shouldIncludeBootstrapInPrimaryFragment(context)) {
       try {
-        out.append(generateSelectionScript(logger, context, artifacts));
+        out.append(generateSelectionScript(logger, context, artifacts, result));
       } catch (UnableToCompleteException e) {
         logger.log(TreeLogger.ERROR, "Problem setting up selection script", e);
         e.printStackTrace();
diff --git a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
index be2552f..cc0fa30 100644
--- a/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
+++ b/user/test/com/google/gwt/dev/jjs/CrossSiteIframeRunAsyncMetrics.gwt.xml
@@ -13,7 +13,9 @@
 <!-- limitations under the License.                                         -->
 
 <module>
-  <inherits name="com.google.gwt.dev.jjs.RunAsyncMetricsIntegrationTest" />
+  <!-- We cannot inherit the RunAsyncMetricsIntegrationTest module because
+       it adds a script tag and the xsiframe linker does not support them -->
+  <inherits name="com.google.gwt.dev.jjs.CompilerSuite"/>
   <add-linker name="xsiframe" />
 
   <public path="public" />