Tuesday, November 20, 2007

GWT, IE7 and bugs

Lately I've been testing my GWT code with IE7, and discovered several bugs that do not break the application, but certainly make a mess of the layout.

As far as I could find out, GWT does officially, but not really support IE7. Apparently there have been some changes to the code that make GWT work with IE7 at all (instead of doing nothing when presented with that browser), but the solutions seems to be to just use IE6 implementations. Check out this thread for more information and check the current UserAgent.gwt.xml ( in gwt-user.jar/com.google.gwt.user).

I haven't tested the bugs I encountered with IE6, so I can't say if it's necessary to recognise IE7 as a separate browser in GWT, but I suspect that a separate implementation will be required sooner or later.

Update: Both issues mentioned here are non-IE7 specific, they also occur in IE6.

But now for the bugs that I found: issues with white-space:nowrap and the preventDefault method on events.

white-space:nowrap

I hit this problem when I used text in tables that I wanted to stay in one line even when the surrounding element (in this case a td) did have less width than the text (thus the nowrap). The markup worked fine in FireFox, but IE7 ignored the white-space property, breaking text to the width of the td element.

After some tests it seems I can classify nowrap behaviour into three areas:
  • overflow: when used on a div with fixed width, nowrap causes the text to be in one line, overflowing the border of the div, which keeps its set width. Divs display the same behaviour in IE7 and FireFox.
  • expansion: when used on a td element with fixed width in FireFox, nowrap causes the text to be in one line and the table cell expanded to encompass the text's width. (This behaviour is also displayed by both browsers when using a span element with set width and nowrap)
  • wrapping: when used on a td element with fixed width in IE7, nowrap is ignored and the text broken into several lines, the table cell keeps its original width.
I'm not quite sure which of these three behaviours is the correct one, but I'm quite sure the last, wrapping mode, is not correct. The desired mode for my application would be expansion, but I'd be fine with the overflow as well.

The problem can be fixed in IE7 by surrounding the text in the table cell with an additional span element upon which the fixed width and nowrap attributed are applied. However, this increases the element load and can, when used with a lot of text, cause a slowdown of the application.

Additionally, since there is no way to fix IE7 bugs specifically in GWT (without hacking the UserAgent.gwt.xml), the workaround cannot be included directly into GWT for IE7 alone. This results in the HTMLTable.CellFormatter.setWordWrap() method to fail silently in IE7.

preventDefault

Another issue I encountered when using GWT with IE7 was the DOM.eventPreventDefault(); method, or rather it not working. I use this method to prevent the selection of text on mouse down, but IE7 ignores it. I haven't tested this issue yet, but my guess would be that IE7 implementation actually fixed the incorrect behaviour displayed by IE6 (IE6 required the event handling method to return false to prevent an event from its default action instead of calling preventDefault() on it as is standard). Since GWT uses its IE6 implementation for IE7 however, the method returns false, and preventDefault() is not used on the event.

Update: This is not a general preventDefault problem but limited to text selection and works the same in both IE6 and IE7. A workaround is to set

element.onselectstart= function(){return false};

Monday, November 12, 2007

Internationalization

These last days I've been thinking about Continuity's internationalization concept. There are a few goals which I'd like to achieve, but so far I haven't found a simple and flexible way to wrap them up. What follows is a list of goals and some solution ideas.

Goals
  • Uniformity: The i18n storage and editing process for GWT and non-GWT (i.e. template-contained) should be the same, or at least seamless to the user.
  • Instant Availability: Changed messages should not require a recompilation of the whole project to be displayed, enabling on-line editing of messages (possibly even in the place they're displayed at).
  • Minimal Code Load: When dealing with GWT, we don't know at run-time which messages will be displayed, requiring either all messages to be preloaded in the browser (expensive) or compile-time analysis of the code for used messages. Since we want to reduce the code load in the browser, the latter is preferable, but it poses problems of its own.
  • Message Comments: When editing messages, comments can often be very helpful. Accordingly, each message should have an associated comment field.

Technical Issues

GWT provides two i18n solutions, a static solution with which messages are wired into the code at compile-time and a dynamic solution which reads message strings from an external source on the host page. While the latter allows for more flexibility, like changing message strings on the fly, only the former enables message selection based on code analysis, reducing the amount of unnecessarily loaded content. Assuming that some applications built on Continuity contain large amounts of text, and that applications use more than one module (and the module's messages are not all the same), pushing all messages to the client with every module seems very impractical.

Spring, and therefore also the Velocity templates employed in Continuity, reads messages from properties files using a cache and in general is flexible enough for most of the above goals. However, while Spring has no issue with comments in properties files, they cannot be accessed natively, nor are they preserved in write operations to the files.

Solutions

These solutions aim at the problems with getting messages in GWT to the client in a small load, with a fast user interface and in a dynamic fashion. The classification of the solutions (simple, medium, hard) refers to the amount of work required to implement them.

Simple Solution: Throw dynamic change overboard

An easy way to fix the incompatibilities of the goals is to get rid of the requirement for dynamic changes. If all changes to messages are done in the original underlying properties files, both the comments are preserved in Spring and static string binding can be used by GWT. This solution still requires the application to be recompiled after a change has been made, but changes can be compiled in a batch. A downside of the compilation requirement is the necessity to keep all the client side source code at hand - it would be much easier to just push changes down to the client without recompilation. This solution has the advantage that it is very simple to implement, but it requires the editing of messages in properties files, which can be an issue when done by users.


Simple Solution: Provide all messages

Although nothing I'd advocate, another simple solution is to just use dynamic message resolving in GWT. This can have two flavours. Since we have the control over the message source in the client, the source can either be a list of all messages in the program (fast user interface, big initial load) or a dynamic message request to the server (sluggish user interface, no initial load). Both solutions have disadvantages that are to big to be practical. A compromise could be that the developer specifies which messages are used in a module and those are pre-fetched. This becomes impractical with large modules though. The advantage of this approach is the full support for dynamic messages, but the disadvantages speak against it.


Medium Solution: Some dynamic change

This is an extension of the first solution introduced above, which used only back-end message editing, to enable some dynamic change of messages. The goal is to provide an editor in the front end, which writes to properties files on the server. These files are then again used to compile the front end once editing is finished. This results in some problems however: if both messages from GWT and the templates are changed (or if one message is employed in both areas), its change will appear right away in the templates, but only after explicit manual recompile in the GWT part of the application. This can be fixed by only resetting the cache manually. Another downside that's not as easy to fix is the requirement for recompilation, which has the same issues as described in the original solution without dynamic change. In difference to the original solution, this method also doesn't preserve or provide any comments (as every solution that writes/reads properties files through Spring).


Hard Solution: Adapting GWT Compiler

As mentioned in the second simple solution, having all messages available dynamically would be very useful. The problem is, however, that finding a list of all required messages for a module can only be done through code analysis (or through human labour in the code, which is not advisable), and this is complicated to implement. An ideal solution would marry both methods offered by GWT: In this case, the GWT code would include message function calls as used by the static method in GWT, but instead of replacing these method calls with literals, they are changed into calls to a cache. In addition, a list of the required messages is provided to the cache, which can then pre-fetch all those messages. Once a message is changed dynamically, the change is simply written through the cache, thus fulfilling both the requirement for dynamic message editing and keeping the code load to a minimum. This solution also has the advantage of keeping the message content separate from the client code, rendering recompilations in production obsolete.


Comment Solution:

Whenever we are talking about dynamic editing of properties files, the problem remains that writing and reading comments in properties files is not supported by the Spring mechanisms. One solution is to write a new properties accessor which gives access to comments in properties files. However, it is also possible to save messages and their comments in an entirely different format, e.g. in a database, and then compile them into the necessary properties files when they've been changed. This would probably be appropriate when using one of the methods above requiring recompilation of client code.

Conclusion

All in all it seems to me that for the beginning the first solution is best, being simple and fast to implement. It can later be extended by the adaption of the GWT compiler, allowing for dynamic change while preserving the reduced code load.