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.
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
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.