Adding Services to Your Firefox Add-on

In last week’s Firefox post, I mentioned Services.jsm. Services.jsm is a Javascript code module that was added to Firefox 4. It’s a great idea, but unfortunately it only works in Firefox 4.

Luckily it’s really easy to create your own Services module to work on earlier versions of Firefox (even Firefox 3.0).

The intent of Services.jsm is to provide an easy way for you to access common services. A lot of add-ons grab services as needed or put something like this at the top of their files:

const gPrefService = Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch);

With the Services.jsm in Firefox 4, we access the pref service directly like this:

Services.prefs.getCharPref("prefname")'

Using Services.jsm gives us more benefits than just ease of use:

  • We don’t load a service until it’s needed
  • Faster loading of our add-on
  • We reuse the same instance of a service across our entire add-on
  • Our code is much more readable

Creating our own services modules gives us even more benefits:

  • We can add services that we use that aren’t in the system Services.jsm
  • We can provide easy access to our add-ons preferences

So how does Services.jsm work? Services.jsm uses two APIs from XPCOMUtils.jsm to accomplish its magic, defineLazyGetter and defineLazyServiceGetter. These two APIs do lazy loading of objects and services.

To create your own Services code module, just copy the code from Services.jsm and take only the services that you need for your app. You can also add services that you need that aren’t in the main services file. You’ll want to change the name of the file and the name in the file to something other than Services. I use MyAddonServices.

So what other interesting things can you do besides just copying the code in Services.jsm? You can have an easy way to access your add-ons preferences:

defineLazyGetter(MyAddonServices, "myaddonprefs", function () {
  return Components.classes["@mozilla.org/preferences-service;1"]
           .getService(Components.interfaces.nsIPrefService)
           .getBranch("extensions.myaddon.")
           .QueryInterface(Components.interfaces.nsIPrefBranch2);
});

This function allows me to access my preferences as

MyAddonServices.myaddonprefs.getCharPref

Also, because I’ve QIed it to an nsIPrefBranch2, I can add a pref observer directly as:

MyAddonServices.myaddonprefs.addObserver("", MyAddon, false);

I mentioned at the beginning that I was even able to make this work on Firefox 3.0. To do this, you need to take the two functions defineLazyGetter and defineLazyServiceGetter and move them into your services file as local functions. Then change any reference like XPCOMUtils.defineLazyServiceGetter to call your function directly. With that change, your services module will work all the way back to Firefox 3.0.

Can you think of other cool things can you do with your own Services code module?

Please note: I reserve the right to delete comments that are offensive or off-topic.

Leave a Reply

Your email address will not be published. Required fields are marked *

4 thoughts on “Adding Services to Your Firefox Add-on

  1. Thanks for the article

    There is a typo:
    .QueryInterface(omponents.interfaces.nsIPrefBranch2);
    should be
    .QueryInterface(components.interfaces.nsIPrefBranch2);

  2. I tried to implement a customized Service.jsm for myself, and I encounter to a problem. Here is the story, It seems that some services can only be initiated once, like this one “@mozilla.org/network/file-input-stream;1”. Consequently if you happen to define them with defineLazyServiceGetter, you will get error on the second call (If you try to initiate it once more)!

    And one question: Do you suggest to use this method to define almost all services? I mean I can move all my service calls to a single Service.jsm file and initiate them with defineLazyServiceGetter. Is this suppose to work better?

    • There’s a difference between getting a reference to a service and creating a service. I’m pretty sure in the file input stream case you’re creating a service, so that method won’t work

      I would suggest that you move every access to a global service to use this method. It should make startup of your add-on faster because you’re not getting a ton of services at startup before you need them.

    • Service is always a singleton. So it can only have only one instance all the time, So it can not create twice. You can read some materials on Design Pattern on Singleton.