How to Add an XPCOM Component in Javascript and Expose it to DOM
Contents
Introduction
This article is based on experience of programming in B2G environment. Whether it applies to firefox desktop edition has not been tested.
All directory structures are by default under gecko directory, if not specified.
Basic Procedure
Say the component name is "Example". Go through this basic procedure and at the end you should be able to see navigator.mozExample in DOM and access its attributes and methods. Then you can continue to add permission control and add test.
- Add code to the files listed below following example patches:
- b2g/installer/package-manifest.in
- browser/installer/package-manifest.in
- dom/Makefile.in
- dom/dom-config.mk
- layout/build/Makefile.in
- toolkit/toolkit-makefiles.sh
- Create dom/example/ as the new component's directory.
- Create these files under dom/example/:
- Makefile.in
- nsIDOMExample.idl
- Example.js
- Example.manifest
nsIDOMExample.idl
Define the IDL following the example patches. Here's a simplified one with just one attribute and one function.
#include "nsISupports.idl" [scriptable, uuid(<uuid>)] interface nsIDOMMozExample: nsISupports { readonly attribute DOMString test; DOMString hello(); };
Example.manifest
component <uuid> Example.js contract @mozilla.org/example;1 <uuid> category Javascript-navigator-property mozExample @mozilla.org/example;1
This builds the connection between uuid, js file and navigator attribute we want to expose. By referencing navigator.mozExample, we eventually reach Example.js
Example.js
This is the implementation of the this component. It need to realize all the interfaces defined in nsIDOMExample.idl.
- Copy code from the example patches.
- Replace the all interface functions and private functions to yours while keeping the nsIDOMGlobalPropertyInitializer implementation part.
- Empty the init() function.
- Modify remaining code carefully to avoid name mismatching.
Here's the simplest version.
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); function Example() { } Example.prototype = { classDescription: "My Example Javascript XPCOM Component", classID: Components.ID("{<uuid>}"), contractID: "@mozilla.org/example;1", QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIDOMMozExample]), get test() { return "This is a test."; }, hello: function() { return "Hello World!"; }, init: function(aWindow) {}, uninit: function unit() {} }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Example]);
Here's a more practical one. You can use it as a template.
"use strict"; const DEBUG = false; function debug(aStr) { if (DEBUG) dump("Example: " + aStr + "\n"); } const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); const EXAMPLE_CONTRACTID = "@mozilla.org/example;1"; const EXAMPLE_CID = Components.ID("{<uuid>}"); const nsIDOMMozExample = Ci.nsIDOMMozExample; const nsIClassInfo = Ci.nsIClassInfo; function Example() { debug("Constructor"); } Example.prototype = { __proto__: DOMRequestIpcHelper.prototype, classID : EXAMPLE_CID, QueryInterface : XPCOMUtils.generateQI([nsIDOMExample, Ci.nsIDOMGlobalPropertyInitializer]), classInfo : XPCOMUtils.generateCI({ classID: EXAMPLE_CID, contractID: EXAMPLE_CONTRACTID, classDescription: "Example", interfaces: [nsIDOMMozExample], flags: nsIClassInfo.DOM_OBJECT }), get test() { return "This is a test."; }, hello: function() { debug("hello"); return "Hello world!"; }, // nsIDOMGlobalPropertyInitializer implementation init: function(aWindow) { debug("init()"); }, // Called from DOMRequestIpcHelper. uninit: function() { debug("uninit()"); } } this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Example])
Makefile.in
Copy code from the example patches and remove test-related code. Here's a simplified version:
DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = dom XPIDL_MODULE = dom_example LIBRARY_NAME = domexample_s LIBXUL_LIBRARY = 1 FORCE_STATIC_LIB = 1 GRE_MODULE = 1 FAIL_ON_WARNINGS := 1 include $(topsrcdir)/dom/dom-config.mk EXPORTS_NAMESPACES = mozilla/dom/example EXTRA_COMPONENTS = \ Example.js \ Example.manifest \ $(NULL) XPIDLSRCS = \ nsIDOMExample.idl \ $(NULL) include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk XPIDL_FLAGS += \ -I$(topsrcdir)/dom/interfaces/base \ $(NULL)
Add Permission Control
TBW
Add Test
TBW
Resources
Documents
How to build an XPCOM component in Javascript
Adding APIs to the navigator object
B2G tutorial slides, 3_Gecko, How to add components to XPCOM