The XPCOMUtils.jsm
JavaScript code module offers utility routines for JavaScript components loaded by the JavaScript component loader.
To use this, you first need to import the code module into your JavaScript scope:
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Exposing a JavaScript class as a component using these utility methods requires four key steps:
XPCOMUtils.jsm
, as explained previously.NSGetFactory()
or NSGetModule()
entry point.This section provides some pseudocode that demonstrates how to put together a JavaScript class based on the steps listed above.
The constructor is a simple method that handles any required initialization tasks.
function MyComponent() { // initialize the component here }
Declare the class prototype, using a form similar to this.
MyComponent.prototype = { // properties required for XPCOM registration: classDescription: "unique text description", classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"), contractID: "@example.com/xxx;1", // [optional] custom factory (an object implementing nsIFactory). If not // provided, the default factory is used, which returns // |(new MyComponent()).QueryInterface(iid)| in its createInstance(). _xpcom_factory: { ... }, // [optional] an array of categories to register this component in. _xpcom_categories: [{ // Each object in the array specifies the parameters to pass to // nsICategoryManager.addCategoryEntry(). 'true' is passed for // both aPersist and aReplace params. category: "some-category", // optional, defaults to the object's classDescription entry: "entry name", // optional, defaults to the object's contractID (unless // 'service' is specified) value: "...", // optional, defaults to false. When set to true, and only if 'value' // is not specified, the concatenation of the string "service," and the // object's contractID is passed as aValue parameter of addCategoryEntry. service: true, // optional array of applications' IDs in the form: // [ "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", ... ] // If this is defined, the component is registered in this // category only on the specified applications. apps: [ ... ] }], // QueryInterface implementation, e.g. using the generateQI helper QueryInterface: XPCOMUtils.generateQI( [Components.interfaces.nsIObserver, Components.interfaces.nsIMyInterface, "nsIFoo", "nsIBar" ]), // [optional] classInfo implementation, e.g. using the generateCI helper. // Will be automatically returned from QueryInterface if that was // generated with the generateQI helper. classInfo: XPCOMUtils.generateCI( {classID: Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"), contractID: "@example.com/xxx;1", classDescription: "unique text description", interfaces: [Components.interfaces.nsIObserver, Components.interfaces.nsIMyInterface, "nsIFoo", "nsIBar"], flags: Ci.nsIClassInfo.SINGLETON}), // ...component implementation... };
Note: The ability to register the component in a category only on specific applications by adding the apps field to a category entry was added in Gecko 2.
Notice that the QueryInterface()
method implemented by the component simply calls the generateQI()
method provided by the XPCOMUtils code module.
You need to create an array that lists the constructors for each component. This array can of course have just one entry:
var components = [MyComponent];
Here, we're calling the array components
.
Finally, you need to implement the NSGetModule()
entry point so Gecko can start up your component:
// "components" is the array created in the previous section if ("generateNSGetFactory" in XPCOMUtils) var NSGetFactory = XPCOMUtils.generateNSGetFactory(components); // Gecko 2.0+ else var NSGetModule = XPCOMUtils.generateNSGetModule(components); // Gecko 1.9.x
function defineLazyGetter(aObject, aName, aLambda); |
function defineLazyModuleGetter(aObject, aName, aResource, [optional] aSymbol); |
function defineLazyServiceGetter(aObject, aName, aContract, aInterfaceName); |
function generateNSGetFactory(componentsArray); |
function generateCI(classInfo); |
function generateQI(interfaces); |
void importRelative(that, path, scope); |
generator IterSimpleEnumerator(enumerator, interface); |
generator IterStringEnumerator(enumerator); |
Attribute | Type | Description |
categoryManager |
nsICategoryManager |
Returns a reference to nsICategoryManager . |
Getter functions in JavaScript give you a way to define a property of an object, but not calculate the property's value until it is accessed. A getter defers the cost of calculating the value until the value is needed, and if it is never needed, you never pay the cost.
A "lazy getter" provides an additional optimization: the value is calculated the first time the getter is called, and is then cached (or memoized), so subsequent accesses return the cached value without recalculating it.
This means that you shouldn't use a lazy getter for a property whose value you expect to change, because the getter will not recalculate the value.
defineLazyGetter
takes three arguments:
Example for this is seen at bottom of this page here.
function defineLazyGetter(
aObject,
aName,
aLambda
);
aObject
aName
aLambda
this
will reference aObject
during execution of the function.Defines a getter on a specified object for a module. The module will not be imported until first use.
function defineLazyModuleGetter(
aObject,
aName,
aResource,
aSymbol
);
aObject
aName
aObject
for the module.aResource
aSymbol
aName
.Defines a function on a specified object which acts as a getter for a service. The service isn't obtained until the first time it's used.
function defineLazyServiceGetter( aObject, aName, aContract, aInterfaceName );
aObject
aName
aContract
aInterfaceName
Generates the NSGetFactory()
function along with the factory definition.
Function generateNSGetFactory( componentsArray );
componentsArray
A function that will return the factory for the components and can be assigned to NSGetFactory
global variable.
Generates an nsIClassInfo
implementation for a component. The returned object should be assigned to the classInfo
property of a JS object, the QueryInterface()
function generated by generateQI
will return it automatically then.
function generateCI( classInfo );
interfaces
, contractID
, classDescription
, classID
, flags
. This parameter should not be the component itself because that would cause a memory leak.An nsIClassInfo
implementation returning the values of the properties from the classInfo
parameter in its various properties.
This method throws an exception with the message "In generateCI, don't use a component for generating classInfo" if classInfo
parameter is an XPCOM component.
Generates a QueryInterface()
function implementation. You need to assign the returned function to the QueryInterface
property of a JavaScript object.
When the generated method is invoked on that object, it checks to see if the specified IID is listed in the array specified by the interfaces
parameter; if it is, this
(that is, the object itself) is returned. Otherwise, null
is returned.
function generateQI( interfaces );
A QueryInterface()
function implementation.
When you implement an interface that inherits from another one, you should generally list all the base interfaces explicitly, except for nsISupports
. For example, if your component implements nsIStreamConverter
:
MyComponent.prototype = { QueryInterface: XPCOMUtils.generateQI([ Components.interfaces.nsIRequestObserver, Components.interfaces.nsIStreamListener, Components.interfaces.nsIStreamConverter, ]), // ...methods... }
Imports a JavaScript code module given the calling JavaScript code module's global object (you should specify this
) and a path relative to that module. This lets modules bundled in a package load one another without having to hard-code full paths.
void importRelative( that, path, scope );
that
this
.path
scope
that
parameter is used.This lets an extension bundle its own JavaScript code modules within itself and have them load one another. For example, if an extension named "MyExtension" bundles foo.jsm
and bar.jsm
, and foo.jsm needs to load bar.jsm
, it can do so like this:
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.importRelative(this, "bar.jsm");
In other words: importRelative
will only work from other code modules (such as JSM files). It will NOT work from overlay scripts or bootstrap.js
or etc. Details can be found here: bug 628669
Wraps an nsISimpleEnumerator
instance into a JavaScript generator that can be easily iterated over.
generator IterSimpleEnumerator( enumerator, interface );
enumerator
nsISimpleEnumerator
instance to iterate over.interface
A generator yielding enumerated objects.
const nsIFile = Components.interfaces.nsIFile; for (var file in XPCOMUtils.IterSimpleEnumerator(dir.directoryEntries, nsIFile)) console.log(file.path);
Wraps an nsIStringEnumerator
or nsIUTF8StringEnumerator
instance into a JavaScript generator that can be easily iterated over.
generator IterStringEnumerator( enumerator );
enumerator
nsIStringEnumerator
or nsIUTF8StringEnumerator
instance to iterate over.A generator yielding enumerated strings.
for (var section in XPCOMUtils.IterStringEnumerator(iniParser.getSections())) console.log(section);
The post-registration callback called by generateModule()
should have the following signature:
postRegister( nsIComponentManager compMgr, nsIFile fileSpec, componentsArray );
compMgr
nsIComponentManager
instance to use for managing the component.fileSpec
nsIFile
instance for... what?componentsArray
generateModule()
.The function doesn't need to return a value.
The pre-unregistration callback passed to generateModule()
should have the following signature:
preUnregister( nsIComponentManager compMgr, nsIFile fileSpec, componentsArray );
compMgr
nsIComponentManager
instance to use for managing the component.fileSpec
nsIFile
instance for... what?componentsArray
generateModule()
.This function doesn't need to return a value.
var myServices = {}; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); //set it up XPCOMUtils.defineLazyGetter(myServices, 'as', function () { return Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService) }); //when you need to use it myServices.as.showAlertNotification('chrome://branding/content/icon64.png', 'this was lazyloaded', 'this is a notification from myServices.as', null, null);