Verbosio progress, 12/2/2006

I had hoped to have a certain list of “immediate to-do” items finished by now, per my previous blog entry. Instead, I got sidetracked. Royally.

First, I ran into an as-yet-undiagnosed layout bug where the DOM had all the nodes in the right place, but certain XBL bindings were not applying. This caused me so much grief that I set the problem aside after a couple days banging on it alone. Then I had a few Bright Ideas.

To spare people who don’t care (because this is a long blog entry), the rest of this article is in the extended section.

Bright Idea Number One

First, I could use the SAX API’s to trace a document’s source code from beginning to end, and with a DOM TreeWalker, walk the equivalent DOM document from root node to end. Why would I want to do that? Well, if I also have the source code of the document (which I need for SAX), then I can match DOM nodes to source code. Almost completely (there are some minor nits that Mozilla’s SAX implementation doesn’t support yet, but not unsolvable ones).

The result is a new interface for Verbosio (xeINodePositionService.idl)
and a new component (nodePositionService.js). Don’t worry if the JS file looks extremely cluttered and sub-optimal. It’s only a first draft, and can easily go through a couple rounds of code clean-up. Later.

This also ties into my Basic XBL validation blog post a few weeks ago. The code, as it was written, was pretty bloated, and of course untested. That didn’t stop me from having another Bright Idea at just about the same time as the previous one.

Bright Idea Number Two

Because now I can identify specific character nodes and attribute values which get fed into JavaScript, and know exactly where they are in the source code, I can check each one for syntax errors. But having a XBL language pack generate a huge string matching JavaScript in source to JavaScript in the string, and whitespace everywhere else, was inefficient, and more bloat.

Instead, I created another interface (xeISourceMap),
for taking these JS strings with known locations and contents, and placing them into a much compacted string internally. Then I evaluate that smaller string in a sandbox, and look for a syntax error. If the source map finds such an error, it translates the error location in the smaller string back to its corresponding location in the original source document. The resulting JS component is jsSourceMap.js.

Now, I should make it clear that I have tested the node position service a bit, but the JavaScript Source Map component has not been tested, and probably doesn’t work quite right. Also, the names for these components and interfaces isn’t final; basically, I picked them out of thin air, and I couldn’t come up with a better name. I’ll take suggestions.

Reusing code is good for the soul. Even if you don’t have a soul.

With the above components, though, XBL validation reduces to four steps:

  1. XML well-formedness checking (done via a XML language pack)
  2. Validation of XBL elements via a recursive algorithm in the XBL language pack component,
  3. Identifying the line and column numbers of every inline JavaScript fragment in the XBL document (nodePositionService), and
  4. Using the JavaScript source map component to map every JS fragment, and then ask the map to look for a syntax error.

Nice, neat, simple. Three adjectives every programmer tries to obtain for their code.

Of these, the second step may also be partially reduced if I write a “validation-purposes-only”
DTD and a DTD-parsing component. I say partially because DTD’s can’t do the detailed levels of validation that XML schemas and custom code can, but DTD + a little extra JavaScript hook function (where the DTD reader component calls back into the XBL language pack for additional checking) should suffice for most validation purposes. If you really want rigorous validation, write a XML schema document and build XULRunner with the schema-validation extension. (Or, wait for Verbosio 0.1 and write a XML Schemas language pack and user interface!)

Sure, this sounds like a lot of work just for a XBL validation process. However, thanks to XPCOM, these components are reusable in other contexts, as I pointed out in the “Basic XBL Validation” blog entry.

Another idea I have on the to-do list is preferences-controlled validation. For example, in the XBL scenario above, a user might not care much about whether his tags aren’t in the correct order, but a JavaScript syntax error would stop him dead. So he sets a preference that makes Verbosio treat an inline JS syntax error as seriously as it would a fatal error in XML parsing (“stop parsing and force me to fix this”). Implementing the control is easy; figuring out how to name the preferences (in a given format) is more difficult – assuming that I want to use the preferences service.

A break in the gloom and doom

Incidentally, that layout bug I opened the article with? It was for a DOM Inspector-like viewer for XML documents. The right-hand panel (where you get nice things like attributes, text content, etc.) just wasn’t working. I could move nodes in to where they should show, and I could manipulate them, but they didn’t have any renderings or XBL bindings. Major headache, especially since I confirmed repeatedly that my code was working correctly.

I planned on creating a reduced testcase by gutting a copy of Verbosio’s code. I was not looking forward to that at all. Verbosio has a lot of little pieces that fit together, and removing a piece in the wrong order without putting an appropriate bypass patch in would have meant some other bustage. I understand the Verbosio code very well (to date I’ve written all of it), and by and large I think it’s good, solid code that combines to make an application. Ripping Verbosio apart to isolate a bug feels like ripping apart a trunk engine to replace one broken bolt: arduous, time-consuming, and not very pleasant… but ultimately necessary.

I may still gut a Verbosio copy to isolate the bug, but suddenly, I’m not under pressure to do it. What I realized is that I was locked into a particular mental model (“you must use a XUL deck element here”), and that the model wasn’t necessarily true. I was having problems with the deck, but not with ordinary XUL boxes. When I visualized the panels of the deck side-by-side horizontally instead of one-at-a-time, I saw intuitively that it could work, and work much better than a deck. It means the user can see more and do more at a glance than they would with the deck. Win-win situation.

It’s another case of common sense in hindsight in application interface design. So there’s an obscure layout bug that I hit. I have a workaround that is actually easier for the end-user to… well, use in the end. I’m perfectly happy with that.

Now, the funny thing is I wrote most of this code in the last 72 hours. I’m not entirely sure why, but I probably spent a good 40 hours coding and maybe 12 hours (including only four last night) sleeping. I literally went to bed at 7 am this morning, and I’ve only eaten once in 24 hours (dinner about, oh, three hours ago). It’s one of those things I don’t do very often. 🙂