Thursday, May 23, 2013

Notes and Rants about Firefox and Chrome Addon building

Originally from April 16, 2012

When I first started building an Addon for Firefox, I thought it was really cool to have access to the inner workings of the browser via their extensive list of API's. I was not used to such openness by an application - usually even the most open application will limit developers in some very important and annoying ways. As a Flash/Actionscript developer, I can rant several days about how annoying a few but very important limitation in Flash are and how many weeks of my life I've devoted to getting around them. This was not the case with FF and I appreciated it. 

But after a few months, I started to get a real picture of how things really worked - every API was different, separate, and likely built by different people. This meant that every time I had to use a new API, there was a learning curve. Then months later debugging, I would have to go back and remember how the h3ll I did that. MDN (Mozilla development reference pages) became my new bible and indispensable too to understanding what would otherwise never makes sense to me. Now a few parts of the way FF extensions work was interesting to me - I really like how modules work and I appreciate the design, as well their DB API was fairly convenient and easy to use, but most curiously, is how they allow development in both C and JS and how the two relate to each other (FF was initially C only but they soon built a layer on top of that to allow for JS coding - early code examples show both a C and JS version, which allows you to glimpse at how they must have translated between the two) . 

Building this all in JS, back when the only alternative was IE and  C/C++, must have made (non-c) developers weep with joy. But unfortunately there are too many artifacts of that early transition that just aren't necessary in JS, but that they keep in place for backwards compatibility? easy understanding by long term FF programmers? simply "that's how we've always done it"?? At least these annoying quirks in FF are well documented, if still confusing and time consuming to implement. Of course that's not including the time it takes to understand each API and more importantly, what the data received from actually means / sent to it actually does ( I found an annoying lack of the details of this type of information on MDN - they explain well how to code for it, but they often neglect the details about the specifics of what the API actually does).

So after a year of my time (9hrs a day, 5 days a week: felt like a lifetime) devoted fully to Firefox development, 3 full revisions of the same plugin and 10000 changes, I, and my employer, now have a stable, well working Addon that I can be proud of. Another major reworking of it is in development, which I can say, in design, is much simpler and has -many- fewer points of possible failure (ie. more elegant). But in the middle of that transition, I was tasked with creating a brother for this Addon in Chrome and IE, though the IE brother is going to be fairly retarded. Thus the process of porting to Chrome begins. 

I knew a little about Addons in Chrome from a year ago when I made a simple history keeping Addon (which only kept the history for domains which I specified, and on those domains scanned each page for certain key words, logging instances when those words appeared). The structure of Chrome is completely different and far less powerful than Firefox - Firefox uses Gecko, which is a full blown application platform, whereas Chrome just provides a few API's. Initially, I was less than wowed. In fact, when visiting the Google campus, I stood outside the Chrome building, and in a voice as loud as I dared, told them to extend their API's because they "sucked" haha. And in scope, yes they do, compared with Firefox. But I came to learn that for what they lacked in scale, they made up for big time in at least having a unified design and a much simpler interface than FF. And fortunately, a HUGE feature of the Addon which I make, the ability to hear and intercept outgoing and incoming requests - was being implemented as I spoke (and boy, is it a million times simpler than the FF equivalent).

Chrome has inherit limitations on what features it can /should provide based upon how Addons are implemented. In Chrome, Addons are simply invisible html pages that are allowed to interact with other html pages the user has open. At the same time, they have a few special privileges in regards to loading information from the internet, as well, Chrome has a few convenient API's by which it can pass information back and forth from the Addon. First major design point - All communication in Chrome is done via JSON. Now, they are nice enough to do the translation for you, automatically calling JSON.parse and JSON.stringify when necessary, making it just a tad simpler for developers. This message passing has the same limitations as normal JSON services which populate the internet. But that also makes this very comfortable/familiar to web developers.  _  _ _ _  (( There are of course a few exceptions to this rule in recent API's, most notably the WebRequest API, which is the most recent API added to Chrome (and thank a deity too, because it's exactly what I needed for my addon). I don't mean that it's not JSON, I mean that it's not typical message passing. For those that don't know, "message passing" is an actual thing, not a noun+verb, and it is asynchronous, which as a feature has the single most significant impact on the design of an Addon for Chrome. And how the WebRequest API is different is that it acts synchronously to an addon's input. I doubt that Chrome developers suddenly decided to have a truly synchronous API, so I imagine it's simply designed to wait at certain times for the addon's response, but it still acts in sequence with external events, which makes it different from other APIs. )) _ _ _ _ _  So basically, everything is asynchronous, and everything is passed around as a JSON object. There are of course a few other quirks, but nothing else so important. Nothing is actually integrated with the browser or has control over any browser function/feature.

This is the exact opposite of Firefox. Firefox actually loads an Addon's code into the browser itself. Using "overlays", something like a CSS which "cascades" the different levels of code into a single combined source. This is then run as Firefox itself (actually, FF has two layers, the chrome and the gecko/xul runner backend ... addon code is added to the chrome). Once combined, an addon has full access to the chrome layer of Firefox and can pretty much do what it wants to that layer. As well, it's possible to create "modules" which are actually run as separate entities,  and even components which are attached to the Gecko backend (not integrated, just run on). The current reworking of the FF addon which I mentioned before, is actually transitioning from a mostly chrome layer based addon, to a purely module based form (aside from necessary UI). All functionality from the backend is done via API's, which as I mentioned before, are each different and occasionally very annoying to deal with. The lack of a single purpose, form, or style amongst these API's, means that not only are you stuck putting the pieces of the puzzle together, you also have to get your razor blade out and cut the pieces to fit each other. You would not -believe- the trouble involved in figuring out if a recently set cookie was a third party cookie or not!! You have to use three different API's and implement a component to do it correctly (certainly you can make some "guesses" and make this much simpler), and the component is terribly confusing because there are tons of different possibilities  no guarantees, and no documentation about the different cases you might see. Oh, don't forget the case where the user has multiple windows open!! Just shoot me now and get the suffering over with! Anyways, Firefox's addon capabilities are truly powerful, and truly scary at times. At least there are synchronous events (observations) and DB calls.

Then the time came to port the code of our mature FF plugin to Chrome. As I mentioned before, I had a negative opinion of Chrome's addon abilities due to it's lack of API's and limitations on functionality. But for my purposes, with Chrome's WebRequest API, it seemed to provide for the functionality I needed and thus would make a fully functioning Chrome addon possible. I'll not write my notes on this port here, but to make my point, I'll just mention that I found myself deleting significant portions of the FF addon source code. In some cases, a file of 300+ lines of code was reduced to 20 lines with Chrome with equivalent functionality. This was across the board - making the code easier to read and understand by those who are not me. Chrome actually seems to provide their API's with intended uses in mind, not just tossing some random information in the air and seeing what happens (FF). For example, just telling me what tab makes a http request is as simple as request.tab in Chrome - but in FF, I have over 80 lines of code to figure out the same thing (simple cases are about 15 lines, but it's the edge cases that really piss me off) and its STILL not guaranteed to find an answer. I'm just gonna give a "WTF?" on that one. 

Though my main concern of finding out what web page set a third party cookie, is about equally difficult in Chrome - as just like FF, the only information you start out with is a domain name of the cookie - the rest you have to figure out yourself. I'm gonna give a "WTF??" to both Chrome and FF on that one. I expect as much from FF, but I'm surprised that Google didn't think that's important information.

No comments:

Post a Comment