Saturday, July 25, 2009

SVG Development with GWT 1

In order for me to fulfill my project's goal of implementing the retained-mode graphics API of Draw2d in terms of the retained-mode API of SVG, I need to be able to develop SVG using GWT. I remain confident that this is feasible, but it has been nontrivial to set up.

I was originally going to use a GWT library called Tatami, the role of which is to expose much of the functionality of the Dojo JavaScript toolkit to Java, including the excellent, cross-browser dojox.gfx retained-mode graphics API. Unfortunately, Tatami turned out to be unsuitable right out of the gate, as its graphics API does not allow the developer to assign event listeners to individual objects, only to the top-level canvas. I have no idea why they chose to impose this restriction, as setting event listeners on individual objects is an extremely common pattern in retained-mode graphics API's (certainly in Draw2d), and, from a technical standpoint, dojox.gfx allows event listeners to be attached to individual canvas objects, so there shouldn't have been any massive technical barriers to implementing it. In any case, this restriction made Tatami unsuitable for this project out of the box, and so I began investigating alternatives.

I've since been investigating the feasibility of scripting pure SVG using GWT. The first possibility I looked into was using GWT's DOM API to manipulate SVG.

The initial consideration was what kind of document would I use to deliver the SVG content? There roughly three possibilities for displaying SVG on the web today, and they are:
1. Via a pure SVG document
2. XHTML document with inline SVG; or an XHTML document with SVG embedded via the object or iframe tags
3. HTML4 document with embedded SVG via the object or iframe tags

HTML5 documents will also allow SVG inlining (without even needing to use XML namespaces!), but I don't think that there are any browsers that currently support this, so I didn't consider this.

What I found was that options 1 and 2 were not feasible, as GWT threw JavaScript exceptions while being loaded in these documents, so that left only option 3.

Option 3 proved to be problematic as well. In principal, what should have occurred was to have the object tag reference an SVG document in its data attribute, and then set an onload event listener on the object tag. Once the object tag has finished loading, then it would be possible to script its DOM via the object tag's contentDocument property. Setting a load listener on a document before scripting its DOM is a very common pattern in web front-end programming, and so I assumed that this would be straightforward to implement with GWT.

Not so. GWT, unfortunately, doesn't support setting event listeners on DOM nodes! I couldn't believe this, because it is such a basic, fundamental feature of HTML DOM, but it's true. I think the motivation for this is that GWT would prefer that you use its built-in widgets rather than low-level DOM. In any case, the effect of this is that it is not possible to listen for an onload event on a document simply using GWT's implemnetation of DOM, so I couldn't script the SVG DOM inside the object tag out of the box with GWT.

I found other problems with GWT's DOM implementation as well. To begin, it does not implement the standard Java interfaces published by the W3C (distributed by the Apache XML Commons). This is problematic because SVG has its own DOM, and while it is not necessary to use it (you can script everything in terms of the standard XML DOM), it would be beneficial to use the SVG DOM in development. Because of the way GWT's DOM has been implemented, it cannot be easily extended to make use of the standard W3C interfaces for SVG.

With these two shortcomings in mind, it seemed that the solution was to actually create a full DOM implementation for GWT in a way that used the standard W3C interfaces. The gwt-dom project started to do this back in 2007, but stopped development after GWT 1.5 included a DOM implementation. On the project page, they say that the project has been made "completely obsolete" by GWT 1.5, but I disagree, as there is still a need for a standards-compliant DOM implementation in GWT, at the very least in order to fulfill the needs of this project. Still, it makes sense to reuse as much of GWT's DOM implementation as possible: it's less work for me to implement and maintain.

To achieve this, my solution has been to use the Adapter Pattern as follows: create DOM implementation classes that extend the standard w3c.dom.* interfaces; these DOMImpl classes compose a type <T extends com.google.gwt.core.client.JavaScriptObject> object, and the DOMImpl classes delegate to this object whenever possible; where not possible, they use JSNI. In this manner, it is mostly possible to delegate to GWT DOM, while exposing a standards-compliant API to DOM that uses w3c interfaces, and I don't have to hack on the GWT core. The gwt-dom project actually has provided a good foundation for this work, as it uses a similar pattern to achieve. One improvement that I have made to gwt-dom is to use Java 5 type annotations so that, rather than simply having each DOM implementation class compose a JavaScriptObject, the wrapped object is a and thus may be specified as far as is practical (for example to a com.google.gwt.core.client.Element), and thus we can have fairly deep integration with GWT. But, overall, I have been able to reuse most of the code in gwt-dom.

There have been some problems so far with this approach so far, but I think this is mostly me tripping over the me tripping over Java's type system. I often need to convert between arbitrary objects that implement DOM interfaces, and GWT DOM objects, and this can be a bit tricky. I have a solution, but it's an ugly one, and I'm hoping to find a better way. I'll post more about this later.

So, my status is as follows: I have HTML DOM scripting working, and I'm now hooking up events. After that, it should be possible to test scripting of SVG content, and I will hopefully be able to start programming against it with respect to Draw2d very soon.

I'm not to sure where this work is going to live when I'm done... upstream in the now-defunct gwt-dom project? I may try to contact the author and see what he thinks.

1 comment:

  1. FWIW, the reason the DOM classes don't support event listeners directly is to avoid the otherwise inevtiable memory leaks. Read more here:
    http://code.google.com/p/google-web-toolkit/wiki/DomEventsAndMemoryLeaks

    ReplyDelete