March 31, 2008

Ten years... and a career

Others can speak more eloquently than I can about the significance of mozilla.org's birth. I'll put it in much simpler terms: I owe my professional career to that event and the years that followed.

I have always been a fan of the Mozilla code base - dating all the way back to my early high school years when Netscape was appearing on the scene. Shortly after I'd finished writing my book on JavaScript, I discovered Mozilla's user-interface had a huge JavaScript presence in it. After a few years tinkering around in the Mozilla codebase, a recruiting agency contacted me and asked if I wanted to do that for a living. To which my answer then - and now - is "absolutely, yes!"

A few years later, I'm working at Skyfire Labs, Inc., (which coincidentally appeared today in the Wall Street Journal), and I'm having the time of my life. I'm doing what I wanted to do, and I'm getting paid nicely to do it. What could possibly be better than that?

So when someone wants to throw a party to celebrate what Mozilla's done for the past ten years - not just at the beginning - I'm there. Mozilla technology made it possible for me to earn a decent living doing what I do best. This community made it possible.

So, to everyone who's written a line of code, filed a bug, written a testcase, figured out how to make it easier on others, or just written down what it does and how to use it... thanks.

Posted by WeirdAl at 8:11 AM | Comments (0)

March 22, 2008

XUL Trees and Objects: ClassTreeView

I love XUL trees. I even smoke them from time to time. But what I don't like is trying to build a hierarchy of objects in them - even though that's probably the best use for them.

Imagine that you want to show this tree of objects, with properties of each object horizontally, and the objects themselves laid out vertically, indented and illustrated to show which objects have which parent objects. DOM Inspector does this with DOM nodes all the time. My chrome registry viewer code does something similar for files (file systems are tree-like), and when you want to see the properties of an object, JS object inspection is usually through a tree. Even Venkman uses trees to show you functions in a file or webpage.

Still, for every different object tree I've come across, there's a different view that has to be built. Usually it's custom-built for that tree. So you've got two options: build your own view, from scratch, every time... or build a XUL tree DOM and let Gecko's own tree utilities show it to you.

Believe it or not, I've tried both approaches... and finally decided to roll my own baseline solution. (If someone else has done this before, please let me know. It's best to have this in a common place.) More details in the extended section.

nsITreeView

Here's the kind of image that comes to mind every time I've looked at the nsITreeView interface:

nsITreeView's heart

Seriously, what in the name of (it's Easter weekend, I don't care to push my luck) what in the world is this? At least half the methods on it deal with row-specific or cell-specific details. Couldn't the people who work with this just given me a nsITreeRow interface and a getRow() method on the tree view?!?

I mean, it feels so dirty. It feels... procedural. Not object-oriented at all. How do you map a row in this tree to an object? You don't through this interface - because there's no row to grab.

It's also an interface with a lot of methods on it to implement. Yeeeuck. It's not immediately obvious which ones you need, and when. I've never truly understood this tree view stuff, even with XULPlanet's tutorial lending a hand.

<xul:treeitem/> and friends

I do understand treeitems, treechildren, treerow, and the like, though. It's DOM! It's something we already have! It's easy to inspect, to debug! It's familiar!

This was my preferred method of building trees for five years. Never mind the various people who kept telling me that tree views were so much better, ignoring me when I said, "No, make this a DOM set of trees, that doesn't need a lot to understand." It's simple. I like simple.

Until two or three months ago, when I discovered nsTreeContentView. For those of you who don't know about it, this is what takes your pretty DOM-based XUL tree and converts it into a nsITreeView object.

So here's what happens: My app spends a lot of time crafting this oh-so-nice, fifteen-levels-deep, memory-crushing, CPU-melting DOM fragment. My program then appends it, forcing nsTreeContentView to go to work, ripping that fragment apart and creating... a nsITreeView that I did everything to avoid dealing with, and which you just waited far too long for. Not to mention the bootstrapping I have to put on top of that DOM tree to bind each row to some object.

There are a few things that are more efficient than this approach...

Starting over

So finally I said, "enough of this." If I'm going to build a tree for objects, I'm going to do it right. I'm going to create a generalized tree view component, reading from both the tree and from objects, to show the object hierarchy. I'm going to define a very simple, and very flexible, API that my component's users can build in and make it all work.

The first concept is that tree columns define what the cells show. So let's just go ahead and define a propertyname attribute to stick on each <xul:treecol/>. element. We still need those, anyway. For more complex properties, define a fallback by allowing the tree's author to set a function on the column element. I do this through the DOM 3 UserData API.

The second concept is that there's a 1:1 mapping between rows and objects. That is, for each row, there should be exactly one object, and vice versa. So, at least internally, we need a TreeRow class to store a reference to the original objects.

The third concept is that the tree view needs to know how to get child objects of a given object. To do that, the tree view requires you to pass in a function which takes an object and returns an array of objects, which you say are children of that object.

The fourth concept is to provide a way to add top-level objects to this tree. Every DOM tree has a root node, every file system has a few items at the top. A generic tree view class can't know about them beforehand, so you have to tell it about them.

Put these four together, and you have enough to build a generic algorithm, a generic "class tree viewer".

ClassTreeView

Source Code

Sample chrome code (copied, altered from the XUL Tutorial on developer.mozilla.org)

testTreeView.xul

Element and MatterState represent (in this case) two similar classes which live in the same tree - so really, you'd only need one of them. One column has a propertyname attribute on it. The other one (further down in the init() function) has a cellGetter function on it. The getObjectChildren() function tells the tree view how to go from one object to its children. Everything else is just raw data, initializing the tree and the tree view, and adding top-level objects to the tree. It's really just that simple, and pretty light-weight.

Now, I will admit this ClassTreeView is not complete. For instance, it's read-only, and doesn't yet support progress meters, check boxes, etc. I don't need those at this point, and to me it's just more complexity (see nsITreeView for details). As a starting point for showing a true object hierarchy, though, it's good enough. If you know tree views and want to finish this, patches accepted!

API-wise, it's a "best guess". I designed this to work based on my needs and understanding. Maybe there are better approaches - but for this particular problem, I think it's a good start.

The one true weakness to ClassTreeView is that content web pages cannot use it. The reason for that is buried at the end of the nsITreeView.idl file: the nsINativeTreeView interface. So perhaps I will someday rewrite this in C++ code - after I figure out C++-to-JavaScript interfaces, and with a lot of reviews to make sure I get it right - and I'll make it available to the Web. (Then again, you don't see a whole lot of XUL on the Web... maybe for reasons like this.)

Posted by WeirdAl at 7:24 PM | Comments (4)

March 20, 2008

Verbosio: Coming out of hibernation

Over the last several weeks, I've been having this gnawing urge to restart work on Verbosio. It's been getting stronger, to the point where I just can't keep quiet about it: I'm getting back into it, and looking forward to completing my work on an 0.1 "proof-of-concept" XML editor.

Since I put Verbosio to sleep several months ago, I've had a number of thoughts:

  • I want to use the new Songbird-provided build system. I've played around with it a few times since it first arrived, and I'm pleased. This makes it much easier to compile a specific XULRunner-based application, using Mozilla's own build system.
  • I've gotten much more comfortable working with C++ code. When I started work on Verbosio, I had a goal that said "no compiling necessary." That was because I didn't want to muck around too much. The new build system - and a couple years experience - obsoletes that requirement, in my opinion. Besides, I still anticipate that I might have to customize Gecko a little bit. Hopefully not that much.
  • CVS would make me pay dearly for my earlier decisions. Because of the above two points, I want to rearrange Verbosio's source code to take advantage of the build infrastructure Mozilla already provides. That means a lot of files and directories moving. In CVS, that means you lose all revision history. No thanks. Fortunately, SVN is coming, and supposedly soon. I am looking forward to it.
  • Writing to ZIP files changes the game. nsIZipWriter means that Verbosio, in its XUL demo work, could start working with jarred chrome for real. Fun times.
  • Bit rot has been surprisingly minimal. Code that I wrote many moons ago still works, at least in Gecko 1.9 beta 5. There was a regression in 1.9b4 that has since been fixed.
  • Waiting for OpenKomodo was a bit of a mistake. I figured by now they would be on the 1.9 code base, which Verbosio requires. Also, I haven't reached "proof-of-concept", that working model that lays the foundation for any possible merging or sharing of code. I need to move forward without them, for the time being.
  • I still have ideas to explore. In addition to this list, I've had a couple more ideas that might be useful. For instance, I've seen myself working with DOM TreeWalkers, where the NodeFilter is wholly implemented in JavaScript. Wouldn't it be nicer, perf-wise, if you could have the first half of the filter implemented as a common C++ object (running ten times faster than JS code, thanks)?
  • I hope I can find the time and the motivation, again. It's been hard, yes, but I've learned a bit about myself. These days, I can usually find the drive to work on side projects one, maybe two days a week. Yet these side projects are what keep my skills and ability to create, to innovate, at the sharpest levels. That's when this business of software development is most fun: creating something new, useful, and radical. It also fires me up when it comes to my day job - I've noticed that my biggest ideas for work come to me either during or soon after a bit of heavy-duty cogitation on something Mozilla, but not Skyfire. I can't explain it, but it works.

Ultimately, I'm still not sure of where this Verbosio project will take me - or where I'll take it. But I take heart in the fact that I still don't see anyone doing anything remotely like what I have in mind for Verbosio. Sure, it's hard to do (as I've said before), but I have a vision, and that makes the years of effort worthwhile.

Thanks for reading!

Posted by WeirdAl at 8:04 PM | Comments (1)

March 19, 2008

CodingHorror visits Firefox extensions

The Dark Side of Extensions

As a guy who works on Firefox code on a regular basis, and as someone who recently started reading CodingHorror again, I thought it worth pointing this post out. Jeff Atwood is usually insightful.

That doesn't mean I agree with him, and I certainly don't, here. I'm posting this in the hopes that someone from our Firefox community will respond. Mr. Atwood is one of those voices worth hearing and answering, in my opinion.

Posted by WeirdAl at 7:20 AM | Comments (4)