Revision: 9643
Author: [email protected]
Date: Thu Jan 27 13:56:33 2011
Log: At application startup, assert that the specified user.agent selection
property value indeed matches the expected value for the executing browser
/ user agent, thus avoid long hours debugging strange error messages when a
single user agent compile, typically created for testing purposes, ends up
being executed in the wrong browser. In other words, this patches saves you
from pulling your hair out.
Fixes isuess: 5861
Review at http://gwt-code-reviews.appspot.com/1278801
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=9643
Added:
/trunk/user/src/com/google/gwt/user/client/UserAgentAsserter.java
/trunk/user/src/com/google/gwt/user/rebind/UserAgentGenerator.java
/trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGenerator.java
Modified:
/trunk/user/src/com/google/gwt/dom/client/DOMImplIE8.java
/trunk/user/src/com/google/gwt/user/UserAgent.gwt.xml
/trunk/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java
/trunk/user/test/com/google/gwt/dom/client/ElementTest.java
/trunk/user/test/com/google/gwt/resources/client/DataResourceDoNotEmbedTest.java
/trunk/user/test/com/google/gwt/user/client/ui/HistoryTest.java
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/user/client/UserAgentAsserter.java Thu
Jan 27 13:56:33 2011
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 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.user.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+
+/**
+ * Helper class, which, during startup, asserts that the specified
user.agent
+ * selection property value indeed matches the expected value for this
browser /
+ * user agent, thus avoid long hours debugging strange error messages when
a
+ * single user agent compile, typically created for testing purposes, ends
up
+ * being executed in the wrong browser.
+ */
+public class UserAgentAsserter implements EntryPoint {
+
+ /**
+ * Interface to provide both the compile time and runtime
+ * <code>user.agent</code> selection property value.
+ */
+ interface UserAgentProperty {
+ String getCompileTimeValue();
+
+ String getRuntimeValue();
+ }
+
+ @Override
+ public void onModuleLoad() {
+ UserAgentProperty impl = GWT.create(UserAgentProperty.class);
+ String compileTimeValue = impl.getCompileTimeValue();
+ String runtimeValue = impl.getRuntimeValue();
+
+ if (!compileTimeValue.equals(runtimeValue)) {
+ displayMismatchWarning(runtimeValue, compileTimeValue);
+ }
+ }
+
+ /**
+ * Implemented as a JSNI method to avoid potentially using any user agent
+ * specific deferred binding code, since this method is called precisely
when
+ * we're somehow executing code from the wrong user.agent permutation.
+ */
+ private native void displayMismatchWarning(String runtimeValue,
+ String compileTimeValue) /*-{
+ $wnd.alert("ERROR: Possible problem with your *.gwt.xml module file."
+ + "\nThe compile time user.agent value (" + compileTimeValue
+ + ") does not match the runtime user.agent value (" + runtimeValue
+ + "). Expect more errors.\n");
+ }-*/;
+}
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/user/rebind/UserAgentGenerator.java Thu
Jan 27 13:56:33 2011
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2011 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.user.rebind;
+
+import com.google.gwt.core.ext.BadPropertyValueException;
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.SelectionProperty;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+import java.io.PrintWriter;
+
+/**
+ * Generator for {@link com.google.gwt.user.client.UserAgentAsserter}.
+ */
+public class UserAgentGenerator extends Generator {
+ private static final String PROPERTY_USER_AGENT = "user.agent";
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ JClassType userType;
+ try {
+ userType = typeOracle.getType(typeName);
+ } catch (NotFoundException e) {
+ logger.log(TreeLogger.ERROR, "OOPS", e);
+ throw new UnableToCompleteException();
+ }
+ String packageName = userType.getPackage().getName();
+ String className = userType.getName();
+ className = className.replace('.', '_');
+
+ JClassType remoteService = typeOracle.findType(typeName);
+ if (remoteService == null) {
+ logger.log(TreeLogger.ERROR, "Unable to find metadata for type '"
+ + typeName + "'", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (remoteService.isInterface() == null) {
+ logger.log(TreeLogger.ERROR, remoteService.getQualifiedSourceName()
+ + " is not an interface", null);
+ throw new UnableToCompleteException();
+ }
+
+ PropertyOracle propertyOracle = context.getPropertyOracle();
+ String userAgentValue;
+ try {
+ SelectionProperty userAgentProperty =
propertyOracle.getSelectionProperty(
+ logger, PROPERTY_USER_AGENT);
+ userAgentValue = userAgentProperty.getCurrentValue();
+ } catch (BadPropertyValueException e) {
+ logger.log(TreeLogger.ERROR, "Unable to find value for '"
+ + PROPERTY_USER_AGENT + "'", e);
+ throw new UnableToCompleteException();
+ }
+
+ String userAgentValueInitialCap = userAgentValue.substring(0,
1).toUpperCase()
+ + userAgentValue.substring(1);
+ className = className + "Impl" + userAgentValueInitialCap;
+
+ ClassSourceFileComposerFactory composerFactory = new
ClassSourceFileComposerFactory(
+ packageName, className);
+
composerFactory.addImplementedInterface(remoteService.getQualifiedSourceName());
+
+ PrintWriter pw = context.tryCreate(logger, packageName, className);
+ if (pw != null) {
+ SourceWriter sw = composerFactory.createSourceWriter(context, pw);
+
+ sw.println();
+ sw.println("public native String getRuntimeValue() /*-{");
+ sw.indent();
+ UserAgentPropertyGenerator.writeUserAgentPropertyJavaScript(sw);
+ sw.outdent();
+ sw.println("}-*/;");
+ sw.println();
+
+ sw.println();
+ sw.println("public String getCompileTimeValue() {");
+ sw.indent();
+ sw.println("return \"" + userAgentValue.trim() + "\";");
+ sw.outdent();
+ sw.println("}");
+
+ sw.commit(logger);
+ }
+ return composerFactory.getCreatedClassName();
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGenerator.java
Thu Jan 27 13:56:33 2011
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2011 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.user.rebind;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.linker.ConfigurationProperty;
+import com.google.gwt.core.ext.linker.PropertyProviderGenerator;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Generator which writes out the JavaScript for determining the value of
the
+ * <code>user.agent</code> selection property.
+ */
+public class UserAgentPropertyGenerator implements
PropertyProviderGenerator {
+
+ /**
+ * List of valid user agent selection property values, which helps
ensure that
+ * UserAgent.gwt.xml stays in sync with the
+ * {@link #writeUserAgentPropertyJavaScript(SourceWriter)} method body
of this
+ * class.
+ */
+ private static final List<String> VALID_VALUES = Arrays.asList(new
String[]{
+ "ie6", "ie8", "gecko1_8", "safari", "opera"});
+
+ /**
+ * Writes out the JavaScript function body for determining the value of
the
+ * <code>user.agent</code> selection property. This method is used to
create
+ * the selection script and by {@link UserAgentGenerator} to assert at
runtime
+ * that the correct user agent permutation is executing. The list of
+ * <code>user.agent</code> values listed here should be kept in sync with
+ * {@link #VALID_VALUES} and <code>UserAgent.gwt.xml</code>.
+ */
+ static void writeUserAgentPropertyJavaScript(SourceWriter body) {
+ body.println("var ua = navigator.userAgent.toLowerCase();");
+ body.println("var makeVersion = function(result) {");
+ body.indent();
+ body.println("return (parseInt(result[1]) * 1000) +
parseInt(result[2]);");
+ body.outdent();
+ body.println("};");
+ body.println("if (ua.indexOf('opera') != -1) {");
+ body.indent();
+ body.println("return 'opera';");
+ body.outdent();
+ body.println("} else if (ua.indexOf('webkit') != -1) {");
+ body.indent();
+ body.println("return 'safari';");
+ body.outdent();
+ body.println("} else if (ua.indexOf('msie') != -1) {");
+ body.indent();
+ body.println("if ($doc.documentMode >= 8) {");
+ body.indent();
+ body.println("return 'ie8';");
+ body.outdent();
+ body.println("} else {");
+ body.indent();
+ body.println("var result = /msie ([0-9]+)\\.([0-9]+)/.exec(ua);");
+ body.println("if (result && result.length == 3) {");
+ body.indent();
+ body.println("var v = makeVersion(result);");
+ body.println("if (v >= 6000) {");
+ body.indent();
+ body.println("return 'ie6';");
+ body.outdent();
+ body.println("}");
+ body.outdent();
+ body.println("}");
+ body.outdent();
+ body.println("}");
+ body.outdent();
+ body.println("} else if (ua.indexOf('gecko') != -1) {");
+ body.indent();
+ body.println("return 'gecko1_8';");
+ body.outdent();
+ body.println("}");
+ body.println("return 'unknown';");
+ }
+
+ @Override
+ public String generate(TreeLogger logger, SortedSet<String>
possibleValues,
+ String fallback, SortedSet<ConfigurationProperty> configProperties) {
+ for (String value : possibleValues) {
+ if (!VALID_VALUES.contains(value)) {
+ logger.log(TreeLogger.ERROR, "Unrecognized user.agent property
value '"
+ + value + "', possibly due to UserAgent.gwt.xml and "
+ + UserAgentPropertyGenerator.class.getName() + " being out of
sync");
+ }
+ }
+
+ StringSourceWriter body = new StringSourceWriter();
+ body.println("{");
+ body.indent();
+ writeUserAgentPropertyJavaScript(body);
+ body.outdent();
+ body.println("}");
+
+ return body.toString();
+ }
+}
=======================================
--- /trunk/user/src/com/google/gwt/dom/client/DOMImplIE8.java Wed Dec 15
10:08:15 2010
+++ /trunk/user/src/com/google/gwt/dom/client/DOMImplIE8.java Thu Jan 27
13:56:33 2011
@@ -34,7 +34,7 @@
return isIE8;
}
- // Stolen and modified from UserAgent.gwt.xml.
+ // Stolen and modified from UserAgentPropertyGenerator
private static native boolean isIE8Impl() /*-{
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf("msie") != -1 && $doc.documentMode == 8) {
=======================================
--- /trunk/user/src/com/google/gwt/user/UserAgent.gwt.xml Thu Nov 11
05:24:51 2010
+++ /trunk/user/src/com/google/gwt/user/UserAgent.gwt.xml Thu Jan 27
13:56:33 2011
@@ -21,33 +21,14 @@
<!-- Browser-sensitive code should use the 'user.agent' property -->
<define-property name="user.agent"
values="ie6,ie8,gecko1_8,safari,opera"/>
- <property-provider name="user.agent"><![CDATA[
- var ua = navigator.userAgent.toLowerCase();
- var makeVersion = function(result) {
- return (parseInt(result[1]) * 1000) + parseInt(result[2]);
- };
-
- if (ua.indexOf("opera") != -1) {
- return "opera";
- } else if (ua.indexOf("webkit") != -1) {
- return "safari";
- } else if (ua.indexOf("msie") != -1) {
- if (document.documentMode >= 8) {
- return "ie8";
- } else {
- var result = /msie ([0-9]+)\.([0-9]+)/.exec(ua);
- if (result && result.length == 3) {
- var v = makeVersion(result);
- if (v >= 6000) {
- return "ie6";
- }
- }
- }
- } else if (ua.indexOf("gecko") != -1) {
- return "gecko1_8";
- }
- return "unknown";
- ]]></property-provider>
+ <property-provider name="user.agent"
generator="com.google.gwt.user.rebind.UserAgentPropertyGenerator"/>
+
+ <!-- Asserts the the compile time value matches the runtime user.agent
value -->
+ <entry-point class="com.google.gwt.user.client.UserAgentAsserter" />
+
+ <generate-with class="com.google.gwt.user.rebind.UserAgentGenerator">
+ <when-type-assignable
class="com.google.gwt.user.client.UserAgentAsserter.UserAgentProperty" />
+ </generate-with>
<!-- Deferred binding to optimize JRE classes based on user agent. -->
<inherits name="com.google.gwt.emul.EmulationWithUserAgent"/>
=======================================
---
/trunk/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java
Tue Aug 10 10:18:55 2010
+++
/trunk/user/src/com/google/gwt/user/client/ui/impl/PopupImplMozilla.java
Thu Jan 27 13:56:33 2011
@@ -59,7 +59,7 @@
var ua = navigator.userAgent;
if (ua.indexOf("Macintosh") != -1) {
- // Version logic taken from UserAgent.gwt.xml.
+ // Version logic taken from UserAgentPropertyGenerator
var result = /rv:([0-9]+)\.([0-9]+)/.exec(ua);
if (result && result.length == 3) {
// Gecko 1.8 and earlier had the scrollbar bug on OS X.
=======================================
--- /trunk/user/test/com/google/gwt/dom/client/ElementTest.java Thu Nov 11
05:24:51 2010
+++ /trunk/user/test/com/google/gwt/dom/client/ElementTest.java Thu Jan 27
13:56:33 2011
@@ -612,11 +612,11 @@
return {};
}-*/;
- // Stolen from UserAgent.gwt.xml.
+ // Stolen from UserAgentPropertyGenerator
private native boolean isIE6or7() /*-{
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf("msie") != -1) {
- if (document.documentMode >= 8) {
+ if ($doc.documentMode >= 8) {
return false;
}
return true;
=======================================
---
/trunk/user/test/com/google/gwt/resources/client/DataResourceDoNotEmbedTest.java
Tue Oct 5 11:03:13 2010
+++
/trunk/user/test/com/google/gwt/resources/client/DataResourceDoNotEmbedTest.java
Thu Jan 27 13:56:33 2011
@@ -51,9 +51,10 @@
* com/google/gwt/resources/Resources.gwt.xml
*/
private static native boolean isPreIe8() /*-{
+ // Stolen from UserAgentPropertyGenerator
var ua = navigator.userAgent.toLowerCase();
return ua.indexOf("msie") != -1
- && !(document.documentMode >= 8);
+ && !($doc.documentMode >= 8);
}-*/;
@Override
=======================================
--- /trunk/user/test/com/google/gwt/user/client/ui/HistoryTest.java Fri Sep
10 07:02:13 2010
+++ /trunk/user/test/com/google/gwt/user/client/ui/HistoryTest.java Thu Jan
27 13:56:33 2011
@@ -47,12 +47,12 @@
}-*/;
/*
- * Copied from UserAgent.gwt.xml and HistoryImplSafari.
+ * Copied from UserAgentPropertyGenerator and HistoryImplSafari.
*/
private static native boolean isSafari2() /*-{
var ua = navigator.userAgent;
- // copied from UserAgent.gwt.xml
+ // copied from UserAgentPropertyGenerator
if (ua.indexOf("webkit") == -1) {
return false;
}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors