How to Build an XPCOM Component in Javascript
From MDC
这是一个在javascript中构建XPCOM组件"hello world"的教程。这个教程不会描述XPCOM是怎么工作或者为什么那么工作,也不打算对每一部分代码进行解释。这个会在其他文章里面祥述elsewhere. 这个教程展示给你用最少的步骤来完成一个能工作的组件的过程。
Caveat: This was done on a Mac. YMMV with Windows.
目录 |
[编辑] 实现
这个组件将会公开一个方法,他返回"Hello World!".
[编辑] 定义接口
如果你想在JavaScript中用你的接口, 或者从其他 XPCOM components中调用, 你要定义一个公开接口 (如果你的组件仅仅用在Javascript中, 你可以使用 wrappedJSObject 技巧来避免这里所建立的接口. 参看 here 上的例子).
Mozilla应用中已经定义了很多接口,你可能不必再定义一个. 你可以浏览现有的Mozilla源代码里面的XPCOM接口,或者使用XPCOMViewer, 他是一个浏览注册接口和组件的GUI工具. 你可以下载旧的跟Firefox 1.5匹配的viewer,在mozdev mirrors.
如果一个现存的接口满足你的要求,那你就不需要再写一个IDL, 编译typelib, 可以直接跳过下一节 next section.
如果你没有发现现有的合适的接口,那你就要定义一个自己的。XPCOM使用一个IDL的修订版来定义接口, 叫做XPIDL. 这里有一个XPIDL对HelloWorld组件的定义:
HelloWorld.idl
#include "nsISupports.idl"
[scriptable, uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]
interface nsIHelloWorld : nsISupports
{
string hello();
};
注意你要为每一个XPCOM组件建立一个UUID。参看Generating GUIDs。
[编辑] 编译 Typelib
你的接口定义必须编译成为二进制(XPT)以便于注册和在Mozilla应用中使用。编译可以利用Gecko SDK. Windows and Linux versions 的SDK可以从wiki.mozilla.org获取. 我编译了一个Mac version的SDK (1.8 branch, OS X PPC 10.4), 暂时放在 here.
执行下面的命令来编译typelib. 其中, {sdk_dir}是你放置Gecko SDK的目录.
{sdk_dir}/bin/xpidl -m typelib -w -v -I {sdk_dir}/idl -e HelloWorld.xpt HelloWorld.idl
这将会在当前目录建立一个typelib文件HelloWorld.xpt.
[编辑] 建立组件
HelloWorld.js
/***********************************************************
constants
***********************************************************/
// reference to the interface defined in nsIHelloWorld.idl
const nsIHelloWorld = Components.interfaces.nsIHelloWorld;
// reference to the required base interface that all components must support
const nsISupports = Components.interfaces.nsISupports;
// UUID uniquely identifying our component
// You can get from: http://kruithof.xs4all.nl/uuid/uuidgen here
const CLASS_ID = Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}");
// description
const CLASS_NAME = "My Hello World Javascript XPCOM Component";
// textual unique identifier
const CONTRACT_ID = "@dietrich.ganx4.com/helloworld;1";
/***********************************************************
class definition
***********************************************************/
//class constructor
function HelloWorld() {
};
// class definition
HelloWorld.prototype = {
// define the function we want to expose in our interface
hello: function() {
return "Hello World!";
},
QueryInterface: function(aIID)
{
if (!aIID.equals(nsIHelloWorld) &&
!aIID.equals(nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
/***********************************************************
class factory
This object is a member of the global-scope Components.classes.
It is keyed off of the contract ID. Eg:
myHelloWorld = Components.classes["@dietrich.ganx4.com/helloworld;1"].
createInstance(Components.interfaces.nsIHelloWorld);
***********************************************************/
var HelloWorldFactory = {
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return (new HelloWorld()).QueryInterface(aIID);
}
};
/***********************************************************
module definition (xpcom registration)
***********************************************************/
var HelloWorldModule = {
_firstTime: true,
registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
{
aCompMgr = aCompMgr.
QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME,
CONTRACT_ID, aFileSpec, aLocation, aType);
},
unregisterSelf: function(aCompMgr, aLocation, aType)
{
aCompMgr = aCompMgr.
QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
},
getClassObject: function(aCompMgr, aCID, aIID)
{
if (!aIID.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
if (aCID.equals(CLASS_ID))
return HelloWorldFactory;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
canUnload: function(aCompMgr) { return true; }
};
/***********************************************************
module initialization
When the application registers the component, this function
is called.
***********************************************************/
function NSGetModule(aCompMgr, aFileSpec) { return HelloWorldModule; }
[编辑] 安装
[编辑] For extensions:
- Copy HelloWorld.js and HelloWorld.xpt to {extensiondir}/components/
- Delete compreg.dat and xpti.dat from your profile directory.
- Restart application
[编辑] For Firefox
- Copy HelloWorld.js and HelloWorld.xpt to the {objdir}/dist/bin/components directory, if running from the source.
- Delete compreg.dat and xpti.dat from the components directory.
- Delete compreg.dat and xpti.dat from your profile directory.
- Restart application
[编辑] 使用你的组件
try {
// this is needed to generally allow usage of components in javascript
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var myComponent = Components.classes['@dietrich.ganx4.com/helloworld;1']
.createInstance(Components.interfaces.nsIHelloWorld);
alert(myComponent.hello());
} catch (anError) {
alert("ERROR: " + anError);
}
[编辑] Other resources
- Two mozillazine forums threads about implementing XPCOM components in JS with some explanations, example code, and troubleshooting tips:
- Implementing XPCOM components in JavaScript at kb.mozillazine.org
- Using XPCOM in JavaScript without leaking - A must-read.
- An example component
- Older JS+XPCOM notes - includes some wrappedJSObject information.
页面分类: Extensions | XPCOM