Customizing GWT’s browser detection

In a recent project, we experienced the phenomenon that the GWT application was broken when started in Internet Explorer 11 embedded in a specific rich client application. It turned out that the user agent string sent by the browser was set to that of IE 7 while the rendering engine ran in IE 11 mode. As GWT detects browsers based on their user agent string, the IE6/IE7 permutation was chosen which resulted in a completely broken and unusable rendering of the application, and multiple JavaScript exceptions were thrown.

As the mentioned rich client could not be changed by us, we had to make GWT choose the right permutation. The solution for this was to overwrite the browser detection code bundled with GWT. The post shows how …

What’s built into GWT

The permutation selection in GWT is based on properties. In a typical application, there are at least two of those properties:

  • user.agent: detected by parsing the user agent string of the used browser
  • locale: detected using the browser language

The code to detect the “user.agent” property at runtime is generated by the class com.google.gwt.useragent.rebind.UserAgentPropertyGenerator. When having a look at this class, we can see that each browser detection part is declared using UserAgentPropertyGeneratorPredicate instances. This is how it looks for IE10:

new UserAgentPropertyGeneratorPredicate("ie10")
  .getPredicateBlock()
  .println("return (ua.indexOf('msie') != -1 && ($doc.documentMode >= 10));")
  .returns("'ie10'")

This class is configured as property provider in com.google.gwt.useragent.UserAgent.gwt.xml the following way:

<property-provider name="user.agent" generator="com.google.gwt.useragent.rebind.UserAgentPropertyGenerator"/>

A deeper look into the browser detection code

As the user agent string of IE11 (something like Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko) does not contain the token “msie”, no predicate matching the IE permutations (ie6, ie8, ie9, ie10) will match. As the user agent string contains “Gecko”, the user.agent property will get the value gecko1_8 (which is totally fine, as IE11’s Trident engine behaves very similar to Firefox).

If we now change IE11’s user agent string to that of IE7, we end up with the user.agent property being set to ie6.

How to customize GWT’s browser detection

To customize GWT’s browser detection code, we have to provide our own implementation of the UserAgentPropertyGenerator. In our case, we had to move the UserAgentPropertyGeneratorPredicate detecting gecko_1.8 up before detection of IE versions. The important change besides moving code was to evaluate the documentMode property available on the document object in newer IE versions:

new UserAgentPropertyGeneratorPredicate("gecko1_8")
  .getPredicateBlock()
  .println("return (ua.indexOf('msie') == -1 && ua.indexOf('gecko') != -1) || (ua.indexOf('msie') != -1 && ($doc.documentMode >= 11));")
  .returns("'gecko1_8'")

To make the custom UserAgentPropertyGenerator to be used in favor of the default one, we have to provide the respective property-provider configuration in our own *.gwt.xml file:

<property-provider name="user.agent" 
  generator="de.oio.someproject.rebind.UserAgentPropertyGenerator"/>

The important part doing this is that this declaration must occur after the typically used inherits of com.google.gwt.user.User, as the last declaration overwrites any previous one:

<inherits name='com.google.gwt.user.User'/>
<property-provider name="user.agent" 
  generator="de.oio.someproject.rebind.UserAgentPropertyGenerator"/>

This solved the problem in a robust way that doesn’t break compatibility with other browsers.

Short URL for this post: http://wp.me/p4nxik-2ha
This entry was posted in Java Web Frameworks and tagged , , . Bookmark the permalink.

One Response to Customizing GWT’s browser detection

  1. Saad says:

    This approach will be helpful to anyone wishing to provide different implementations for mobile devices (without using mgwt) while keeping their code clean (not using Navigator.getUserAgent() in app-specific code such as in onModuleLoad). Great post!

Leave a Reply