How to implement xpcom module in JavaScript

来源:互联网 发布:广东网络电视营业厅 编辑:程序博客网 时间:2024/05/23 01:14


Jump to: navigation, search

Contents

  • 1Introduction
  • 2Implement the Extension Step by Step
    • 2.1Define Interface
    • 2.2Declare manifiest
    • 2.3The Source code
    • 2.4Make file for the module
    • 2.5All Trivial Modifications
    • 2.6Access your extension in App
  • 3Performance Tip

Introduction

If we intend to access some functions which are not available in the default DOM. The only way to make things work is we "extend" the Gecko/DOM by ourselves.

Javascript is considered as a most convenient way to implement DOM API quickly. However, it also brings some limitation with JS modules. Comparing to C++ module, which can talk to Linux directly, JS module is only allowed to talk to some other "xpcom modules". Thanks to Mozilla, now there're already many xpcom module available, which enable you to manager process, thread, system files, network and so on.

Implement the Extension Step by Step

Define Interface

The interface is defined as <<xxxx.idl>>, which is written in Mozilla xpidl language.

#include "domstubs.idl"#include "nsIDOMDOMRequest.idl" [scriptable, uuid(0c2b8ae8-ad6c-49d7-85d4-1eb626cd0284)]interface jrdIDOMExtension: nsISupports{  /*To get system information. Now we support the parameter of:   * "MAC_ADDR" for wifi mac address    * "BATTERY_TEMP" for battery temeratur   */  nsIDOMDOMRequest getSysInfo(in string parameter);  ......}

The DOMRequest is always considered as the most popular way to convey the information from Gecko to Gaia, even we have some other choices. (Like, let Gaia set an attribute as callback function, and let Gecko trigger it when the process is finished)

Note: the uuid of each module in xpcom should be different. Please do not copy it from other module to avoid collision. Using uuidgen (in Linux) to generate a new one every time you write a module. uuidgen would give very low possibility of collision of id.

Declare manifiest

Compose a manifest file as <<xxx.manifest>> in the same level as IDL file. The content of manifest is fairly simple.

It builds the connection between uuid, js file and navigator attribute we want to expose. By referencing navigator.jrdExtension, we eventually reach JrdExtension.js

component {bad6c492-42cc-4080-89bf-de2f5e3bf6b8} JrdExtension.jscontract @jrdcom.com/extension;1 {bad6c492-42cc-4080-89bf-de2f5e3bf6b8}category JavaScript-navigator-property jrdExtension @jrdcom.com/extension;1

The Source code

This is the implementation of the this component. It need to realize all the interfaces defined in nsIDOMExample.idl.

  • Copy code from the example <<gecko/dom/jrd/JrdExtension.js>>.
  • Replace the all interface functions and private functions to yours while keeping the nsIDOMGlobalPropertyInitializer implementation part.
  • Modify remaining code carefully to avoid name mismatching.

Here's the simplest version, without any interface implemented. You might reference to <<JrdExtension.js>> for some more practical implementation.

'use strict'; //static functionslet DEBUG = 0;if (DEBUG)  var debug = function(s) { dump('<TZM_LOG> -*- JrdExtension -*-: ' + s + '\n'); };else  var debug = function(s) {}; const Cc = Components.classes;const Ci = Components.interfaces;const Cu = Components.utils; //Include all the jsm requiredCu.import('resource://gre/modules/XPCOMUtils.jsm');Cu.import('resource://gre/modules/Services.jsm');Cu.import('resource://gre/modules/DOMRequestHelper.jsm');Cu.import('resource://gre/modules/FileUtils.jsm'); function jrdIDOMExtension() {} jrdIDOMExtension.prototype = {   __proto__: DOMRequestIpcHelper.prototype,   //For XPCOM/XPCONNECT  classDescription: 'The JRD extension for DOM',  classID: Components.ID('{bad6c492-42cc-4080-89bf-de2f5e3bf6b8}'),  contractID: '@jrdcom.com/extension;1',  classInfo: XPCOMUtils.generateCI(              {classID: Components.ID('{bad6c492-42cc-4080-89bf-de2f5e3bf6b8}'),              ontractID: '@jrdcom.com/extension;1',              classDescription: 'JRDExtension',              interfaces: [Ci.jrdIDOMExtension],              flags: Ci.nsIClassInfo.DOM_OBJECT}),  QueryInterface: XPCOMUtils.generateQI([Ci.jrdIDOMExtension, Ci.nsIDOMGlobalPropertyInitializer]),   init: function(aWindow){}} var components = [jrdIDOMExtension];if ('generateNSGetFactory' in XPCOMUtils)  this.NSGetFactory =    XPCOMUtils.generateNSGetFactory(components);  // Firefox 4.0 and higherelse  this.NSGetModule =    XPCOMUtils.generateNSGetModule(components);    // Firefox 3.x

Make file for the module

We should compose a new make file <<Makefile.in>> in the same level as manifest file, in which specify the position/name of your source code goes into building process.


The interesting fields we should care are:

LIBRARY_NAME: Give an arbitrary name to your module, it will become the identification of the module in xpcom

XPIDL_MODULE: Give an arbitrary name to your IDL, it will become the identification of this IDL in xpcom

XPIDLSRCS: List the IDL file here

EXTRA_COMPONENTS: List all js source code files here

EXTRA_JS_MODULES: Fill in the js module here (if any).

DEPTH= @DEPTH@topsrcdir= @top_srcdir@srcdir= @srcdir@VPATH            = \  $(srcdir)        \  $(NULL)include $(DEPTH)/config/autoconf.mkMODULE         = domLIBRARY_NAME   = jsjrd_sLIBXUL_LIBRARY = 1XPIDL_MODULE   = dom_jrdGRE_MODULE     = 1XPIDLSRCS = \  jrdIDOMExtension.idl \  $(NULL)EXTRA_COMPONENTS =        \  JrdExtension.js       \  jrdExtension.manifest \  $(NULL)EXTRA_JS_MODULES =   \  $(NULL)# Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend# subdirectory (and the ipc one).include $(topsrcdir)/config/config.mkinclude $(topsrcdir)/ipc/chromium/chromium-config.mkinclude $(topsrcdir)/config/rules.mkDEFINES += -D_IMPL_NS_LAYOUTifeq (gonk,$(MOZ_WIDGET_TOOLKIT))# extra options to compile nv.h# RQ : (u)int32/16/8 will be defined a PR(u)int32DEFINES += -D_UINT32_DEFINED -D_UINT16_DEFINED -D_UINT8_DEFINEDDEFINES += -D_INT32_DEFINED -D_INT16_DEFINED -D_INT8_DEFINEDDEFINES += -DPACKED=__packed -DPACKED_POST=LOCAL_INCLUDES += \-I$(topsrcdir)/../vendor/qcom/proprietary/oncrpc/inc \-I$(topsrcdir)/../vendor/qcom/proprietary/oncrpc/inc/oncrpc \-I$(topsrcdir)/../vendor/qcom/proprietary/common/inc \-I$(DIST)/../../include/nv/inc/ \$(NULL)endif

All Trivial Modifications

Now we have nearly complete all the code part. Before we start a rebuild, there are still some small modifications need to apply for some files. This is mandatory in Mozilla's design. Please be very careful here because no specific error message will be prompt if we make some mistake here, but it will lead to your module doesn't work!

Be aware that the name of the idl and module should be same as you specified in the make file before.

diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.inindex 94ea33d..c1e2852 100644--- a/browser/installer/package-manifest.in+++ b/browser/installer/package-manifest.in@@ -165,6 +165,7 @@ @BINPATH@/components/dom_apps.xpt @BINPATH@/components/dom_base.xpt @BINPATH@/components/dom_system.xpt+@BINPATH@/components/dom_jrd.xpt #ifdef MOZ_B2G_RIL @BINPATH@/components/dom_telephony.xpt @BINPATH@/components/dom_wifi.xpt@@ -500,6 +501,8 @@ @BINPATH@/components/PermissionSettings.manifest @BINPATH@/components/ContactManager.js @BINPATH@/components/ContactManager.manifest+@BINPATH@/components/JrdExtension.js+@BINPATH@/components/jrdExtension.manifest @BINPATH@/components/AlarmsManager.js @BINPATH@/components/AlarmsManager.manifest @BINPATH@/components/TCPSocket.js--- a/layout/build/Makefile.in+++ b/layout/build/Makefile.in@@ -73,6 +73,7 @@ SHARED_LIBRARY_LIBS = \ $(DEPTH)/dom/network/src/$(LIB_PREFIX)dom_network_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/media/$(LIB_PREFIX)dom_media_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/sms/src/$(LIB_PREFIX)dom_sms_s.$(LIB_SUFFIX) \+$(DEPTH)/dom/jrd/$(LIB_PREFIX)jsjrd_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/events/$(LIB_PREFIX)jsdomevents_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/json/$(LIB_PREFIX)json_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \@@ -290,6 +291,7 @@ LOCAL_INCLUDES+= -I$(srcdir)/../base \    -I$(topsrcdir)/view/src \    -I$(topsrcdir)/dom/base \    -I$(topsrcdir)/dom/file \+   -I$(topsrcdir)/dom/jrd \    -I$(topsrcdir)/dom/src/json \    -I$(topsrcdir)/dom/src/jsurl \    -I$(topsrcdir)/dom/src/storage \

Access your extension in App

After the gecko is rebuilt, we should be able to access our module in the application with

navigator.jrdExtension.function_name() ornavigator.jrdExtension.property_name

Performance Tip

According to Mozilla, the module written in Javascript is running in seperated context/compartment. There will be a considerable RAM cost (around several hundred kilobytes) for the JS interpreter comparing to c++ modules. Hence, we should not create a lot of JS modules which run permanently on the system in low-end devices.

0 0
原创粉丝点击