Make SDM compute the base url correctly
In order for GWT RPC to users need to be able to
set the base url for GWT RPC. This can be done by using a meta
tag, a baseurl tag in the dom or can be calculated dynamically.
This also fixes SDM usage for clients running of the local file
system like Phonegap clients, since they do not fail to calculate the
base url for their server anymore.
Bug: issue 8941, issue 8938
Change-Id: I4da959d27ea8f2537f0136ebab015c2e05e92f86
diff --git a/dev/codeserver/java/com/google/gwt/dev/codeserver/recompile_lib.js b/dev/codeserver/java/com/google/gwt/dev/codeserver/recompile_lib.js
index 01011dd..3044ed3 100644
--- a/dev/codeserver/java/com/google/gwt/dev/codeserver/recompile_lib.js
+++ b/dev/codeserver/java/com/google/gwt/dev/codeserver/recompile_lib.js
@@ -18,6 +18,14 @@
// We do not consider any of these classes a public API and they will be changed as needed.
$namespace.lib = $namespace.lib || {};
+StringHelper = {};
+
+StringHelper.endsWith = function(str, suffix) {
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
+};
+
+$namespace.lib.StringHelper = StringHelper;
+
/**
* Construct an instance of the PropertyHelper.
*
@@ -350,21 +358,80 @@
return $doc.getElementsByTagName('script');
};
+BaseUrlProvider.prototype.__stripHashQueryAndFileName = function(url) {
+ // Truncate starting at the first '?' or '#', whichever comes first.
+ var hashIndex = url.lastIndexOf('#');
+ if (hashIndex == -1) {
+ hashIndex = url.length;
+ }
+
+ var queryIndex = url.indexOf('?');
+ if (queryIndex == -1) {
+ queryIndex = url.length;
+ }
+
+ var slashIndex = url.lastIndexOf('/', Math.min(queryIndex, hashIndex));
+ if (slashIndex < 0) {
+ throw new 'Cannot find slash in: ' + url;
+ }
+
+ return url.substring(0, slashIndex + 1);
+};
+
+BaseUrlProvider.prototype.__maybeConvertToAbsoluteUrl = function(url) {
+ // test for valid protocol starts
+ if ((url.match(/^(?:(?:https)|(?:http)|(?:file)):\/\//))) {
+ return url;
+ }
+
+ // 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 = url + 'clear.cache.gif';
+ return this.__stripHashQueryAndFileName(img.src);
+};
+
BaseUrlProvider.prototype.getBaseUrl = function() {
+ // This code follows the order in com/google/gwt/core/ext/linker/impl/computeScriptBase.js
+ // Try to get the url from a meta property first
+ var url = this.__getBaseUrlFromMetaTag();
+ if (url) {
+ return this.__maybeConvertToAbsoluteUrl(url);
+ }
+
+ // try the nocache next
var expectedSuffix = this.__moduleName + '.nocache.js';
var scriptTags = this.__getScriptTags();
for (var i = 0; i < scriptTags.length; i++) {
var tag = scriptTags[i];
var candidate = tag.src;
- var lastMatch = candidate.lastIndexOf(expectedSuffix);
- if (lastMatch == candidate.length - expectedSuffix.length) {
- // Assumes that either the URL is absolute, or it's relative
- // and the html file is hosted by this code server.
- return candidate.substring(0, lastMatch);
+ if (StringHelper.endsWith(candidate, expectedSuffix)) {
+ var stripedUrl = this.__stripHashQueryAndFileName(candidate);
+ return this.__maybeConvertToAbsoluteUrl(stripedUrl);
}
}
- throw 'Unable to compute base url for module: ' + this.__moduleName;
+ //try the base tag
+ var baseElements = this.__getBaseElements();
+ if (baseElements.length > 0) {
+ // It's always the last parsed base tag that will apply to this script.
+ var baseElementUrl = baseElements[baseElements.length - 1].href;
+ return this.__maybeConvertToAbsoluteUrl(baseElementUrl);
+ }
+
+ // Now we are getting desperate and as a last resort we try the current doc
+ var fallbackUrl = this.__stripHashQueryAndFileName($doc.location.href);
+ return this.__maybeConvertToAbsoluteUrl(fallbackUrl);
+};
+
+BaseUrlProvider.prototype.__getBaseElements = function() {
+ return $doc.getElementsByTagName('base');
+};
+
+BaseUrlProvider.prototype.__getBaseUrlFromMetaTag = function() {
+ return __gwt_getMetaProperty('baseUrl');
};
//Export BaseUrlProvider to namespace
diff --git a/dev/codeserver/javatests/com/google/gwt/dev/codeserver/client/CodeServerGwtTest.java b/dev/codeserver/javatests/com/google/gwt/dev/codeserver/client/CodeServerGwtTest.java
index db60ce7..87fcdf5 100644
--- a/dev/codeserver/javatests/com/google/gwt/dev/codeserver/client/CodeServerGwtTest.java
+++ b/dev/codeserver/javatests/com/google/gwt/dev/codeserver/client/CodeServerGwtTest.java
@@ -159,11 +159,21 @@
assertStringEquals('test2', props.test1);
}-*/;
- public native void testBaseUrlProvider () /*-{
+ public native void testBaseUrlProvider_nocache () /*-{
var assertStringEquals = @CodeServerGwtTest::assertEquals(Ljava/lang/String;Ljava/lang/String;);
var BaseUrlProvider = $wnd.namespace.lib.BaseUrlProvider;
var baseUrlProvider = new BaseUrlProvider('testModule');
+ // have other sources return null
+ baseUrlProvider.__getBaseUrlFromMetaTag = function() {
+ return null;
+ };
+
+ // have other sources return nothing
+ baseUrlProvider.__getBaseElements = function() {
+ return [];
+ };
+
baseUrlProvider.__getScriptTags = function() {
return [
{src: 'http://localhost:9876/somepath/testModule.recompile.nocache.js'},
@@ -176,6 +186,57 @@
baseUrlProvider.getBaseUrl());
}-*/;
+ public native void testBaseUrlProvider_metatag() /*-{
+ var assertStringEquals = @CodeServerGwtTest::assertEquals(Ljava/lang/String;Ljava/lang/String;);
+ var BaseUrlProvider = $wnd.namespace.lib.BaseUrlProvider;
+ var baseUrlProvider = new BaseUrlProvider('testModule');
+
+ baseUrlProvider.__getBaseUrlFromMetaTag = function() {
+ return 'http://localhost:8888/somepathFromMeta/';
+ };
+
+ // have other sources return nothing
+ baseUrlProvider.__getBaseElements = function() {
+ return [];
+ };
+
+ // have other sources return nothing
+ baseUrlProvider.__getScriptTags = function() {
+ return [];
+ };
+
+ assertStringEquals('http://localhost:8888/somepathFromMeta/',
+ baseUrlProvider.getBaseUrl());
+ }-*/;
+
+ public native void testBaseUrlProvider_basetag() /*-{
+ var assertStringEquals = @CodeServerGwtTest::assertEquals(Ljava/lang/String;Ljava/lang/String;);
+ var BaseUrlProvider = $wnd.namespace.lib.BaseUrlProvider;
+ var baseUrlProvider = new BaseUrlProvider('testModule');
+
+ // have other sources return null
+ baseUrlProvider.__getBaseUrlFromMetaTag = function() {
+ return null;
+ };
+
+
+ baseUrlProvider.__getBaseElements = function() {
+ return [
+ {href: 'http://localhost:9876/somepath1/'},
+ {href: 'http://localhost:9876/somepath2/'},
+ {href: 'http://localhost:9876/somepath3/'}
+ ];;
+ };
+
+ // have other sources return nothing
+ baseUrlProvider.__getScriptTags = function() {
+ return [];
+ };
+
+ assertStringEquals('http://localhost:9876/somepath3/',
+ baseUrlProvider.getBaseUrl());
+ }-*/;
+
private void ensureJsInjected() {
if(injected) {
return;
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 52b6d93..2a1192c 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
@@ -92,6 +92,9 @@
}
function isLocationOk() {
+ // Dealing with CVE-2012-3695
+ // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-3695
+ // Safari is affected until 5.1.7 and iOS until 5.1.1
var loc = $doc.location;
return loc.href ==
(loc.protocol + "//" + loc.host + loc.pathname + loc.search + loc.hash);