Fixed a number of problems with the new startup, including:
- Got rid of gwt:base; module base URL now automagically computed from script include tag
- Resolved several module base URL problems
- Hosted mode now almost identical startup to web mode

Review by: jgw


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@576 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index fa7ab4b..df3aa23 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -481,7 +481,8 @@
 
     // Generate the call to tell the bootstrap code that we're ready to go.
     sb.append("\n");
-    sb.append("parent." + moduleFunction + ".onScriptLoad(window);\n");
+    sb.append("if ($wnd." + moduleFunction + ") $wnd." + moduleFunction
+        + ".onScriptLoad();\n");
     sb.append("--></script></body></html>\n");
 
     String s = sb.toString();
diff --git a/dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java b/dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java
index aa3709a..6b13ca6 100644
--- a/dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java
+++ b/dev/core/src/com/google/gwt/dev/util/SelectionScriptGenerator.java
@@ -56,10 +56,52 @@
 public class SelectionScriptGenerator {
 
   private static String cssInjector(String cssUrl) {
-    return "  if (!__gwt_stylesLoaded['" + cssUrl + "']) {\n"
-        + "    __gwt_stylesLoaded['" + cssUrl + "'] = true;\n"
-        + "    document.write('<link rel=\\\"stylesheet\\\" href=\\\"" + cssUrl
-        + "\\\">');\n" + "  }\n";
+    if (isRelativeURL(cssUrl)) {
+      return "  if (!__gwt_stylesLoaded['"
+          + cssUrl
+          + "']) {\n"
+          + "    __gwt_stylesLoaded['"
+          + cssUrl
+          + "'] = true;\n"
+          + "    document.write('<link rel=\\\"stylesheet\\\" href=\\\"'+base+'"
+          + cssUrl + "\\\">');\n" + "  }\n";
+    } else {
+      return "  if (!__gwt_stylesLoaded['" + cssUrl + "']) {\n"
+          + "    __gwt_stylesLoaded['" + cssUrl + "'] = true;\n"
+          + "    document.write('<link rel=\\\"stylesheet\\\" href=\\\""
+          + cssUrl + "\\\">');\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 {
+      URL testUrl = 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;
+  }
+
+  private static String literal(String lit) {
+    return "\"" + lit + "\"";
   }
 
   private static void replaceAll(StringBuffer buf, String search, String replace) {
@@ -71,10 +113,21 @@
   }
 
   private static String scriptInjector(String scriptUrl) {
-    return "  if (!__gwt_scriptsLoaded['" + scriptUrl + "']) {\n"
-        + "    __gwt_scriptsLoaded['" + scriptUrl + "'] = true;\n"
-        + "    document.write('<script language=\\\"javascript\\\" src=\\\""
-        + scriptUrl + "\\\"></script>');\n" + "  }\n";
+    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";
+    }
   }
 
   private final String moduleFunction;
@@ -88,9 +141,7 @@
    * same generated code for the same set of compilations.
    */
   private final Map propertyValuesSetByStrongName = new TreeMap();
-
   private final Scripts scripts;
-
   private final Styles styles;
 
   /**
@@ -208,12 +259,24 @@
       for (Iterator iterator = propValuesSet.iterator(); iterator.hasNext();) {
         String[] propValues = (String[]) iterator.next();
 
-        pw.print("      O([");
+        pw.print("      unflattenKeylistIntoAnswers([");
+        boolean firstPrint = true;
         for (int i = 0; i < orderedProps.length; i++) {
-          if (i > 0) {
-            pw.print(",");
+          Property prop = orderedProps[i];
+          String activeValue = prop.getActiveValue();
+          if (activeValue == null) {
+            // This is a call to a property provider function; we need it to
+            // select the script.
+            //
+            if (!firstPrint) {
+              pw.print(",");
+            }
+            firstPrint = false;
+            pw.print(literal(propValues[i]));
+          } else {
+            // This property was explicitly set at compile-time; we do not need
+            // it.
           }
-          pw.print(literal(propValues[i]));
         }
         pw.print("]");
         pw.print(",");
@@ -253,56 +316,30 @@
         }
         pw.println();
         pw.println("};");
-
-        // Emit a wrapper that verifies that the value is valid.
-        // It is this function that is called directly to get the propery.
-        pw.println();
-        pw.println("props['" + prop.getName() + "'] = function() {");
-        pw.println("  var v = providers['" + prop.getName() + "']();");
-        pw.println("  var ok = values['" + prop.getName() + "'];");
-        // Make sure this is an allowed value; if so, return.
-        pw.println("  if (v in ok)");
-        pw.println("    return v;");
-        // Not an allowed value, so build a nice message and call the handler.
-        pw.println("  var a = new Array(" + knownValues.length + ");");
-        pw.println("  for (var k in ok)");
-        pw.println("    a[ok[k]] = k;");
-        pw.print("  " + moduleFunction + ".onBadProperty(");
-        pw.print(literal(prop.getName()));
-        pw.println(", a, v);");
-        pw.println("  if (arguments.length > 0) throw null; else return null;");
-        pw.println("};");
-        pw.println();
       }
     }
   }
 
   private void genPropValues(PrintWriter pw) {
-    pw.println("      var F;");
-    pw.print("      var I = [");
     for (int i = 0; i < orderedProps.length; i++) {
-      if (i > 0) {
-        pw.print(", ");
-      }
-
       Property prop = orderedProps[i];
       String activeValue = prop.getActiveValue();
       if (activeValue == null) {
-        // This is a call to a property provider function.
+        // This is a call to a property provider function; we need it to
+        // select the script.
         //
         PropertyProvider provider = prop.getProvider();
         assert (provider != null) : "expecting a default property provider to have been set";
         // When we call the provider, we supply a bogus argument to indicate
         // that it should throw an exception if the property is a bad value.
         // The absence of arguments (as in hosted mode) tells it to return null.
-        pw.print("(F=props['" + prop.getName() + "'],F(1))");
+        pw.print("[");
+        pw.print("computePropValue('" + prop.getName() + "')");
+        pw.print("]");
       } else {
-        // This property was explicitly set at compile-time.
-        //
-        pw.print(literal(activeValue));
+        // This property was explicitly set at compile-time; we do not need it.
       }
     }
-    pw.println("];");
   }
 
   /**
@@ -316,8 +353,8 @@
     replaceAll(buf, "__MODULE_FUNC__", moduleFunction);
     replaceAll(buf, "__MODULE_NAME__", moduleName);
 
-    // Remove hosted mode only stuff
     if (orderedProps != null) {
+      // Remove shell servlet only stuff (hosted mode support)
       int startPos = buf.indexOf("// __SHELL_SERVLET_ONLY_BEGIN__");
       int endPos = buf.indexOf("// __SHELL_SERVLET_ONLY_END__");
       buf.delete(startPos, endPos);
@@ -361,14 +398,10 @@
         // Web mode or hosted mode.
         if (orderedProps.length > 0) {
           pw.println();
-          genPropValues(pw);
-          pw.println();
           genAnswers(pw);
           pw.println();
           pw.print("      strongName = answers");
-          for (int i = 0; i < orderedProps.length; i++) {
-            pw.print("[I[" + i + "]]");
-          }
+          genPropValues(pw);
         } else {
           // Rare case of no properties; happens if you inherit from Core
           // alone.
@@ -393,35 +426,4 @@
     mainPw.print(buf.toString());
   }
 
-  /**
-   * 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 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 {
-      URL testUrl = 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;
-  }
-
-  private String literal(String lit) {
-    return "\"" + lit + "\"";
-  }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js b/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js
index e10caba..c9cd47e 100644
--- a/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js
+++ b/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js
@@ -18,27 +18,33 @@
   // ---------------- INTERNAL GLOBALS ----------------
   
   // Cache symbols locally for good obfuscation
-  var wnd = window;
-  var external = wnd.external;
+  var wnd = window
+  ,doc = document
+  ,external = wnd.external
   
   // These two variables gate calling gwtOnLoad; both must be true to start
-  var scriptsDone, loadDone;
+  ,scriptsDone, loadDone
+  
+  // If non-empty, an alternate base url for this module
+  ,base = ''
   
   // A map of properties that were declared in meta tags
-  var __gwt_metaProps = {};
+  ,metaProps = {}
   
-  // A map of module rebasings
-  var __gwt_base = {};
+  // Maps property names onto sets of legal values for that property.
+  ,values = []
   
-  // These variables contain deferred-binding properties, values, and
-  // providers.
-  //
-  var props = [];
-  var values = [];
-  var providers = [];
+  // Maps property names onto a function to compute that property.
+  ,providers = []
   
-  // Property answers go here
-  var answers = [];
+  // A multi-tier lookup map that uses actual property values to quickly find
+  // the strong name of the cache.js file to load.
+  ,answers = []
+
+  // Error functions.  Default unset in compiled mode, may be set by meta props.
+  ,onLoadErrorFunc, propertyErrorFunc
+  
+  ; // end of global vars
 
   // ------------------ TRUE GLOBALS ------------------
 
@@ -50,31 +56,9 @@
 
   // --------------- INTERNAL FUNCTIONS ---------------
 
-  // The default module load error function; may be overwritten via meta props
-  //
-  function __gwt_onLoadError() {
-    alert('Failed to load module __MODULE_NAME__' +
-      '".\nPlease see the log in the development shell for details.');
-  }
-
-  // The default bad property error function; may be overwritten via meta props
-  //
-  function __gwt_onPropertyError(propName, allowedValues, badValue) {
-    var msg = 'While attempting to load module __MODULE_NAME__, property \"'
-      + propName;
-    if (badValue != null) {
-      msg += '\" was set to the unexpected value \"' + badValue + '\"';
-    } else {
-      msg += '\" was not specified';
-    }
-    msg += 'Allowed values: ' + allowedValues;
-    alert(msg);
-  }
-  
-
   function isHostedMode() {
     return (external && external.gwtOnLoad &&
-        (document.location.href.indexOf('gwt.hybrid') == -1));
+        (wnd.location.search.indexOf('gwt.hybrid') == -1));
   }
   
 
@@ -83,66 +67,80 @@
   //
   function maybeStartModule() {
     if (scriptsDone && loadDone) {
-      var iframe = document.getElementById('__MODULE_NAME__');
+      var iframe = doc.getElementById('__MODULE_NAME__');
       var frameWnd = iframe.contentWindow;
       // copy the init handlers function into the iframe
       frameWnd.__gwt_initHandlers = __MODULE_FUNC__.__gwt_initHandlers;
+      // inject hosted mode property evaluation function
+      if (isHostedMode()) {
+        frameWnd.__gwt_getProperty = function(name) {
+          return computePropValue(name);
+        };
+      }
       // remove this whole function from the global namespace to allow GC
       __MODULE_FUNC__ = null;
-      iframe.contentWindow.gwtOnLoad(__gwt_onLoadError, '__MODULE_NAME__');
+      frameWnd.gwtOnLoad(onLoadErrorFunc, '__MODULE_NAME__');
+    }
+  }
+  
+  // Determine our own script's URL via magic :)
+  //
+  function computeScriptBase() {
+    doc.write('<script id="__gwt_marker___MODULE_NAME__"></script>');
+    var markerScript = doc.getElementById("__gwt_marker___MODULE_NAME__");
+    // the previous script element is this script
+    var content = markerScript.previousSibling.src;
+    if (content) {
+      var eq = content.lastIndexOf('/');
+      if (eq >= 0) {
+        base = content.substring(0, eq + 1);
+      }
+      // remove the marker element
+      markerScript.parentNode.removeChild(markerScript);
     }
   }
   
   // Called to slurp up all <meta> tags:
-  // gwt:property, gwt:base, gwt:onPropertyErrorFn, gwt:onLoadErrorFn
+  // gwt:property, gwt:onPropertyErrorFn, gwt:onLoadErrorFn
   //
   function processMetas() {
     var metas = document.getElementsByTagName('meta');
-  
     for (var i = 0, n = metas.length; i < n; ++i) {
-      var meta = metas[i];
-      var name = meta.getAttribute('name');
+      var meta = metas[i], name = meta.getAttribute('name'), content;
   
       if (name) {
         if (name == 'gwt:property') {
-          var content = meta.getAttribute('content');
+          content = meta.getAttribute('content');
           if (content) {
-            var name = content, value = '';
-            var eq = content.indexOf('=');
-            if (eq != -1) {
+            var value, eq = content.indexOf('=');
+            if (eq >= 0) {
               name = content.substring(0, eq);
               value = content.substring(eq+1);
+            } else {
+              name = content;
+              value = '';
             }
-            __gwt_metaProps[name] = value;
+            metaProps[content] = value;
           }
         } else if (name == 'gwt:onPropertyErrorFn') {
-          var content = meta.getAttribute('content');
+          content = meta.getAttribute('content');
           if (content) {
             try {
-              __gwt_onPropertyError = eval(content);
+              propertyErrorFunc = eval(content);
             } catch (e) {
               alert('Bad handler \"' + content +
                 '\" for \"gwt:onPropertyErrorFn\"');
             }
           }
         } else if (name == 'gwt:onLoadErrorFn') {
-          var content = meta.getAttribute('content');
+          content = meta.getAttribute('content');
           if (content) {
             try {
-              __gwt_onLoadError = eval(content);
+              onLoadErrorFunc = eval(content);
             } catch (e) {
               alert('Bad handler \"' + content + '\" for \"gwt:onLoadErrorFn\"');
             }
           }
-        } else if (name == 'gwt:base') {
-          var content = meta.getAttribute('content');
-          var eqPos = content.lastIndexOf('=');
-          if (eqPos == -1) {
-            continue;
-          }
-          var moduleBase = content.substring(0, eqPos);
-          var moduleName = content.substring(eqPos + 1);
-          __gwt_base[moduleName] = moduleBase;
         }
       }
     }
@@ -163,23 +161,41 @@
    * Returns a meta property value, if any.  Used by DefaultPropertyProvider.
    */
   function __gwt_getMetaProperty(name) {
-    var value = __gwt_metaProps[name];
+    var value = metaProps[name];
     return (value == null) ? null : value;
   }
 
-  // Deferred-binding mapper function.
+  // 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 O(a,v) {
+  function unflattenKeylistIntoAnswers(propValArray, value) {
     var answer = answers;
-    var i = -1;
-    var n = a.length - 1;
-    while (++i < n) {
-      if (!(a[i] in answer)) {
-        answer[a[i]] = [];
-      }
-      answer = answer[a[i]];
+    for (var i = 0, n = propValArray.length - 1; i < n; ++i) {
+      // lazy initialize an empty object for the current key if needed
+      answer = answer[propValArray[i]] || (answer[propValArray[i]] = []);
     }
-    answer[a[n]] = v;
+    // set the final one to the value
+    answer[propValArray[n]] = value;
+  }
+  
+  // Computes the value of a given property.  propName must be a valid property
+  // name. Used by the generated PERMUTATIONS code.
+  //
+  function computePropValue(propName) {
+    var value = providers[propName](), allowedValuesMap = values[propName];
+    if (value in allowedValuesMap) {
+      return value;
+    }
+    var allowedValuesList = [];
+    for (var k in allowedValuesMap) {
+      allowedValuesList[allowedValuesMap[k]] = k;
+    }
+    if (propertyErrorFunc) {
+      propertyErrorFunc(propName, allowedValuesList, value);
+    }
+    throw null;
   }
   
   // --------------- PROPERTY PROVIDERS ---------------
@@ -204,55 +220,47 @@
     loadDone = true;
     maybeStartModule();
   }
-  
+
   // --------------- STRAIGHT-LINE CODE ---------------
 
+  // do it early for compile/browse rebasing
+  computeScriptBase();
+  
 // __SHELL_SERVLET_ONLY_BEGIN__
   // Force shell servlet to serve compiled output for web mode
   if (!isHostedMode()) {
-    document.write('<script src="__MODULE_NAME__.nocache.js?compiled"></script>');
+    doc.write('<script src="' + base + '__MODULE_NAME__.nocache.js?compiled"></script>');
     return;
   }
+
+  // Default shell servlet load error function
+  //
+  onLoadErrorFunc = function() {
+    alert('Failed to load module __MODULE_NAME__' +
+      '".\nPlease see the log in the development shell for details.');
+  };
+
+  // Default shell servlet property error function
+  //
+  propertyErrorFunc = function(propName, allowedValues, badValue) {
+    var msg = 'While attempting to load module __MODULE_NAME__, property \"'
+      + propName;
+    if (badValue != null) {
+      msg += '\" was set to the unexpected value \"' + badValue + '\"';
+    } else {
+      msg += '\" was not specified';
+    }
+    msg += 'Allowed values: ' + allowedValues;
+    alert(msg);
+  };
+
 // __SHELL_SERVLET_ONLY_END__
 
   processMetas();
 
   var strongName;
   if (isHostedMode()) {
-    // In hosted mode, inject the script frame directly.
-    var iframe = document.createElement('iframe');
-    iframe.id = '__MODULE_NAME__';
-    iframe.style.width = '0px';
-    iframe.style.height = '0px';
-    iframe.style.border = '0px';
-    document.body.appendChild(iframe);
-
-    iframe.src = 'blank.html';
-    iframe.onload = function() {
-      var frameWnd = iframe.contentWindow;
-      frameWnd.$wnd = wnd;
-      frameWnd.$doc = wnd.document;
-
-      // inject hosted mode property evaluation function
-      frameWnd.__gwt_getProperty = function(name) {
-        return providers[name]();
-      };
-
-      // inject gwtOnLoad
-      frameWnd.gwtOnLoad = function(errFn, modName) {
-        if (!external.gwtOnLoad(frameWnd, modName)) {
-          errFn(modName);
-        }
-      }
-
-      // Hook the iframe's onunload, so that the hosted browser has a chance
-      // to clean up its ModuleSpaces.
-      frameWnd.onunload = function() {
-        external.gwtOnLoad(frameWnd, null);
-      };
-
-      __MODULE_FUNC__.onScriptLoad();
-    };
+    strongName = "hosted.html?__MODULE_FUNC__";
   } else {
     try {
 // __PERMUTATIONS_BEGIN__
@@ -263,19 +271,14 @@
       return;
     }  
 
-	  // TODO: do we still need this query stuff?
-	  var query = location.search;
-	  query = query.substring(0, query.indexOf('&'));
-
-	  var base = __gwt_base['__MODULE_NAME__'];
-	  var newUrl = (base ? base + '/' : '') + strongName + '.cache.html' + query;
-	  document.write('<iframe id="__MODULE_NAME__" style="width:0;height:0;border:0" src="' + newUrl + '"></iframe>');
+    strongName += '.cache.html';
   }
 
+  doc.write('<iframe id="__MODULE_NAME__" style="width:0;height:0;border:0" src="' + base + strongName + '"></iframe>');
 // __MODULE_DEPS_BEGIN__
   // Module dependencies, such as scripts and css
 // __MODULE_DEPS_END__
-  document.write('<script>__MODULE_FUNC__.onInjectionDone(\'__MODULE_NAME__\')</script>');
+  doc.write('<script>__MODULE_FUNC__.onInjectionDone(\'__MODULE_NAME__\')</script>');
 }
 
 // Called from compiled code to hook the window's resize & load events (the
@@ -290,15 +293,18 @@
 // 3) This function will be copied directly into the script frame window!
 //
 __MODULE_FUNC__.__gwt_initHandlers = function(resize, beforeunload, unload) {
-  var wnd = window;
-  var oldOnResize = wnd.onresize;
+  var wnd = window
+  , oldOnResize = wnd.onresize
+  , oldOnBeforeUnload = wnd.onbeforeunload
+  , oldOnUnload = wnd.onunload
+  ;
+
   wnd.onresize = function() {
    resize();
    if (oldOnResize)
      oldOnResize();
   };
   
-  var oldOnBeforeUnload = wnd.onbeforeunload;
   wnd.onbeforeunload = function() {
    var ret = beforeunload();
   
@@ -311,7 +317,6 @@
    return oldRet;
   };
   
-  var oldOnUnload = wnd.onunload;
   wnd.onunload = function() {
    unload();
    if (oldOnUnload)
diff --git a/user/src/com/google/gwt/core/public/blank.html b/user/src/com/google/gwt/core/public/blank.html
deleted file mode 100644
index 8c7fe21..0000000
--- a/user/src/com/google/gwt/core/public/blank.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<html>
-</html>
\ No newline at end of file
diff --git a/user/src/com/google/gwt/core/public/gwt.js b/user/src/com/google/gwt/core/public/gwt.js
index a5468a4..03dc568 100644
--- a/user/src/com/google/gwt/core/public/gwt.js
+++ b/user/src/com/google/gwt/core/public/gwt.js
@@ -12,29 +12,21 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 // 
-// This startup script should be included in host pages either just after
-// <body> or inside the <head> after module <meta> tags.
+// This startup script is for legacy support and is now deprecated. Instead of
+// using this script, just include the selection script directly.
 //
 (function(){
 	var metas = document.getElementsByTagName("meta");
-
 	for (var i = 0, n = metas.length; i < n; ++i) {
-		var meta = metas[i];
-		var name = meta.getAttribute("name");
-		if (name) {
-			if (name == "gwt:module") {
-				var moduleName = meta.getAttribute("content");
-				if (moduleName) {
-					var eqPos = moduleName.lastIndexOf("=");
-					if (eqPos != -1) {
-						var base = moduleName.substring(0, eqPos);
-						moduleName = moduleName.substring(eqPos + 1);
-						window.__gwt_base = { };
-						window.__gwt_base[moduleName] = base;
-						moduleName = base + '/' + moduleName;
-					}
-					document.write('<script src="' + moduleName + '.nocache.js"></script>');
+		var meta = metas[i], name = meta.getAttribute("name");
+		if (name == "gwt:module") {
+			var content = meta.getAttribute("content");
+			if (content) {
+				var eqPos = content.lastIndexOf("=");
+				if (eqPos != -1) {
+					content = content.substring(0, eqPos) + '/' + content.substring(eqPos + 1);
 				}
+				document.write('<script src="' + content + '.nocache.js"></script>');
 			}
 		}
 	}
diff --git a/user/src/com/google/gwt/core/public/hosted.html b/user/src/com/google/gwt/core/public/hosted.html
new file mode 100644
index 0000000..538cff2
--- /dev/null
+++ b/user/src/com/google/gwt/core/public/hosted.html
@@ -0,0 +1,25 @@
+<html>
+<head><script>
+var $wnd = parent;
+var $doc = $wnd.document;
+var $moduleName;
+</script></head>
+<body>
+<font face='arial' size='-1'>This html file is for hosted mode support.</font>
+<script><!--
+function gwtOnLoad(errFn, modName){
+  $moduleName = modName;
+  if (!external.gwtOnLoad(window, modName)) {
+    if (errFn) {
+      errFn(modName);
+    }
+  }
+}
+
+window.onunload = function() {
+  external.gwtOnLoad(window, null);
+};
+
+var query = window.location.search.substr(1);
+if (query && $wnd[query]) $wnd[query].onScriptLoad();
+--></script></body></html>