XPCOM changes in Gecko 2.0

Several changes that affect XPCOM component compatibility are taking place in Gecko 2. This article details those changes, and provides suggestions for how to update your code.

No more frozen interfaces

There are no longer any frozen interfaces; from now on, all interfaces are subject to change. Documentation will be updated as time allows to remove references to interfaces being "frozen" or "unfrozen."

Component registration

The way XPCOM components are registered changed in Gecko 2. Prior to Gecko 2, during component registration, all binary and JavaScript component files were loaded and called, asking them to register themselves. If you used XPCOMUtils.jsm, some of this was hidden from you, but it was still there.

Starting in Gecko 2, however, components are registered using manifest files, similarly to how chrome is registered. In fact, the same chrome manifest file will be used to register components.

All existing XPCOM components will need to be updated to support this. However, it's very easy to do, and you can actually support both types of registration for backward compatibility.

Component manifests

All component registration is now handled through manifest files. For extensions, this is the same chrome.manifest currently used to register chrome.

XPT files

The path of any XPT files must be listed explicitly in a manifest using an interfaces directive:

interfaces components/mycomponent.xpt

JavaScript components

The registration information for JavaScript components is no longer located in the component itself; instead, it's located in the manifest. The component is loaded only when the XPCOM component manager needs to create a component.

chrome.manifest:

# The {classID} here must match the classID in mycomponent.js
component {e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8} components/mycomponent.js
contract @foobar/mycomponent;1 {e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8}
category profile-after-change MyComponent @foobar/mycomponent;1

The JavaScript code no longer exports a NSGetModule() function. It now must export a NSGetFactory() function, which accepts a class ID (CID) as a parameter.

For example, in your component's JavaScript code :

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function myComponent() {
}
myComponent.prototype = {
  // this must match whatever is in chrome.manifest!
  classID: Components.ID("{e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8}"),
  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMyComponent]),
  /* nsIMyComponent implementation goes here */
  ...
};
// The following line is what XPCOM uses to create components. Each component prototype
// must have a .classID which is used to create it.
const NSGetFactory = XPCOMUtils.generateNSGetFactory([myComponent]);

A component may implement backwards compatibility with Gecko 1.9.2 by dynamically detecting which symbols are exported by XPCOMUtils.jsm and exporting the correct function:

/**
* XPCOMUtils.generateNSGetFactory was introduced in Mozilla 2 (Firefox 4, SeaMonkey 2.1).
* XPCOMUtils.generateNSGetModule was introduced in Mozilla 1.9 (Firefox 3.0).
*/
if (XPCOMUtils.generateNSGetFactory)
    var NSGetFactory = XPCOMUtils.generateNSGetFactory([myComponent]);
else
    var NSGetModule = XPCOMUtils.generateNSGetModule([myComponent]);

Binary components

Binary components must be listed explicitly in a manifest using a binary-component directive:

binary-component components/mycomponent.dll

C++ in the component must be changed: a binary component no longer exports a NSGetModule() function. Instead, it exports an NSModule data symbol which points to a mozilla::Module structure. For more information about the mozilla::Module structure, see the Module.h header file. For an up-to-date example of implementing a dynamic modules, see nsSampleModule.cpp.

Note that nsIGenericFactory.h has been removed. References to nsIGenericFactory.h should be replaced with mozilla/ModuleUtils.h.

It is possible for a binary component to be compatible with Mozilla 1.9.2 and Mozilla 2.0 by using the extra macro NS_IMPL_MOZILLA192_NSGETMODULE. See nsSampleModule.cpp for more details.

Note: Binary XPCOM components must be recompiled for every new major release of Firefox starting with Firefox 4. Your life would be much easier over the long term if you switch to using js-ctypes instead.

Also note that extensions using binary components must now use the unpack property in the install manifest.

Platform-specific directories

The component/chrome system used to look in platform-specific subdirectories of an extension, such as platform/WINNT_x86-msvc/chrome.manifest on Windows. This is no longer supported. You can use the OS and ABI chrome registration directives to achieve the same effect:

binary-component components/windows/mycomponent.dll ABI=WINNT_x86-msvc
binary-component components/mac/mycomponent.dylib ABI=Darwin_x86-gcc3
binary-component components/mac/mycomponent64.dylib ABI=Darwin_x86_64-gcc3
binary-component components/linux/mycomponent.so ABI=Linux_x86-gcc3

This also means that platform-specific preferences are no longer possible. If you need to adjust default preferences based on platform, you can do so at first run by looking up what platform you're on and changing the preferences at that time.

Category registration

Prior to Gecko 2, extensions could listen for the xpcom-startup and app-startup notifications during startup, and perform actions during those. This is no longer the case. The earliest startup notification extensions can receive now is profile-after-change, which has always been the recommended notification to observe. That's because it's among the earliest notifications that occurs after the profile folder (and therefore preferences and other services) is available.

What you need to change

If your extension currently observes either xpcom-startup or app-startup, you need to update your code to observe profile-after-change instead.

Typically, extensions observed app-startup because in the past, you needed to load for app-startup in order to be able to register to observe profile-after-change in the first place. As of Gecko 1.9.1, this is no longer the case, however; you can now register for profile-after-change using the Category Manager. See Receiving startup notifications for details.

To add a category entry, you must insert the following line to your chrome.manifest:

category profile-after-change MyComponent @foobar/mycomponent;1 
Important: Formerly, the contract id of the category entry was prefixed with "service," if the component was implemented as a service. This prefix needs to be dropped when migrating to chrome.manifest.

Changed category names

The XPCOM category manager is used to register certain global helper objects. Because chrome.manifest is a space-delimited format, category names with spaces cannot be registered. Therefore the following categories have changed:

Old name New name
JavaScript global constructor JavaScript-global-constructor
JavaScript global constructor prototype alias JavaScript-global-constructor-prototype-alias
JavaScript global property JavaScript-global-property
JavaScript global privileged property JavaScript-global-privileged-property
JavaScript global static nameset JavaScript-global-static-nameset
JavaScript global dynamic nameset JavaScript-global-dynamic-nameset
JavaScript DOM class JavaScript-DOM-class
JavaScript DOM interface JavaScript-DOM-interface
XSLT extension functions XSLT-extension-functions

But why?

Previously, whenever Gecko detected that the application version had changed, or one or more extensions was added, removed, enabled, or disabled, it was necessary to throw away all existing component registrations, then restart the application (what we call the "Extension Manager restart") during its startup process. This was necessary in order to ensure that any components that should no longer be available are disposed of properly, and to re-register everything, loading any new components that may be needed.

In theory, this is invisible to the user, but it's a costly process, since every component needs to be loaded and executed, then unloaded, then reloaded again during the restart.

On top of that, with the ongoing work to make Firefox multithreaded, content processes either need to register components on a per-process basis, or somehow share a component cache with the chrome process.

The changes to the component registration model let this so-called Extension Manager restart become a thing of the past. Instead of relying on a potentially stale component cache on startup, we read the application's component registrations out of its manifest file and load those components. This gets enough of XPCOM loaded and running that we can then load the Extension Manager and perform the necessary installing, uninstalling, and updating of any installed extensions.

Once that's done, the extensions can then be loaded by simply reading their manifests, loading their components, and continuing the startup process, all without having to restart the browser.

Electrolysis content processes can simply read the component registrations during startup.

XPCNativeWrapper changes

You can't disable XPCNativeWrappers from your manifest

Specifying xpcnativewrappers=no in your manifest (that is, XPCNativeWrapper automation) is no longer supported. This was always intended to be a short-term workaround to allow extensions to continue to work while their authors updated their code to use XPCNativeWrappers.

If your add-on depends upon XBL bindings attached to content objects—for example, the ability to call functions or get and set properties created by the XBL binding—you will need to use the XPCNativeWrapper property wrappedJSObject to access wrapped objects.

If you need to be able to call functions or access properties defined by web content, you'll need to do this as well. This may be the case if, for example, you've written an extension that adds a delete button to a web mail service, and the service defines a window.delete() function that you need to call.

If, on the other hand, all you're doing with content is accessing DOM methods and properties, you've never needed to be using xpcnativewrappers=no in the first place, and should simply remove it from your manifest.

Miscellaneous XPCNativeWrapper changes

  • Using the delete operator on "expando" properties of an XPCNativeWrapper no longer throws a security exception.

XPCOMUtils.jsm changes

The XPCOMUtils.jsm code module has been updated to let you specify the application IDs of the applications you wish to register your component in.

XPCOM service getters

A number of commonly used XPCOM services now have service getter functions available in the mozilla::services namespace; these make it much easier to get access to these services from C++ code.

See also

Document Tags and Contributors

Tags: 
 Last updated by: Sheppy,