Chrome Frame, IE Woes, Canvases

Google’s latest salvo in the War Against Terror Microsoft is Chrome Frame, a plugin for IE that replaces IE’s HTML and JavaScript engines with Chrome’s, i.e. Webkit with slightly inferior text rendering and slightly different JavaScript performance.

I’ve spent almost exactly the last six months working on a project called Acumen. My previous job was web advertising, and with web advertising the most important target is the lowest common denominator. In large part this is because no-one wants to see what you have to offer (no-one is going to download a plugin just to see your flashing banner ad). The other side of this is that not only does IE6 represent the lowest common denominator in terms of computer hardware and software, but it’s a pretty handy indicator of the lowest common denominator in pretty much everything else.

(Who is going to click on a “lose weight and get rich by clicking here” banner ad that is pretending to be a Windows error dialog that looks really convincing, except for the fact it jiggles and has a Windows ME frame?)

So it’s refreshing to work on a project that (a) people might deliberately want to use, that (b) is attempting in some small way to make the world a better place (by providing web access to the UA Library system’s special collections, and — ultimately we hope — special collections everywhere) and (c) values standards compliance over the lowest common denominator. The sad thing is that while the front end works beautifully in Safari and IE8, and quite well in Firefox (yes, as of writing Firefox has bugs that Safari and IE8 do not), it explodes in a screaming heap even on IE7.

Acumen's Asset Viewer is an unholy collection of nested divs implemented in JavaScript
Acumen's Asset Viewer is an unholy collection of nested divs implemented in JavaScript It supports two views of a given image -- this is the smaller size.

The main problem for me is the “asset viewer” component — about 400 lines of JavaScript that, when loaded on top of a metadata file (the digital equivalent of a catalog index card) performs a quick query of the server for assets belonging to the metadata file (in other words, if this is a metadata file for the papers of S. D. Cabanis, do we have any of the actual papers available on line?) and then presents them in a vaguely useful way in the page.

The first cut of this component uses jQuery and CSS to assemble an unholy collection of nested divs to produce a fairly straightforward browser UI. You get a horizontal scrolling list of asset thumbnails and, if you click on one, you get to see it up close in a window below. This is very similar to the UI of any number of image browsers such as Picasa or iPhoto. As I said, it works quite nicely on any current reasonably standards-compliant browser.

Even IE8.

But there are all kinds of issues. To begin with, displaying images inside an <img> tag has all kinds of downsides, not least of which is that you can’t wrap a draggable interface around <img> tags because they don’t receive the events. (I blame Safari! Safari — back in version 1.0 — introduced the ability to drag images from Safari into any other desktop app rather than right-click to “Save Image As…”. This is incredibly useful when browsing typical web pages — e.g. it probably reduces the time it takes to download a whole bunch of porn significantly — but for web app developers it means that images don’t get left clicks and drag-type events because the browser is using them.)

Now, I’d love to use <img> tags for the simple reason that they afford you a way of scaling images. If you want to scale an image to 73% of its actual size you (a) load it into an Image object in JavaScript, (b) when it’s loaded and you know its size, you create an <img> tag and sets its height and width to 73% of its native dimensions. But then you can’t scroll the image around by dragging. Also, browsers tend to do an absolutely crappy job of scaling images this way. (If it weren’t for this last problem, I’d probably work around the <img> not receiving events issue using a floating <div>.)

So what I originally did (and if you click the Acumen link you can see it in action) was store two sizes of image — 512×512 and 2048×2048 — and load the images into a <div> tag (which can receive all mouse events). It works pretty well (except in IE6 and IE7 which just can’t deal, and Firefox 3.5 which occasionally loses the plot and thinks you’re clicking on an <img> tag — I’ve reported the bug) but you can only scale to “too small” and “too big”, and generating huge numbers of differently scaled images server-side seems like a Bad Idea to me.

There’s another bad aspect to this asset viewer, and that’s the architecture is ugly. Unless I’m willing to build my own UI library or use an off-the-shelf one I actually like (when will Atlas finally ship?) there’s a huge disconnect between the way you build up a UI inside a browser via the DOM and the way it actually works. E.g. drawing a scrollbar requires the programmer to be acutely aware of the fact that it’s a div (the scrollbar) wrapping another div (the thumb) that has dimensions equal to the sum of its size and border widths. It’s very hard to simply write a routine that knows how to draw scrollbars and another routine that knows how to handle events relating to scrollbars. It’s just ugly. This makes the logic that handles events and refreshes very unpleasant indeed.

Google to the Rescue

The ideal option would be to use the HTML5 canvas for everything. This means you can handle all your events in once place (the canvas, or the element containing the canvas, receives all the events) and you can handle all your drawing — more or less — in one place (everything gets drawn in the canvas). It also lets you do things like scale images to arbitrary sizes on the client (reducing the amount of crap the server needs to store and/or the amount of work the server needs to do — e.g. resizing images or generating giant lists of available images).

But no version of IE, even IE8, supports the canvas.

Then Google did something very clever. It implemented a version of the HTML5 canvas in JavaScript by faking most of the functionality of a Canvas using an unholy combination of VML (Microsoft’s not-quite-equivalent of SVG, I think) and nested <div> tags. All you do is import excanvas.js and initialize any canvas objects you create on the fly with a one-line call and you’re golden.

Well, almost.

Acumen Asset Viewer 2 is implemented using canvas (excuse the debug crap in the top-left corner).
Acumen Asset Viewer 2 is implemented using canvas. Note that arbitrary zooming is supported, so the image has been "scaled to fit".

I’ve got everything working quite beautifully in IE8, Firefox, and Safari. Not quite so well in Opera (but, really, who the frack cares?). In IE7 it works just well enough to convince me that I can probably get it working either by (a) fooling IE7 into going into the right “mode” or (b) making specific allowances for IE7 (basically IE seems to be mismatching metrics for bitmaps and vectors so all the bitmaps end up in the wrong places). This falls into the “lots of extra work to support a broken platform” category, and I’d rather avoid it. Me and pretty much everyone who isn’t a Microtard.

Now, Google has done something both very clever and — perhaps more importantly — cunning. Chrome Frame promises to let us develop standards-compliant websites and just stop caring about IE, which is simply huge. And since it’s completely open source that pretty much means IE can be dead as far as anyone — except web advertisers — is concerned. If I want my site to render nicely in IE then I just add the Chrome Frame tags and forget about it.