Thursday, March 05, 2009

Why Can't We All Just Get Along?

While building a browser-based graphical editor in Rails for a client, I ran across an cross-technology compatibility problem. The reason was not immediately apparent and caused me quite a bit of head-scratching. After sleeping on it for a night, I finally figured it out and found a workaround.

In the graphical editing app that I'm building, one of the things a user does is create selections of items. When selecting, shift-clicking on an item toggles it in or out of a selection. I use SVG to represent the items being edited in the browser and manipulate them using Javascript.

Building composite graphical elements is made easier by using the SVG use element since this can encapsulate each part, setting up display and event handling for each in a compartmentalized way, and the code for representing the parts can be shared across items. An item may reference many use elements in its representation. These elements are then added to the document and displayed automagically in the browser. Javascript functions handle the events targeted at the items.

Everything's good so far. Simple SVG elements can be manipulated easily by the Javascript, and AJAX calls are sent to the controller to update the model. Shift-clicks toggle items in and out of the selection as expected. But shift-clicking an element that includes a use element in its definition (in my case, a path element is being referenced by another element) causes a new graphical editing window to be opened in addition to the toggling.

What the heck?! Debugging the Javascript in Firebug shows that all of the expected things are happening correctly and the code is running cleanly. Yet as soon as the code finishes, up pops a new window. I'm certainly not firing a request to do this. What's going on?

It turns out that Firefox implements shift-click-on-a-link functionality that will open a linked reference in a new tab or window. This is extremely useful - I do it all the time when I'm browsing. But it didn't occur to me that this behaviour transcends normal event handling! I need different behavior in my editor!

It turns out that the way that SVG support is built in, shift-clicking on an SVG element that's drawn with the use element is misinterpretted by Firefox as a request by the user to follow the link embedded in the use element. But this reference is meant to be invisible - SVG uses these reference to display graphics, not to expose clickable links.

This wouldn't be so bad if there were a way to tell the browser that the event has been handled and not to do the default shift-click-on-a-link functionality. Normally such things are done by halting the propagation of events. Alas, for the shift-click, this has no effect. It appears that it is not possible to stop this behavior through Javascript. There may be a way, but I couldn't find it.

It's not a bug in the browser. It's not a bug in SVG. It's a bug that emerges when the two technologies come together. If the browser didn't hide access to its shift-click behavior, all would be well. Or if SVG didn't use the html linkage mechanism to do element referencing, all would also be well. But shift-click is inescapable in the browser and the element referencing is as it is in SVG. The bug is in the space where they overlap.

The workaround is not to use the use element in SVG if such elements can be shift-clicked. This causes the size of scene representations to balloon when the items are complex, but at least they can respond appropriately to events. The fix is to make Firefox (and whatever other browsers exhibit this problem) understand the difference between internal and external referencing via linkage in SVG.

Emergent bugs like these are insidious. It is the clash between multiple context-free systems that occupy the same technological space that lead to such problems. I predict they will appear more frequently as we move forward and continue building embeddable software. The best we can do for now is to fix or workaround the problems as they are found as I have here.

The bottom line is that we must recognize that in the areas where context-free systems that are used together overlap there will be ambiguity. The composite behavior of these systems must be considered and dealt with appropriately, either through code or policy. It's not clear that we'll ever be able to find all of these overlaps prior to encountering the problems they cause; I just hope the scenarios in which the problems occur aren't too dangerous.