Fix for issue #1022; makes GWT.getModuleBaseURL() correct in the mashup case. Some cases require crazy magic with setting and retrieving the URL of an image. Also adds a new GWT.getHostPageBaseURL().
Found by: me
Suggested by: jason.essington
Review by: bruce, jgw, mmendez
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1052 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 efa9e75..0d63765 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -454,7 +454,7 @@
out.newlineOpt();
out.print("var $doc = $wnd.document;");
out.newlineOpt();
- out.print("var $moduleName = \"" + moduleName + "\";");
+ out.print("var $moduleName, $moduleBase;");
out.newlineOpt();
out.print("</script></head>");
out.newlineOpt();
@@ -500,11 +500,11 @@
// Setup the well-known variables.
//
- out.print("var $wnd = parent;");
+ out.print("var $wnd = window;");
out.newlineOpt();
out.print("var $doc = $wnd.document;");
out.newlineOpt();
- out.print("var $moduleName = \"" + moduleName + "\";");
+ out.print("var $moduleName, $moduleBase;");
out.newlineOpt();
return out.toString();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 66d473c..eff99d2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -1273,7 +1273,9 @@
private void generateGwtOnLoad(List entryFuncs, JsStatements globalStmts) {
/**
* <pre>
- * function gwtOnLoad(errFn, modName){
+ * function gwtOnLoad(errFn, modName, modBase){
+ * $moduleName = modName;
+ * $moduleBase = modBase;
* if (errFn) {
* try {
* init();
@@ -1297,8 +1299,17 @@
JsParameters params = gwtOnLoad.getParameters();
JsName errFn = fnScope.declareName("errFn");
JsName modName = fnScope.declareName("modName");
+ JsName modBase = fnScope.declareName("modBase");
params.add(new JsParameter(errFn));
params.add(new JsParameter(modName));
+ params.add(new JsParameter(modBase));
+ JsExpression asg = createAssignment(
+ topScope.findExistingUnobfuscatableName("$moduleName").makeRef(),
+ modName.makeRef());
+ body.getStatements().add(asg.makeStmt());
+ asg = createAssignment(topScope.findExistingUnobfuscatableName(
+ "$moduleBase").makeRef(), modBase.makeRef());
+ body.getStatements().add(asg.makeStmt());
JsIf jsIf = new JsIf();
body.getStatements().add(jsIf);
jsIf.setIfExpr(errFn.makeRef());
@@ -1493,7 +1504,8 @@
}
JReferenceType enclosingType = x.getEnclosingType();
- if (!typeOracle.checkClinit(currentMethod.getEnclosingType(), enclosingType)) {
+ if (!typeOracle.checkClinit(currentMethod.getEnclosingType(),
+ enclosingType)) {
return null;
}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
index 421f3c9..05129b2 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
@@ -46,7 +46,7 @@
"Error", "Function", "Global", "Image", "Math", "Number", "Object",
"RegExp", "String", "VBArray", "window", "document", "event",
"arguments", "call", "toString", "$wnd", "$doc", "$moduleName",
- "debugger", "undefined"};
+ "$moduleBase", "debugger", "undefined"};
for (int i = 0; i < commonBuiltins.length; i++) {
String ident = commonBuiltins[i];
diff --git a/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java b/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java
index 10d0a77..9947b5e 100644
--- a/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/HostedModeSourceOracle.java
@@ -21,6 +21,9 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.jdt.StandardSourceOracle;
import com.google.gwt.dev.jdt.StaticCompilationUnitProvider;
+import com.google.gwt.util.tools.Utility;
+
+import java.io.IOException;
/**
* Does a little extra magic to handle hosted mode JSNI and
@@ -28,102 +31,10 @@
*/
public class HostedModeSourceOracle extends StandardSourceOracle {
- private final CompilationUnitProvider cuMeta = new StaticCompilationUnitProvider(
- "com.google.gwt.core.client", "GWT", null) {
- public char[] getSource() {
- StringBuffer sb = new StringBuffer();
- sb.append("package com.google.gwt.core.client;\n");
- sb.append("public final class GWT {\n");
-
- // UncaughtExceptionHandler
- //
- sb.append(" public interface UncaughtExceptionHandler {\n");
- sb.append(" void onUncaughtException(Throwable e);\n");
- sb.append(" }\n");
-
- sb.append(" private static String sModuleBaseURL = null;\n");
-
- // Hosted mode default to logging
- //
- sb.append(" private static UncaughtExceptionHandler sUncaughtExceptionHandler = \n");
- sb.append(" new UncaughtExceptionHandler() {\n");
- sb.append(" public void onUncaughtException(Throwable e) {\n");
- sb.append(" log(\"Uncaught exception escaped\", e);\n");
- sb.append(" }\n");
- sb.append(" };\n");
-
- // Implement getUncaughtExceptionHandler()
- //
- sb.append(" public static UncaughtExceptionHandler getUncaughtExceptionHandler() {\n");
- sb.append(" return sUncaughtExceptionHandler;\n");
- sb.append(" }\n");
-
- // Implement setUncaughtExceptionHandler()
- //
- sb.append(" public static void setUncaughtExceptionHandler(\n");
- sb.append(" UncaughtExceptionHandler handler) {\n");
- sb.append(" sUncaughtExceptionHandler = handler;\n");
- sb.append(" }\n");
-
- // Proxy create().
- //
- sb.append(" public static Object create(Class classLiteral) {\n");
- sb.append(" return ");
- sb.append(ShellGWT.class.getName());
- sb.append(".create(classLiteral);\n");
- sb.append(" }\n");
-
- // Proxy getTypeName().
- //
- sb.append(" public static String getTypeName(Object o) {\n");
- sb.append(" return ");
- sb.append(ShellGWT.class.getName());
- sb.append(".getTypeName(o);");
- sb.append(" }\n");
-
- // Hard-code isScript() to false.
- //
- sb.append(" public static boolean isScript() {\n");
- sb.append(" return false;");
- sb.append(" }\n");
-
- // Actually, we don't need to proxy getModuleName().
- // It's hard-coded.
- //
- sb.append(" public static String getModuleName() {\n");
- sb.append(" return \"");
- sb.append(moduleName);
- sb.append("\";\n");
- sb.append(" }\n");
-
- // Proxy getModuleBaseURL() to the Impl class.
- //
- sb.append(" public static String getModuleBaseURL() {\n");
- sb.append(" if (sModuleBaseURL == null) {\n");
- sb.append(" sModuleBaseURL = Impl.getModuleBaseURL();\n");
- sb.append(" }\n");
- sb.append(" return sModuleBaseURL;\n");
- sb.append(" }\n");
-
- // Proxy log().
- //
- sb.append(" public static void log(String message, Throwable e) {\n ");
- sb.append(ShellGWT.class.getName());
- sb.append(".log(message, e);\n");
- sb.append(" }\n");
-
- sb.append("}\n");
- return sb.toString().toCharArray();
- }
- };
-
private final JsniInjector injector;
- private final String moduleName;
-
- public HostedModeSourceOracle(TypeOracle typeOracle, String moduleName) {
+ public HostedModeSourceOracle(TypeOracle typeOracle) {
super(typeOracle);
- this.moduleName = moduleName;
this.injector = new JsniInjector(typeOracle);
}
@@ -131,25 +42,29 @@
String typeName, CompilationUnitProvider existing)
throws UnableToCompleteException {
- // MAGIC: The implementation of GWT.create() is handled intrinsically by
- // the compiler in web mode, so its on-disk definition is totally empty so
- // as to be trivially compilable. In hosted mode, GWT.create() is
- // actually a real call, so we patch different source for that class in
- // hosted mode only.
- //
- // MAGIC: The implementation of GWT.getTypeSignature() is handled
- // differently in web mode versus hosted mode. The on-disk version is
- // the web mode version, so here we substitute the hosted mode version.
- //
+ /*
+ * MAGIC: The implementation of GWT can be very different between hosted
+ * mode and web mode. The compiler has special knowledge of GWT for web
+ * mode. The source for hosted mode is in GWT.java-hosted.
+ */
if (typeName.equals("com.google.gwt.core.client.GWT")) {
- return cuMeta;
+ try {
+ String source = Utility.getFileFromClassPath("com/google/gwt/core/client/GWT.java-hosted");
+ return new StaticCompilationUnitProvider("com.google.gwt.core.client",
+ "GWT", source.toCharArray());
+ } catch (IOException e) {
+ logger.log(
+ TreeLogger.ERROR,
+ "Unable to load 'com/google/gwt/core/client/GWT.java-hosted' from class path; is your installation corrupt?",
+ e);
+ throw new UnableToCompleteException();
+ }
}
// Otherwise, it's a regular translatable type, but we want to make sure
// its JSNI stuff, if any, gets handled.
//
CompilationUnitProvider jsnified = injector.inject(logger, existing);
-
return jsnified;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
index 111efca..a107f79 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
@@ -89,8 +89,7 @@
// Create a host for the hosted mode compiler.
// We add compilation units to it as deferred binding generators write them.
//
- SourceOracle srcOracle = new HostedModeSourceOracle(typeOracle,
- module.getName());
+ SourceOracle srcOracle = new HostedModeSourceOracle(typeOracle);
// Create or find the compiler to be used by the compiling class loader.
//
diff --git a/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js b/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js
index fca59b1..d7bc6f2 100644
--- a/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js
+++ b/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate-xs.js
@@ -79,7 +79,7 @@
function maybeStartModule() {
// TODO: it may not be necessary to check gwtOnLoad here.
if (gwtOnLoad && bodyDone) {
- gwtOnLoad(onLoadErrorFunc, '__MODULE_NAME__');
+ gwtOnLoad(onLoadErrorFunc, '__MODULE_NAME__', base);
}
}
@@ -97,17 +97,32 @@
thisScript = markerScript.previousSibling;
}
- if (thisScript) {
+ function getDirectoryOfFile(path) {
+ var eq = path.lastIndexOf('/');
+ return (eq >= 0) ? path.substring(0, eq + 1) : '';
+ };
+
+ if (thisScript && thisScript.src) {
// Compute our base url
- var content = thisScript.src;
- if (content) {
- var eq = content.lastIndexOf('/');
- if (eq >= 0) {
- base = content.substring(0, eq + 1);
- }
- }
+ base = getDirectoryOfFile(thisScript.src);
}
+ // Make the base URL absolute
+ if (base == '') {
+ // Trivial case; the base must be the same as the document location
+ base = getDirectoryOfFile($doc.location.href);
+ } else if ((base.match(/^\w+:\/\//))) {
+ // If the URL is obviously absolute, do nothing.
+ } else {
+ // Probably a relative URL; use magic to make the browser absolutify it.
+ // I wish there were a better way to do this, but this seems the only
+ // sure way! (A side benefit is it preloads clear.cache.gif)
+ // Note: this trick is harmless if the URL was really already absolute.
+ var img = $doc.createElement("img");
+ img.src = base + 'clear.cache.gif';
+ base = getDirectoryOfFile(img.src);
+ }
+
if (markerScript) {
// remove the marker element
markerScript.parentNode.removeChild(markerScript);
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 637487f..a2eed87 100644
--- a/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js
+++ b/dev/core/src/com/google/gwt/dev/util/SelectionScriptTemplate.js
@@ -90,7 +90,7 @@
}
// remove this whole function from the global namespace to allow GC
__MODULE_FUNC__ = null;
- frameWnd.gwtOnLoad(onLoadErrorFunc, '__MODULE_NAME__');
+ frameWnd.gwtOnLoad(onLoadErrorFunc, '__MODULE_NAME__', base);
}
}
@@ -114,15 +114,30 @@
}
}
- if (thisScript) {
+ function getDirectoryOfFile(path) {
+ var eq = path.lastIndexOf('/');
+ return (eq >= 0) ? path.substring(0, eq + 1) : '';
+ };
+
+ if (thisScript && thisScript.src) {
// Compute our base url
- var content = thisScript.src;
- if (content) {
- var eq = content.lastIndexOf('/');
- if (eq >= 0) {
- base = content.substring(0, eq + 1);
- }
- }
+ base = getDirectoryOfFile(thisScript.src);
+ }
+
+ // Make the base URL absolute
+ if (base == '') {
+ // Trivial case; the base must be the same as the document location
+ base = getDirectoryOfFile($doc.location.href);
+ } else if ((base.match(/^\w+:\/\//))) {
+ // If the URL is obviously absolute, do nothing.
+ } else {
+ // Probably a relative URL; use magic to make the browser absolutify it.
+ // I wish there were a better way to do this, but this seems the only
+ // sure way! (A side benefit is it preloads clear.cache.gif)
+ // Note: this trick is harmless if the URL was really already absolute.
+ var img = $doc.createElement("img");
+ img.src = base + 'clear.cache.gif';
+ base = getDirectoryOfFile(img.src);
}
if (markerScript) {
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java
index 2fa0075..ecc3739 100644
--- a/user/src/com/google/gwt/core/client/GWT.java
+++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 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
@@ -21,6 +21,10 @@
* deferred binding.
*/
public final class GWT {
+ /*
+ * This is the web mode version of this class. Because it's so special,
+ * there's also a hosted mode version. See GWT.java-hosted.
+ */
/**
* This interface is used to catch exceptions at the "top level" just before
@@ -35,11 +39,7 @@
void onUncaughtException(Throwable e);
}
- // cache of the module base URL
- private static String sModuleBaseURL = null;
-
// web mode default is to let the exception go
- // hosted mode default is to log the exception to the log window
private static UncaughtExceptionHandler sUncaughtExceptionHandler = null;
/**
@@ -58,10 +58,6 @@
*/
public static Object create(Class classLiteral) {
/*
- * In hosted mode, this whole class definition is replaced at runtime with
- * an implementation defined by the hosting environment. Maintainers: see
- * HostedModeSourceOracle.cuMeta}.
- *
* In web mode, the compiler directly replaces calls to this method with a
* new Object() type expression of the correct rebound type.
*/
@@ -70,6 +66,18 @@
}
/**
+ * Gets the URL prefix of the hosting page, useful for prepending to relative
+ * paths of resources which may be relative to the host page. Typically, you
+ * should use {@link #getModuleBaseURL()} unless you have a specific reason to
+ * load a resource relative to the host page.
+ *
+ * @return if non-empty, the base URL is guaranteed to end with a slash
+ */
+ public static String getHostPageBaseURL() {
+ return Impl.getHostPageBaseURL();
+ }
+
+ /**
* Gets the URL prefix of the module which should be prepended to URLs that
* are intended to be module-relative, such as RPC entry points and files in
* the module's public path.
@@ -77,18 +85,15 @@
* @return if non-empty, the base URL is guaranteed to end with a slash
*/
public static String getModuleBaseURL() {
- if (sModuleBaseURL == null) {
- sModuleBaseURL = Impl.getModuleBaseURL();
- }
- return sModuleBaseURL;
+ return Impl.getModuleBaseURL();
}
/**
* Gets the name of the running module.
*/
- public static native String getModuleName() /*-{
- return $moduleName;
- }-*/;
+ public static String getModuleName() {
+ return Impl.getModuleName();
+ }
/**
* Gets the class name of the specified object, as would be returned by
diff --git a/user/src/com/google/gwt/core/client/GWT.java-hosted b/user/src/com/google/gwt/core/client/GWT.java-hosted
new file mode 100644
index 0000000..1b4c22d
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/GWT.java-hosted
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client;
+
+import com.google.gwt.dev.shell.ShellGWT;
+
+/**
+ * The hosted mode implementation of the magic GWT class, with different
+ * implementations of certain core methods.
+ */
+public final class GWT {
+ public interface UncaughtExceptionHandler {
+ void onUncaughtException(Throwable e);
+ }
+
+ // hosted mode default is to log the exception to the log window
+ private static UncaughtExceptionHandler sUncaughtExceptionHandler = new UncaughtExceptionHandler() {
+ public void onUncaughtException(Throwable e) {
+ log("Uncaught exception escaped", e);
+ }
+ };
+
+ public static UncaughtExceptionHandler getUncaughtExceptionHandler() {
+ return sUncaughtExceptionHandler;
+ }
+
+ public static void setUncaughtExceptionHandler(
+ UncaughtExceptionHandler handler) {
+ sUncaughtExceptionHandler = handler;
+ }
+
+ public static Object create(Class classLiteral) {
+ // deferred binding at runtime
+ return ShellGWT.create(classLiteral);
+ }
+
+ public static String getTypeName(Object o) {
+ // uses reflection in hosted mode
+ return ShellGWT.getTypeName(o);
+ }
+
+ public static boolean isScript() {
+ // false in hosted mode
+ return false;
+ }
+
+ public static String getHostPageBaseURL() {
+ return Impl.getHostPageBaseURL();
+ }
+
+ public static String getModuleBaseURL() {
+ return Impl.getModuleBaseURL();
+ }
+
+ public static String getModuleName() {
+ return Impl.getModuleName();
+ }
+
+ public static void log(String message, Throwable e) {
+ // logs to the shell logger in hosted mode
+ ShellGWT.log(message, e);
+ }
+}
diff --git a/user/src/com/google/gwt/core/client/Impl.java b/user/src/com/google/gwt/core/client/Impl.java
index 0dad830..cb37219 100644
--- a/user/src/com/google/gwt/core/client/Impl.java
+++ b/user/src/com/google/gwt/core/client/Impl.java
@@ -38,9 +38,8 @@
(o.$H ? o.$H : (o.$H = @com.google.gwt.core.client.Impl::getNextHashId()()));
}-*/;
- static native String getModuleBaseURL() /*-{
- // this is intentionally not using $doc, because we want the module's own url
- var s = document.location.href;
+ static native String getHostPageBaseURL() /*-{
+ var s = $doc.location.href;
// Pull off any hash.
var i = s.indexOf('#');
@@ -60,4 +59,12 @@
// Ensure a final slash if non-empty.
return s.length > 0 ? s + "/" : "";
}-*/;
+
+ static native String getModuleBaseURL() /*-{
+ return $moduleBase;
+ }-*/;
+
+ static native String getModuleName() /*-{
+ return $moduleName;
+ }-*/;
}
diff --git a/user/src/com/google/gwt/core/public/hosted.html b/user/src/com/google/gwt/core/public/hosted.html
index 9aeacec..e2cd669 100644
--- a/user/src/com/google/gwt/core/public/hosted.html
+++ b/user/src/com/google/gwt/core/public/hosted.html
@@ -2,13 +2,14 @@
<head><script>
var $wnd = parent;
var $doc = $wnd.document;
-var $moduleName;
+var $moduleName, $moduleBase;
</script></head>
<body>
<font face='arial' size='-1'>This html file is for hosted mode support.</font>
<script><!--
-function gwtOnLoad(errFn, modName){
+function gwtOnLoad(errFn, modName, modBase){
$moduleName = modName;
+ $moduleBase = modBase;
if (!external.gwtOnLoad(window, modName)) {
if (errFn) {
errFn(modName);