Includes, scoping and mozIJSSubScriptLoader
Tutorial March 20th, 2007I’ve finished (tenatively) my work on includes and it’s working really well.
There’s a reasonable example of its use in the code that sets it all up, include.js. In particular, you should take a look at the ftst.configureLogger() method and the ftst.include(ftst.getMain()) call at the bottom.
Basically, a call to ftst.include will include the specified file in that place. A call to ftst.includeInScope will include the file within a confined scope and return an object that holds that scope.
Why do I care about scoping so much? Because all extension development in Firefox is in a shared scope. Your code is in everyone else’s way and theirs is in yours. That’s why it’s so important to compartmentalise your code. This isn’t such a problem in normal web-development. In a webpage all of your JS shares the same scope, but you control it all. In extension dev there can be all sorts of stuff running around, which is a nightmare. Because of this, it’s best to limit your footprint as much as possible.
In include.js, you can see that I’m using Log4js to do logging, and I’m including it in a hidden scope, so that it doesn’t get into the global namespace. I’ve changed it (I’ll explain why in my next post) and now it also uses this date formatting code, which I configure into it after it’s initialised (from another hidden scope). Why don’t I just include it directly? Well, because there are…
Problems with mozIJSSubScriptLoader and scope
Remember that this is the code that sets up the loader.
var jsLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader)
then we call (1)jsLoader.loadSubscript(filename) to load a JS file into the global scope, or (2)jsLoader.loadSubscript(filename, localScope) to load a JS file into the variable localScope.
The complication happens when we try to include one JS from within another. You can call (1) from within (1), (1) from within (2) and (2) from within (1). What you absolutely can’t do is call (2) from within (2). You can’t include something in a localScope when you are a script that is being included in a localScope no visible error occurs, but your script will halt and nothing will happen.
That would be alright, if it weren’t for this problem: A script included in a global scope from a script that is being included in a localScope will be globally available outside the local scope. If you include (1) from (2), (2) is confined to its localScope, but (1) is completely global. This seems wrong to me. It could be argued both ways, but I think that a script included from (2) should be confined to (2).
Anyway, what this means is that for Log4js to use simpledate, simpledate must either be included globally (by include.js or Log4js) or it can be included locally by include.js and then set into Log4js after it is loaded. I care a lot about scope (as you can tell) so I’m doing the latter.
The very worst thing about Firefox extension development is the shared JS context that every extension shares.
PS
I know that dojo does something similar to this, but from reading, it doesn’t seem to do it right (particularly for chrome) and also does a lot more than I want.