2010-12-26

Vintage Firefox

When I occasionally need to check how far back a feature was introduced in Firefox, it usually turns out I only have the last few versions installed, and takes a while before I find where mozilla.org hit them, so here goes: a complete archive of the latest (mac -- tweak the urls or browse yourself for Linux, Windows or otherwise) release of every major version of Firefox from 0.8 to date (at current time of writing, 3.6.13 is the most recent Firefox released) off of ftp://ftp.mozilla.org/pub/mozilla.org/firefox/releases/, where mozilla.org hosts them, and the Firebug releases you need to do any useful web development work with them:


(Corresponding Gecko and Firebug versions, the second and third most messy to find Firefox entity, from the Gecko page on MDC and the install.rdf of the corresponding Firebug xpi:s archived at getfirebug.com.)

Never ever download and install Firefox from any other site! There is a huge, shady "pay per install" market of people bundling other people's software with malware (bot nets, typically); read all about it from Symantec (pdf) or Kevin Stevens (also pdf, the latter from Black Hat DC 2010). This goes for all executable programs you download and install on your machine; if you have no reason to trust the source, get it from a source you do trust.

2010-12-12

Opera 11 extensions @ Add-on-Con

I attended Add-on-Con this year again (I think I missed last year's), and figured I should sum up some of the interesting and/or useful stuff I picked up on while there. What I found most gratifying was the upcoming Opera (11) add-on API, which takes on the same approach as the Chrome and Safari herd, with the clean, HTML5 / postMessage / content script based design we have grown to expect, with optional background and popup pages. (Mozilla kind of wants to get there too, but does not expect to have anything remotely near it for at least another year, and I don't think it's even on any road maps yet.)

In Opera's current version, they don't go quite as far as does Chrome with its process separation, so an extension's popup can actually read from and write to localStorage from the same origin as its background page (which lets you simplify the message passing a little if you're not aiming for easy porting). Your content script runs in its own global object, but it has a window reference to the current window object, as the page sees it, and in the interest of not automatically leaking your script's guts into the page's global scope, it is not in the inheritance chain, so you actually have to use long references like window.document and window.localStorage to access them. (I tried assigning this.__proto__ = window;, which in theory should import all window properties in my content script's scope, and while that seemed to allow the former, it still failed on the latter, so I guess I hit unsupported stuff or beta bugs.)

To try it out, I made my github improved user script an Opera 11 extension. Run rake to build the zipped-up oex from the content script if you check out or fork the codebase, which puts the content script in includes/ (all scripts in includes/ with a // ==UserScript== block with Greasemonkey-style @include / @exclude rules run, not at the DOMContentLoaded event, but at page start, so you have to set your own DOMContentLoaded listener in a browser forked section if you want to execute under the same page conditions under Opera as in Firefox or a Chrome content script set to load at page ready).

Touting standards, Opera decided that a browser extension is essentially a Widget, and reused the W3C Widget standard they helped coin a while back, making its manifest sit in config.xml in the oex (a zip file) root directory (and emphasized that it also means you have to have an index.html next to it – the background page – but that it can be empty, if you like). For instance:

<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets" version="1.0.0-0">
  <name>Github improved</name>
  <icon src="icon.png"/>
  <author href="http://ecmanaut.blogspot.com/">Johan Sundström</author>
  <description xml:lang="en">Adds github changeset unfolding and other site improvements.</description>
</widget>

This also means that you can use Widget storage and config-based preference init, if you like. It indeed even seems to be the recommended way, as that gets you unlimited storage (actually: the same total cap Opera sets for itself globally), whereas your background page's localStorage gets subjected to the same arbitrary per-domain storage size cap that web sites are.

As for extension distribution, they of course have their own extension gallery you may want to submit your things to, and like all human-driven approval processes, they have a slightly anal process to get through first. Essentially, the less you can state about your add-on, the fewer opportunities you'll have to fail their test criteria; I was advised by the presenters not to add an add-on homepage, for example.

Currently, every submission attempt of yours in the approval process requires you to update your version number, so you can't aim for releasing a final 1.0 and treat all the attempts on the way there as release candidate 1 through N; my test add-on went through three revisions before coming out, and ended up named 1.0.0-0 in the end. It also was required to list change notes, that are reflected on the download page even when for an add-on that never existed before. Badly implemented good intentions also featured in the insistent numerous-stage wizard walking you through many steps every time (including screen shots, icons, et c), but there is at least the option to copy data from a previous version.

I think I may make Opera extensions of some of the extension-worthy hacks I do, but having tried the Chrome extension gallery's really smooth process, showing how it should be done, I don't think I'll have the patience to jump through Opera's hoops very often unless they improve notably.

To me, the bottom line is that Opera's add-on system doesn't make it prohibitively difficult to support Opera too, if you are already making an add-on for Chrome that does not rely on lots of deeper-integration Chrome API:s. It still surprises me that while they have the best technical user javascript implementation (and first too) a browser has ever had, they gave it the worst UI (a config switch listing a directory into which you yourself drop user scripts manually, after having modded them to add a DOMContentLoaded event listener to start the main part of the script) ever. Their js hooks still thorougly beat both Greasemonkey and Chrome, yet the lack of installation when visiting a *.user.js file locks that feature set in for only the really expert of browser users.