You are on page 1of 8

Using JavaScript Code Modules in Firefox 4 Add-Ons

Using JavaScript Code Modules in


Firefox 4 Add-Ons
Finnbarr P. Murphy
(fpm@fpmurphy.com)

The concept of a JavaScript code module in the Gecko layout engine was first introduced in Gecko
1.9. This post discusses how such code modules can be used to simplify preference and add-on
management in Firefox 4 which uses Gecko 2.0 and JavaScript 1.8.5. It uses a simple Firefox

ly
add-on called HTML5toggle as an example of how to modify existing code to use Javascript code
modules.

on
A JavaScript code module is simply some JavaScript code located in a registered (well-known)
location. JavaScript code modules are primarily used to share code between different privileged
scopes. They can also be used to create global JavaScript singletons that previously required using

se
JavaScript XPCOM objects.

Here is a list of the current JavaScript code modules in Firefox 4.0:


lu
Name Purpose
AddonManager.jsm Provides routines to install, manage, and uninstall add-ons.
a
AddonRepository.jsm Allows searching of the add-ons repository.
Interface that allows JavaScript code to call native libraries without
nn

ctypes.jsm
requiring the development of an XPCOM component.
Provides the path to the directory into which the last download
DownloadLastDir.jsm
occurred.
o

Provides routines for performing basic geometric operations on points


Geometry.jsm
rs

and rectangles.
Provides routines to convert between JavaScript Date objects and ISO
ISO8601DateUtils.jsm
8601 date strings.
pe

Provides networking utility routines, including the ability to easily


NetUtil.jsm
copy data from an input stream to an output stream asynchronously.
Provides access to the last URL opened using the "Open Location"
openLocationLastURL.jsm
option in the File menu.
r

Provides access to low-level hardware and OS performance


Fo

PerfMeasurement.jsm
measurement tools.
Provides an easy way to get the correct plural forms for the current
PluralForm.jsm
locale, as well as ways to localize to a specific plural rule.
PopupNotifications.jsm Provides an easy way to present non-modal notifications to users.
Provides getters for conveniently obtaining access to commonly-used
Services.jsm
services.
Contains utilities for JavaScript components loaded by the JavaScript
XPCOMUtils.jsm
component loader.

The Components.utils.import method is used to import a JavaScript code module into a specific
JavaScript scope. A module must be imported before any functionality in the module is used.
Modules are cached when loaded and subsequent imports do not reload a new version of the
module, but instead use the previously cached version. Thus a given module is shared when
imported multiple times.

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 1/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

One of the most commonly used modules is the Services.jsm module which offers a wide
assortment of lazy getters that simplify the process of obtaining references to commonly used
services. A lazy getter is a getter function that checks the item which it is to retrieve (get) and
initializes it if it is not set.

Here is a list of the available services in Services.jsm:

Service Accessor Service Interface Service Name


appinfo nsIXULAppInfo nsIXULRuntime Application information service
console nsIConsoleService Error console service
dirsvc nsIDirectoryService nsIProperties Directory service
droppedLinkHandler nsIDroppedLinkHandler Dropped link handler service
io nsIIOService nsIIOService2 I/O Service

ly
locale nsILocaleService Locale service

on
obs nsIObserverService Observer service
perms nsIPermissionManager Permission manager service
nsIPrefBranch nsIPrefBranch2
prefs Preferences service
nsIPrefService

se
prompt nsIPromptService Prompt service
scriptloader mozIJSSubScriptLoader lu JavaScript subscript loader service
search nsIBrowserSearchService Browser search service
storage mozIStorageService Storage API service
strings nsIStringBundleService String bundle service
a
tm nsIThreadManager Thread Manager service
vc nsIVersionComparator Version comparator service
nn

wm nsIWindowMediator Window mediator service


ww nsIWindowWatcher Window watcher service
o

For Firefox 4 the add-on manager was extensively reworked. It is now based on a JavaScript code
rs

module called AddonManager.jsm. See the source code for AddonManagerInternal in


…/mozapps/extensions/AddonManager.jsm for full details. Visually it is implemented as a tab
pe

instead of a separate window and uses icons that are 64×64 pixels instead of 32×32 pixels.
Add-on metadata is now stored in an SQLite database instead of RDF-based storage.

Here is a list of the available methods in AddonManager.jsm:


r

METHOD PURPOSE
Fo

getInstallForURL Asynchronously gets an AddonInstall for a URL


getInstallForFile Asynchronously gets an AddonInstall for an nsIFile
getAddonByID Asynchronously gets an add-on with a specific ID
getAddonsByIDs Asynchronously gets an array of add-ons
Asynchronously gets add-ons that have operations waiting for
getAddonsWithOperationsByTypes
an application restart to complete
getAddonsByTypes Asynchronously gets add-ons of specific types
getAllAddons Asynchronously gets all installed add-ons
Asynchronously gets all current AddonInstalls optionally
getInstallsByTypes
limiting to a list of types
getAllInstalls Asynchronously gets all current AddonInstalls
Check whether installation is enabled for a particular
isInstallEnabled
mimetype

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 2/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

METHOD PURPOSE
Checks whether a particular source is allowed to install
isInstallAllowed
add-ons of a given mimetype
Starts installation of an array of AddonInstalls notifying the
installAddonsFromWebpage
registered web install listener of blocked or started installs
Adds a new InstallListener if the listener is not already
addInstallListener
registered
removeInstallListener Removes an InstallListener if the listener is registered
Adds a new AddonListener if the listener is not already
addAddonListener
registered
removeAddonListener Removes an AddonListener if the listener is registered

ly
Note that all of the above methods are asynchronous which mean that a callback function is
required. The callback may well only be called after the method returns.

on
In earlier versions of Firefox, you have to observe em-action-requested to determine whether an
add-on should be uninstalled or not. For example, the following code could be used in Firefox 3 to
detect when to cleanup up the preferences for HTML5toggle during add-on removal.

se
var uninstall = NULL,
observe: function(subject, topic, data)
{
lu
if (topic == "em-action-requested") {
subject.QueryInterface(Components.interfaces.nsIUpdateItem);
if (subject.id == MY_EXTENSION_UUID) {
a
if (data == "item-uninstalled") {
this.uninstall = true;
nn

} else if (data == "item-cancel-action") {


this.uninstall = false;
}
}
o

}
if (topic == "quit-application-granted") {
rs

if (this.uninstall) {
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefServic
pe

e);
var prefBranch = prefService.getBranch('extensions.html5toggle.');
prefBranch.deleteBranch("");
}
this.unregister();
r

}
Fo

},
register: function()
{
var observerService = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "em-action-requested", false);
observerService.addObserver(this, "quit-application-requested", false);
observerService.addObserver(this, "quit-application-granted", false);
},
unregister : function()
{
var observerService = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "em-action-requested");
observerService.removeObserver(this, "quit-application-granted");
observerService.removeObserver(this, "quit-application-requested");
}

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 3/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

This code no longer works in Firefox 4. Instead, an add-on needs to handle two events,
onUninstalling and onOperationCancelled, as shown in below.

Here is the source code for overlay.js prior to being upgraded to using JavaScript code modules. It
is from HTML5toggle v1.02.

const MY_EXTENSION_UUID = "{16f32596-71ca-4053-a4f3-cfc8b157f541}";


var html5toggle = {
beingUninstalled: null,
onLoad: function() {
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService).getBranch("html5.");
var value = prefs.getBoolPref("enable");
// save the current value of html5.enable preference in our own branch

ly
var myprefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var mybranch = myprefs.getBranch('extensions.html5toggle.');

on
mybranch.setBoolPref('enable', value);
myprefs.savePrefFile(null);
this.register();
this.setTBbutton(value);
this.setMIchecked(value);

se
},
setMIchecked: function (value) {
var menuitem = document.getElementById('html5toggle-menuitem');
if (value) {
lu
menuitem.setAttribute("checked", "false");
} else {
menuitem.setAttribute("checked", "true");
a
}
},
nn

setTBbutton: function (value) {


var tbButton = document.getElementById('html5toggle-toolbar-button');
var stringsBundle = document.getElementById('html5toggle-string-bundle');
if (tbButton) { // button might still be on Toolbar Palette
o

if (value) {
tbButton.tooltipText = stringsBundle.getString('onString');
rs

tbButton.setAttribute("image","chrome://html5toggle/skin/images/HTML5on16N.png"
);
} else {
pe

tbButton.tooltipText = stringsBundle.getString('offString');
tbButton.setAttribute("image","chrome://html5toggle/skin/images/HTML5off16N.pn
g");
}
}
r

},
onToolbarButton: function(e) {
Fo

var prefs = Components.classes["@mozilla.org/preferences-service;1"]


.getService(Components.interfaces.nsIPrefService).getBranch("html5.");
var value = prefs.getBoolPref("enable");
value = !value;
prefs.setBoolPref("enable", value);
this.setTBbutton(value);
this.setMIchecked(value);
},
onMenuItem: function(e) {
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService).getBranch("html5.");
var value = prefs.getBoolPref("enable");
var menuitem = document.getElementById('html5toggle-menuitem');
value = menuitem.getAttribute("checked") == "true" ? false : true;
prefs.setBoolPref("enable", value);
this.setTBbutton(value);
},
observe: function(subject, topic, data)

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 4/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

{
if (topic == "quit-application-granted") {
dump("\nhtml5toggle:quit-application-granted ....");
if (this.beingUninstalled) {
// restore initial value of html5.enable preference
var myprefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var mybranch = myprefs.getBranch('extensions.html5toggle.');
var value = mybranch.getBoolPref("enable");
mybranch.deleteBranch("");
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var branch = prefs.getBranch('html5.');
branch.setBoolPref('enable', value);
prefs.savePrefFile(null);
}

ly
this.unregister();
}
},

on
onUninstalling: function(addon) {
if (addon.id == MY_EXTENSION_UUID) {
this.beingUninstalled = true;
}
},

se
onOperationCancelled: function(addon) {
if (addon.id == MY_EXTENSION_UUID) {
this.beingUninstalled = (addon.pendingOperations & AddonManager.PENDING_UNI
NSTALL) != 0;
}
lu
},
register: function()
{
a
dump("\nhtml5toggle:register ....");
var observerService = Components.classes["@mozilla.org/observer-service;1"].
nn

getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "quit-application-granted", false);
Components.utils.import("resource://gre/modules/AddonManager.jsm");
AddonManager.addAddonListener(this);
o

},
unregister: function()
rs

{
dump("\nhtml5toggle:unregister ....");
var observerService = Components.classes["@mozilla.org/observer-service;1"].
pe

getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "quit-application-granted");
AddonManager.removeAddonListener(this);
}
};
r

window.addEventListener("load", function () { html5toggle.onLoad(); }, false);


Fo

Here is the same source file after being modified to use JavaScript code modules. It is from
HTML5toggle v1.03.

const MY_EXTENSION_UUID = "{16f32596-71ca-4053-a4f3-cfc8b157f541}";


const PREF_BRANCH_HTML5 = Services.prefs.getBranch("html5.");
const PREF_BRANCH_HTML5TOGGLE = Services.prefs.getBranch("extensions.html5toggle.");
const OBSERVER = Services.obs;
const ADDONMANAGER = AddonManager;
var html5toggle = {
beingUninstalled: null,
onLoad: function() {
let value = PREF_BRANCH_HTML5.getBoolPref("enable");
// save the current value of html5.enable preference in our own branch
PREF_BRANCH_HTML5TOGGLE.setBoolPref('enable', value);
Services.prefs.savePrefFile(null);

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 5/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

this.register();
this.setTBbutton(value);
this.setMIchecked(value);
},
setMIchecked: function (value) {
let menuitem = document.getElementById('html5toggle-menuitem');
if (value) {
menuitem.setAttribute("checked", "false");
} else {
menuitem.setAttribute("checked", "true");
}
},
setTBbutton: function (value) {
let tbButton = document.getElementById('html5toggle-toolbar-button');
let stringsBundle = document.getElementById('html5toggle-string-bundle');
if (tbButton) { // button might still be on Toolbar Palette

ly
if (value) {
tbButton.tooltipText = stringsBundle.getString('onString');
tbButton.setAttribute("image","chrome://html5toggle/skin/images/HTML5on16N.png"

on
);
} else {
tbButton.tooltipText = stringsBundle.getString('offString');
tbButton.setAttribute("image","chrome://html5toggle/skin/images/HTML5off16N.pn
g");

se
}
}
},
onToolbarButton: function(e) {
lu
let value = PREF_BRANCH_HTML5.getBoolPref("enable");
PREF_BRANCH_HTML5.setBoolPref("enable", !value);
this.setTBbutton(value);
this.setMIchecked(value);
a
},
onMenuItem: function(e) {
nn

let menuitem = document.getElementById('html5toggle-menuitem');


let value = menuitem.getAttribute("checked") == "true" ? false : true;
PREF_BRANCH_HTML5.setBoolPref("enable", value);
this.setTBbutton(value);
o

},
observe: function(subject, topic, data)
rs

{
if (topic == "quit-application-granted") {
dump("\nhtml5toggle:quit-application-granted ....");
pe

if (this.beingUninstalled) {
// restore initial value of html5.enable preference
let value = PREF_BRANCH_HTML5TOGGLE.getBoolPref("enable");
PREF_BRANCH_HTML5TOGGLE.deleteBranch("");
PREF_BRANCH_HTML5.setBoolPref('enable', value);
r

Service.prefs.savePrefFile(null);
Fo

}
this.unregister();
}
},
onUninstalling: function(addon) {
if (addon.id == MY_EXTENSION_UUID) {
this.beingUninstalled = true;
}
},
onOperationCancelled: function(addon) {
if (addon.id == MY_EXTENSION_UUID) {
this.beingUninstalled = (addon.pendingOperations & AddonManager.PENDING_UNI
NSTALL) != 0;
}
},
register: function()
{
dump("\nhtml5toggle:register ....");
OBSERVER.addObserver(this, "quit-application-granted", false);

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 6/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

ADDONMANAGER.addAddonListener(this);
},
unregister: function()
{
dump("\nhtml5toggle:unregister ....");
OBSERVER.removeObserver(this, "quit-application-granted");
ADDONMANAGER.removeAddonListener(this);
}
};
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/AddonManager.jsm");
window.addEventListener("load", function () { html5toggle.onLoad(); }, false);

As you can see if you study the above source code, using JavaScript code modules reduces the
number of lines of code and makes the source code more readable. I assume you are familiar with

ly
JavaScript in particular and Mozilla add-ons in general if you are reading this post so I am not
going to try and explain the above code in any detail.

on
You should also have noticed that I updated the source code to use the JavaScript let keyword
instead of var where possible. This keyword was introduced in JavaScript 1.7 which is a Mozilla
only extension. Variables declared by let have local scope, i.e. their scope (visibility) is the block in

se
which they are defined as well as in any sub-blocks in which they are not redefined. Contrast that
with variables declared by var which have as their scope the entire enclosing function.
lu
Consider the following short JavaScript example:
a
var i = 0;
for ( let i = i; i < 2 ; i++ )
nn

print ( "> " + i );


print ( i );

If you used the var keyword in the for statement, the variable would be visible within the whole
o

function containing the loop. To reduce the visibility of the variable to just the scope of the for
rs

loop, we use the let keyword. Using the 0


>1
>2
pe

There is an enormous amount of useful functionality in the current set of JavaScript code modules.
r

You need to read the appropriate documentation for each code module to gain an understanding
Fo

of what is available in each module. Unfortunately the documentation is written in the usual terse
style adopted by the Mozilla developers and use case examples are scarce.

For example, if you examine the Services.jsm documentation page, you will notice a prompt
service accessor. This provides access to a number of useful methods including alert which shows
an alert dialog with an OK button. It works the same way as window.alert but accepts a title for
the dialog.

Components.utils.import("resource://gre/modules/Services.jsm");
....
Services.prompt.alert(null, "My blog", "Hello there, dear reader of my blog");

The current set of JavaScript code modules does not include string localization. I would like to see
somebody develop a good JavaScript code module for string localization as that would simplify the

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 7/8


Using JavaScript Code Modules in Firefox 4 Add-Ons

coding of restartless add-ons.

Well, that is all that your should need to know to get you started on updating your Firefox add-ons
to take advantage of JavaScript code modules. One thing to watch out for with JavaScript code
modules is that you do not break backwards compatibility if your add-on is expected to work with
both Firefox 3 and Firefox 4.Good luck!

P.S. The full source code for HTML5toggle versions 1.02 and 1.03 is available on my website.

ly
on
se
a lu
o nn
rs
pe
r
Fo

02-25-2011 Copyright 2004-2011 Finnbarr P. Murphy. All rights reserved. 8/8

You might also like