2007-08-22

Extending Greasemonkey by way of getters

Javascript getters and setters may be one of the most useful upcoming language features today (already working in some browsers, including Firefox), but google the feature and you will be swamped with articles and posts, not on how to put them to good use, but how to defend against malicious use of them for web pages tripping (Firefox / ditto extension) security holes. It's really just John Resig that stands out of the crowd, showing how it is done, suggesting good uses, and teaching the stuff.

Getters and setters are a Javascript 1.6 (IIRC) language feature that lets you provide interfaces that look and feel like DOM 0 interfaces, where you read and set a variable or property like document.title or document.links, and receive something that may or may not require computation, compositing and much else behind the scenes. The only news is that you can now implement things like the location object (set href and affect all the other properties in one go) from mere javascript and that the syntax is even rather friendly, by javascript standards:
var myLoc = (function() {
var url; // private variable hidden in the function closure

return {
toString: function() {
return this.href;
},
get href() {
return url;
},
set href(h) {
return url = h;
},
get prototcol() {
return url.match(/^.*?:/)[0];
},
set protocol(p) {
url = p + url.match(/^.*:(.*)/)[1];
return p;
}
};
})();
- which will yield you an object with an href and a protocol property you can tweak, just like any other, and see changes reflect to the other property, what you see if you alert(myLoc), and so on. It's a dream for making APIs that don't feel like java pushed down your throat with a ten-foot pole.

I've been meaning to add some features to Greasemonkey to grant it proper introspection (as all great software systems are introspective), for the Greasemonkey header block, and once I thought twice about it, for the source code at large. Something like a self object, with a "source" property for the code and a headers property for the parsed headers -- so self.headers.include would be the array of all @include headers, and so on.

I wanted that feature to give anybody the option to make their own cool extensions and libraries to Greasemonkey, making use of structured information -- much like an @xpath feature I've been noodling on on and off over the past few months to make GM scripts much more maintainable (and quick to write, and, optionally, self deprecating).

Anyway, the natural way of implementing this is via getters, and I was just about done when I hit a wall with Mozilla's extension API which wouldn't let the script executing in the Greasemonkey sandbox read my exposed getter variable (at the time named GM_headers): it threw a "Permission denied to get property Sandbox.GM_headers" exception when code accessed GM_headers, and that was that. Is this feature crippled for extensions wanting to provide getter / setter APIs from javascript code, or do I miss some incantations to bless the sandboxed code to read that?

The (hopefully) full picture of relevant details, surrounding my implementation:
var safeWin = new XPCNativeWrapper(unsafeContentWin);
var sandbox = new Components.utils.Sandbox(safeWin);
// ...
sandbox.__proto__ = {
get GM_headers() {
return headers.get();
}
};
sandbox.__proto__.__proto__ = safeWin;
This misfeature is also what stopped us from getting a responseXML property on the GM_xmlhttpRequest response object, earlier this spring. Any Mozilla hackers out there with solutions, knowledge or even just input on this? Your help and insights on this would be most valued.

2007-06-25

Users drift, time zones vary

In a time of user centricity on the web, I have still not seen any web site or service that groks that time zone is not a per-author (or sometimes even per-visitor) configuration setting, but a function of present location, which in turn is a variable entity over time. (Hint: per-item, with a preference towards suggesting last used time zone, works a lot better. Do it in style, presenting it by the widget you have that shows creation time or similar, so it reads something like "June 25, 11:36, Europe/Stockholm time", relevant fields appropriately editable.)

I entered my flight details (I'm visiting San Francisco most of the later half of July) in Google Calendar the other day, but couldn't tie down start and end points in geography, so it shows my westward flights as ridiculously short, and my first-hop home flight in later July as a huge 24-hour stretch from San Francisco to Amsterdam. There are not always 24 hours in a day when in transit, but it's a very common assumption when crafting user interfaces. Of course it's a can of worms coming up with a good visualization of what time it is throughout the day, if you're in Sweden the first few hours of the day, in London by lunch and Vancouver at the end of the day, adding hours to the day every hop on the way.

Maybe FireEagle and cool hacks like this might pave the way towards betterization on this front, for the "here and now" kind of tools, like blogs, instant messengers and other social applications.

Good OpenID integration for the presence awareness broker would of course be another huge hit. That probably needs integration work from both sides, as the time for breaking some of the assumptions ranted about above comes upon us. I personally have a feeling this forthcoming user centric wave will hit the web from the client rather than server side, though.

These days, neither clients nor users are as dumb as they used to be treated as. But most server side code still is.

2007-06-13

Javascript MD5 singleton

I really love how Yahoo! are spreading the gospel on sound javascript code practices and design patterns, popularizing good ideas among the masses, the most recent example being the module pattern.

In some ways it's the pop version of Douglas Crockford's writings on javascript, in this case private members in javascript, which tells the story of how almost any higher aspect of programming in javascript is accomplished through exploiting the few properties of the function keyword and closures. You can do most kinds of programming in javascript, there is just very rarely any syntactic aid for it.

By contrast, posts like this one would be the boring narrative recount telling you to read the gospel. I'm actually going to add a tiny bit more than that, though, and give an example of how to (re)structure code (not even necessarily your own) to use the module pattern, where it did not use to. It greatly helps keeping mess out of your way.

For a one-off hack (a user script that shows bookmark permalinks at del.icio.us, so you can share them with others, even before more people than yourself have bookmarked the url), I needed a javascript MD5 implementation. Paul Johnston's one (linked) is BSD licensed and works well, but exposes lots of namespace clutter, so I wrapped it up in an MD5 object, exposing only the bits I wanted from it.

Basically, I ended up framing the code inside a var MD5 = (function(){ /* original function definitions here */ return {hash:hex_md5}; })(); block, which exposes the only method I wanted as MD5.hash( data ). No namespace pollution, readable code.

Since an MD5 component is something I often want to use to interface with the world (here it was just to interface with Del.icio.us, which uses hex encoded MD5 hashes of urls for permalinks), I decided to go on and wrap up a version exposing the full (MD5 related) feature set of the lib, though, into a tidy MD5 module.

The API is very lightly wrapped, into methods MD5.string(data), which does a raw MD5 hash on its input string, MD5.hex(data), returning the same, but hexified, MD5.base64(data), ditto but base64 encoded instead, and the same method names with an "MD5.hmac." prefix for the hmac variants. (There is an MD5.test() method for good measure too.) The same config options as the original are supported too, and with the same default values, so set MD5.b64pad = "=" for real base64.

I thought I'd mention this too, here: if you want a similar javascript AES/Rijndael singleton, you should peek at Josh Davis's project on javascript cryptography, which has embarked on a similar path to the above since its conception. It might still be applying an UTF-8 codec on all data that passes through it, but if you need raw AES, just drop the few utf8 related lines, and you've got one.