platform/xpcom
If all you need to do is use XPCOM objects that someone else has implemented,
then you don't need to use this module. You can just use require("chrome")
to get direct access to the
Components
object, and
access XPCOM objects from there.
The xpcom
module makes it simpler to perform three main tasks:
- Implement XPCOM object interfaces
- Implement and register XPCOM factories
- Implement and register XPCOM services
Implementing XPCOM Interfaces
"Unknown
" is named after the
"IUnknown
" interface in COM.
This module exports a class called Unknown
which implements the fundamental
XPCOM interface
nsISupports
.
By subclassing Unknown
, either using
standard JavaScript inheritance
or using the SDK's heritage
module, you can provide your own implementations of XPCOM interfaces.
For example, the add-on below implements the
nsIObserver
interface to listen for and log all topic notifications:
var { Class } = require('sdk/core/heritage');
var { Unknown } = require('sdk/platform/xpcom');
var { Cc, Ci } = require('chrome')
var observerService = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
var StarObserver = Class({
extends: Unknown,
interfaces: [ 'nsIObserver' ],
topic: '*',
register: function register() {
observerService.addObserver(this, this.topic, false);
},
unregister: function() {
addObserver.removeObserver(this, this.topic, false);
},
observe: function observe(subject, topic, data) {
console.log('star observer:', subject, topic, data);
}
});
var starobserver = StarObserver();
starobserver.register();
Implementing XPCOM Factories
The xpcom
module exports a class called Factory
which implements the
nsIFactory
interface. You can use this class to register factories for XPCOM
components you have defined.
For example, this add-on defines a subclass of Unknown
called
HelloWorld
that implements a function called hello
. By
creating a Factory
and passing it the
contract ID
for the HelloWorld
component and the HelloWorld
constructor, we enable
XPCOM clients to access the HelloWorld
component, given its contract ID.
In this example the HelloWorld
component is available to JavaScript only,
so we use the technique documented under the "Using wrappedJSObject"
section of
How to Build an XPCOM Component in JavaScript.
var { Class } = require('sdk/core/heritage');
var { Unknown, Factory } = require('sdk/platform/xpcom');
var { Cc, Ci } = require('chrome');
var contractId = '@me.org/helloworld';
// Define a component
var HelloWorld = Class({
extends: Unknown,
get wrappedJSObject() this,
hello: function() {return 'Hello World';}
});
// Create and register the factory
var factory = Factory({
contract: contractId,
Component: HelloWorld
});
// XPCOM clients can retrieve and use this new
// component in the normal way
var wrapper = Cc[contractId].createInstance(Ci.nsISupports);
var helloWorld = wrapper.wrappedJSObject;
console.log(helloWorld.hello());
Using class ID
You can specify a
class ID
for the factory by setting the id
option in the factory's constructor.
If you don't specify a class ID, then the factory will generate one.
Either way, it will be accessible as the value of the factory's id
property.
XPCOM users can look up the factory using the class ID instead of the contract ID. Here's the example above, rewritten to use class ID instead of contract ID for lookup:
var { Class } = require('sdk/core/heritage');
var { Unknown, Factory } = require('sdk/platform/xpcom');
var { Cc, Ci, components } = require('chrome');
// Define a component
var HelloWorld = Class({
extends: Unknown,
get wrappedJSObject() this,
hello: function() {return 'Hello World';}
});
// Create and register the factory
var factory = Factory({
Component: HelloWorld
});
var id = factory.id;
// Retrieve the factory by class ID
var wrapper = components.classesByID[id].createInstance(Ci.nsISupports);
var helloWorld = wrapper.wrappedJSObject;
console.log(helloWorld.hello());
Replacing Factories
If the factory you create has the same contract ID as an existing registered
factory, then your factory will replace the existing one. However, the
Components.classes
object commonly used to look up factories by contract ID
will not be updated at run time. To access the replacement factory you need
to do something like this:
var id = Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
contractIDToCID('@me.org/helloworld');
var wrapper = Components.classesByID[id].createInstance(Ci.nsISupports);
The xpcom
module exports a function factoryByContract
to simplify this
technique:
var wrapper = xpcom.factoryByContract('@me.org/helloworld').createInstance(Ci.nsISupports);
Registration
By default, factories are registered and unregistered automatically. To learn more about this, see Registering and Unregistering.
Implementing XPCOM Services
The xpcom
module exports a class called Service
which you can use to
define
XPCOM services,
making them available to all XPCOM users.
This example implements a logging service that just appends a timestamp
to all logged messages. The logger itself is implemented by subclassing
the Unknown
class, then we create a service which associates the logger's
constructor with its
contract ID.
After this, XPCOM users can access the service using the getService()
API:
var { Class } = require('sdk/core/heritage');
var { Unknown, Service } = require('sdk/platform/xpcom');
var { Cc, Ci } = require('chrome');
var contractId = '@me.org/timestampedlogger';
// Implement the service by subclassing Unknown
var TimeStampedLogger = Class({
extends: Unknown,
get wrappedJSObject() this,
log: function(message) {
console.log(new Date().getTime() + ' : ' + message);
}
});
// Register the service using the contract ID
var service = Service({
contract: contractId,
Component: TimeStampedLogger
});
// Access the service using getService()
var wrapper = Cc[contractId].getService(Ci.nsISupports);
var logger = wrapper.wrappedJSObject;
logger.log('a timestamped message');
By default, services are registered and unregistered automatically. To learn more about this, see Registering and Unregistering.
Registering and Unregistering
By default, factories and services are registered with XPCOM automatically when they are created, and unregistered automatically when the add-on that created them is unloaded.
You can override this behavior using the register
and unregister
options to the factory or service constructor:
var xpcom = require('sdk/platform/xpcom');
var factory = xpcom.Factory({
contract: contractId,
Component: HelloWorld,
register: false,
unregister: false,
});
If you disable automatic registration in this way, you can use the
register()
function to register factories and services:
xpcom.register(factory);
You can use the corresponding unregister()
function to unregister them,
whether or not you have disabled automatic unregistration:
xpcom.unregister(factory);
You can find out whether a factory or service has been registered by using the
isRegistered()
function:
if (xpcom.isRegistered(factory))
xpcom.unregister(factory);
API Reference
Classes
Unknown
This is the base class for all XPCOM objects. It is not intended to be used
directly but you can subclass it,
either using
standard JavaScript inheritance
or using the SDK's heritage
module, to create new
implementations of XPCOM interfaces. For example, this subclass implements the
nsIRequest
interface:
var { Class } = require('sdk/core/heritage');
var { Unknown } = require('sdk/platform/xpcom');
var Request = Class({
extends: Unknown,
interfaces: [ 'nsIRequest' ],
initialize: function initialize() {
this.pending = false;
},
isPending: function() { return this.pending; },
resume: function() { console.log('resuming...'); },
suspend: function() { console.log('suspending...'); },
cancel: function() { console.log('canceling...'); }
});
This component definition:
Although Request
also implements
nsISupports
, there is no need to add it here, because
the base class Unknown
declares support for nsISupports
and this
is accounted for when retrieving objects.
- specifies that we support
nsIRequest
using theinterfaces
property. - initializes
pending
ininitialize()
- adds our implementation of the
nsIRequest
interface
We can register a factory for this component by using the Factory
class to associate its constructor with its contract ID:
var { Factory } = require('sdk/platform/xpcom');
var { Cc, Ci } = require('chrome');
var contractId = '@me.org/request'
// Create and register the factory
var factory = Factory({
contract: contractId,
Component: Request
});
Now XPCOM users can access our implementation in the normal way:
var request = Cc[contractId].createInstance(Ci.nsIRequest);
request.resume();
Methods
QueryInterface(interface)
This method is called automatically by XPCOM, so usually you don't
need to call it yourself. It is passed an interface identifier and
searches for the identifier in the interfaces
property of:
- this object
- any of this object's ancestors
- any classes in the
implements
array property of the instance (for example, any classes added to this object via theimplements
option defined inheritage
).
If it finds a match, it returns this
, otherwise it throws
Components.results.NS_ERROR_NO_INTERFACE
.
The interface to ask for. This is typically given as a property of the
Components.interfaces
object.
The object itself(this
).
Properties
interfaces : array
The set of interfaces supported by this class. Unknown
sets this to
nsISupports
.
Factory
Use this class to register an XPCOM factory. To register a factory for
a component, construct a Factory
, giving it:
- a constructor for the component
- a contract ID and/or a class ID
// Create and register the factory
var factory = Factory({
contract: '@me.org/myComponent',
Component: MyComponent
});
- The component constructor typically returns a descendant of
Unknown
, although it may return a custom object that satisfies thensISupports
interface. - The contract ID and/or class ID may be used by XPCOM clients to retrieve
the factory. If a class ID is not given, a new one will be generated. The
contract ID and class ID are accessible as the values of the
contract
andid
properties, respectively. - By default, the factory is registered when it is created and
unregistered when the add-on that created it is unloaded. To override this
behavior, you can pass
register
and/orunregister
options, set tofalse
. If you do this, you can use theregister()
andunregister()
functions to register and unregister.
Constructors
Factory(options)
Constructor for the component this factory creates. This will typically
return a descendant of Unknown
, although it may return a custom object
that satisfies the nsISupports
interface.
A contract ID.
Users of XPCOM can use this value to retrieve the factory from
Components.classes
:
var factory = Components.classes['@me.org/request']
var component = factory.createInstance(Ci.nsIRequest);
This parameter is formally optional, but if you don't specify it, users
won't be able to retrieve your factory using a contract ID. If specified,
the contract ID is accessible as the value of the factory's contract
property.
A class ID.
Users of XPCOM can use this value to retrieve the factory using
Components.classesByID
:
var factory = components.classesByID[id];
var component = factory.createInstance(Ci.nsIRequest);
This parameter is optional. If you don't supply it, the factory constructor
will generate a fresh ID. Either way, it's accessible as the value of the
factory's id
property.
By default, the factory is registered as soon as it is constructed.
By including this option, set to false
, the factory is not automatically
registered and you must register it manually using the register()
function.
By default, the factory is unregistered as soon as the add-on which created it
is unloaded. By including this option, set to false
, the factory is not
automatically unregistered and you must unregister it manually using
the unregister()
function.
Methods
createInstance(outer, iid)
Creates an instance of the component associated with this factory.
This argument must be null
, or the function throws
Cr.NS_ERROR_NO_AGGREGATION
.
Interface identifier. These objects are usually accessed through
the Components.interfaces
, or Ci
, object. The methods of this
interface will be callable on the returned object.
If the object implements an interface that's already defined in XPCOM, you can pass that in here:
var about = aboutFactory.createInstance(null, Ci.nsIAboutModule);
// You can now access the nsIAboutModule interface of the 'about' object
If you will be getting the wrappedJSObject
property from the returned
object to access its JavaScript implementation, pass Ci.nsISupports
here:
var custom = factory.createInstance(null, Ci.nsISupports).wrappedJSObject;
// You can now access the interface defined for the 'custom' object
The component created by the factory.
lockFactory()
This method is required by the nsIFactory
interface, but as in most
implementations it does nothing interesting.
QueryInterface()
See the documentation for
Unknown.QueryInterface()
.
Properties
interfaces : array
The set of interfaces supported by this object. Factory
sets this to
nsIFactory
.
id : string
This factory's class ID.
contract : string
This factory's contract ID.
Service
Use this class to register an XPCOM service. To register a service for
a component, construct a Service
, giving it:
- a constructor for the object implementing the service
- a contract ID and/or a class ID
var service = Service({
contract: contractId,
Component: AlertService
});
After this, XPCOM users can access the service implementation by supplying the contract ID:
var alertService = Cc[contractId].getService(Ci.nsIAlertsService);
alertService.showAlertNotification(...);
The Service
interface is identical to the
Factory
interface, so refer to the
Factory
interface documentation
for details.
Functions
register(factory)
Register the factory or service supplied. If the factory or service
is already registered, this function throws
Components.results.NS_ERROR_FACTORY_EXISTS
.
By default, factories and services are registered automatically, so
you should only call register()
if you have overridden the default behavior.
The factory or service to register.
unregister(factory)
Unregister the factory or service supplied. If the factory or service is not registered, this function does nothing.
By default, factories and services are unregistered automatically when the add-on that registered them is unloaded.
The factory or service to unregister.
isRegistered(factory)
Find out whether a factory or service is registered.
True if the factory or service is registered, false otherwise.
autoRegister(path)
Register a component (.manifest) file or all component files in a directory.
See nsIComponentRegistrar.autoRegister()
for details.
Path to a component file to be registered or a directory containing component files to be registered.
factoryByID(id)
Given a
class ID,
this function returns the associated factory or service. If the factory or
service isn't registered, this function returns null
.
This function wraps Components.ClassesByID.
A class ID.
The factory or service identified by the class ID.
factoryByContract(contract)
Given a contract ID
this function returns the associated factory or service. If the factory
or service isn't registered, this function throws
Components.results.NS_ERROR_FACTORY_NOT_REGISTERED
.
This function is similar to the standard Components.classes[contractID]
with
one significant difference: that Components.classes
is not updated
at runtime.
So if a factory is registered with the contract ID "@me.org/myComponent", and another factory is already registered with that contract ID, then:
Components.classes["@me.org/myComponent"]
will return the old factory, while:
xpcom.factoryByContract("@me.org/myComponent")
will return the new one.
Contract ID of the factory or service to retrieve.
The factory or service identified by the contract ID.