跨平台组件对象模型(XPCOM)是Mozilla的跨平台组件模型,类似于CORBA和Microsoft®COM。 它具有多种语言绑定和IDL描述,因此XPCOM组件可以在JavaScript,Java™语言,Python和C ++中使用和实现。 您可以将自定义函数插入框架,并将它们与其他组件连接。
XPCOM支持Windows®和Linux®平台。 在本文中,所有指令,环境变量和命令行都将在Linux操作系统中。
在本文中,学习如何:
- 定义XPCOM接口并写入定义文件。
- 为XPCOM组件实现按需行为。
- 与Firefox集成和测试XPCOM组件。
先决条件
要继续进行,您将需要:
- 下载 Gecko SDK软件包。 Gecko SDK,也称为XULRunner SDK,是一组XPIDL文件,具有标头和实用程序以开发XPCOM组件,然后可以使用JavaScript从XUL对其进行访问。
- 确保Linux系统上已安装Firefox浏览器。 您将在Firefox浏览器中集成并测试示例XPCOM。
定义XPCOM接口
XPCOM提供了浏览器应用程序界面,您可以使用它们来访问低级操作,例如系统硬件诊断,大量文件操作以及系统健康状况实时监视。 通常,XPCOM函数是用C / C ++编写的,接口作为动态库链接,JavaScript和C ++层之间的连接是XPConnect。 图1显示了浏览器和XPCOM之间的典型交互。
图1.浏览器和XPCOM之间的交互
在图1中,sample.js包含用于调用XPCOM接口JavaScript。 该代码使用足够的参数实现对XPCOM接口的调用,并在XPCOM完成任务时从中获取结果。
首先,您必须定义要向浏览器公开的界面或要提供给Web应用程序的功能。 接口以.idl文件编写。 清单1显示了一个示例接口定义文件。
清单1.样本IDL内容
#include "nsISupports.idl"
//658abc9e-29cc-43e9-8a97-9d3c0b67ae8b
[scriptable, uuid(658ABC9E-29CC-43E9-8A97-9D3C0B67AE8B)]
interface sample : nsISupports
{
long sample_numeric(in long a, in long b);
string sample_string();
void sample_nonreturn();
};
每个XPCOM组件都有一个接口名称。 在清单1中,名称为sample
,并显示了三个常见的函数原型。 658abc9e-29cc-43e9-8a97-9d3c0b67ae8b
是UUID; 每个XPCOM组件都有此唯一ID。 在Linux系统上,您可以在shell中使用genuuid
命令轻松获得此ID: linux-d8xr:#: genuuid
。
要将接口定义文件转换为C ++源文件,您需要gecko-sdk软件包中的实用程序(请参阅先决条件 )。 您可以使用默认选项安装gecko-sdk。 通常,它将内容存储在/ opt / gecko-sdk中。 目录结构应该与清单2非常相似。
清单2. Gecko-sdk安装内容
drwxrwxrwx 2 500 501 144 Jun 9 2006 bin
drwxrwxrwx 2 500 501 7984 Jun 9 2006 idl
drwxrwxrwx 5 500 501 10312 Jun 9 2006 include
drwxrwxrwx 2 500 501 368 Jun 9 2006 lib
要转换.idl中定义的接口,您需要在bin文件夹中使用xpidl 。 该示例使用它两次来生成.xpt文件和.h文件。 .xpt是IDL的二进制形式,而.h是一个头文件,其中包含XPCOM接口的基类定义。 通常,您不会在此头文件中进行任何修改。 但是,要扩展并从基类继承,可以添加更多私有方法和变量进行扩展。 清单3显示了在Linux上生成命令的示例。
清单3.生成.xpt和头文件
XPID_BIN=the path to geck-sdk/bin/xpidl
GECK_SDK_PATH=the path to geok-sdk
$(XPID_BIN) -m header -I $(GECKO_SDK_PATH)/idl -o sample sample.idl
$(XPID_BIN) -m typelib -I $(GECKO_SDK_PATH)/idl -o sample sample.idl
如果在sample.idl文件中定义接口,则在准确执行命令后将获得sample.idl和sample.h。
扩展,自定义和实现XPCOM方法
在本节中,您将开始真正的XPCOM功能开发。 如前所述,您无需修改两个生成的文件:您可以从它们继承接口。 有时,您可能无法在开发阶段开始时完成所有界面。 例如,如果您决定修改,添加或删除XPCOM组件的接口,则必须重新生成并覆盖sample.h。 您将希望使基类的内容和结构尽可能简单。 在扩展文件中,您可以添加更复杂的方法和功能。 清单4显示了如何编写extend
类的示例。
清单4. extend
类样本的内容
#ifndef _SAMPLEEXTEND_H_
#define _SAMPLEEXTEND_H_
#include "sample.h"
#define SAMPLE_COMPONENT_CONTRACTID "@cn.ibm.com/XPCOM/sample;1"
#define SAMPLE_COMPONENT_CLASSNAME "Sample XPCOM Interface Layer"
#define SAMPLE_COMPONENT_CID { 0x658abc9e, 0x29cc, 0x43e9,
{ 0x8a, 0x97, 0x9d, 0x3c, 0x0b, 0x67, 0xae, 0x8b } }
//658abc9e-29cc-43e9-8a97-9d3c0b67ae8b
class sampleextend : public sample
{
public:
NS_DECL_ISUPPORTS
NS_DECL_SAMPLE
sampleextend();
virtual ~sampleextend();
//additional member functions
int sampleextend_add();
};
#endif
上面三个粗体显示了三个必需的宏。
-
SAMPLE_COMPONENT_CONTACTID
- 在JavaScript中,当自定义代码调用XPCOM方法时,Firefox将调查已向其注册的组件,并找到与ID匹配的组件,然后将JavaScript中的参数传递给方法条目。 SAMPLE_CONTACT_CLASSNAME
- 您自己的XPCOM组件的名称。 SAMPLE_COMPONENT_CID
- 生成的UUID。
为了使浏览器受益于XPCOM组件提供的功能,必须首先将该组件注册到浏览器。 必须为您的组件准备一份表格。 上述三个宏必须以格式放置。 浏览器在启动时会知道您的组件,并在组件列表中注册它们。 清单5显示了sample.idl的注册表格。
清单5.示例组件注册表单
#include "nsIGenericFactory.h"
#include "sample.h"
#include "sampleextend.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(sampleextend)
static nsModuleComponentInfo components[] =
{
{
SAMPLE_COMPONENT_CLASSNAME,
SAMPLE_COMPONENT_CID,
SAMPLE_COMPONENT_CONTRACTID,
sampleextendConstructor,
}
};
NS_IMPL_NSGETMODULE("sampleModule", components)
至此,您已经:
- sample.h,其中包含接口基类
- sampleextend.h,其中包含提取的私有方法和函数定义
- sampleComponent.cpp,这是浏览器的组件注册表
强烈建议您将XPCOM的功能和服务的实现放在单独的项目文件中。 除非需要修改组件的接口,否则无需修改这三个文件。 清单6显示了一个示例。
清单6. XPCOM服务实现示例
#include "sample.h"
#include "sampleextend.h"
#include "nsMemory.h"
#include "plstr.h"
#include "stdio.h"
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
using namespace std;
NS_IMPL_ISUPPORTS1(sampleextend, sample)
sampleextend::sampleextend()
{}
sampleextend::~sampleextend()
{}
/* long sample_numeric (in long a, in long b); */
NS_IMETHODIMP sampleextend::Samplenumeric(PRInt32 a, PRInt32 b, PRInt32 *_retval)
{
*_retval = a+b;
return NS_OK;
}
/* string sample_string (); */
NS_IMETHODIMP sampleextend::Samplestring(char **_retval)
{
*_retval = (char*) nsMemory::Alloc(20);
PL_strcpy(*_retval,"hello from xpcom");
return NS_OK;
}
/* void sample_nonreturn (); */
NS_IMETHODIMP sampleextend::Samplenonreturn(void)
{
return NS_OK;
}
/* additional functions */
int sampleextend::sampleextend_add()
{
return 1;
}
Here is an integrated makefile for generating headers and
compiling sources for the sample component:
GECKO_SDK_PATH = $(PWD)/gecko-sdk/gecko-sdk
XPID_BIN = $(GECKO_SDK_PATH)/bin/xpidl
XPID_TARGET = sample.h sample.xpt
GECKO_CONFIG_INCLUDE = -include mozilla-config.h
GECKO_DEFINES = -DXPCOM_GLUE -DMOZILLA_STRICT_API
GECKO_LDFLAGS = -L $(GECKO_SDK_PATH)/lib -lxpcomglue -lxpcomglue_s
CXX=g++
INCLUDE = -I $(GECKO_SDK_PATH)/include
DEBUG=y
ifeq ($(DEBUG), y)
FLAGS = -g -Wall
else
FLAGS = -O2
endif
TMP_OBJECTS = $(foreach i,$(SOURCES),$i)
CPP_OBJECTS = $(TMP_OBJECTS:.cpp=$(OBJ))
OBJECTS = sampleModule.o sampleextend.o
%.o:%.cpp
$(CXX) -c -o $@ $(FLAGS) $(INCLUDE) $*.cpp
LIB_SUFFIX=.so
LIBRARY=sampleglue
LIB_FULL=lib$(LIBRARY)$(LIB_SUFFIX)
all: xpcomheader $(OBJECTS) $(LIB_FULL)
xpcomheader:
$(XPID_BIN) -m header -I $(GECKO_SDK_PATH)/idl -o sample sample.idl
$(XPID_BIN) -m typelib -I $(GECKO_SDK_PATH)/idl -o sample sample.idl
$(LIB_FULL):
$(CXX) -shared -o $(LIB_FULL) $(OBJECTS)
.PHONY : clean
clean:xpcomclean
xpcomclean:
-rm -rf $(XPID_TARGET)
-rm -rf *.o
-rm -rf *.so
上面的示例只是向浏览器应用程序返回“ hello world”。 您可以添加更切合实际的逻辑或功能来支持您要开发的应用程序。 样本makefile是构建XPCOM组件时要使用的典型参考。 如果按照上述步骤操作,并使用make
通过makefile构建示例,则将有一个sample.xpt和libsampleglue.so文件。 下一节将探讨测试这些二进制文件的方法。
集成并测试XPCOM组件
Firefox之类的浏览器可以使用XPCOM提供的功能和服务。 通常用JavaScript(网页的大型机)编写,您只需使用HTML或XML即可实现。 该示例使用XUL。 如果尝试HTML,则可以进行各种改进。 在大多数情况下,JavaScript函数作为一个库包含在内,并保存在一个单独的.js文件中。 清单7显示了一个XUL示例。
清单7.用XUL和sample.js编写的测试页面内容
============== sample.xul ==============
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE window>
<window id="sample" xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
width = "1024"
height = "768"
>
<script src="sample.js"/>
<hbox width="40" height="45" style="background-color: blue;">
<button id="sample" label="press me" height="45"
width="12" oncommand="sample_change();"/>
<description id="sample-glue">
xpcom-sample
</description>
</hbox>
</window>
============== sample.js ==============
var samplegluexpcominterface=null;
function sample_change()
{
var res;
try
{
if(samplegluexpcominterface==null)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
samplegluexpcominterface= Components.classes["@cn.ibm.com/XPCOM/sample;1"].
createInstance(Components.interfaces.sample);
}
}
catch(err)
{
alert(err);
}
document.getElementById("sample-glue").value = samplegluexpcominterface.samplestring();
}
该样本未做任何实际工作; 它只是调用XPCOM接口并返回一个虚拟的“来自xpcom的问候”。 请注意,按下按钮时,该功能由事件驱动。 在Firefox中打开页面之前,请确保按照以下步骤设置XPCOM组件并将其注册到浏览器:
- 如果将Firefox安装在默认目录中,则将sample.xpt复制到/ usr / lib / firefox / components。
- 将libsampleglue.so复制到/ usr / lib / firefox / components。
- 在/ usr / lib / firefox / chrome中创建一个名为
gui
的目录,并将sample.xul和sample.js复制到此新目录。 - 创建一个.manifest文件,并按如下所示输入内容:
content sample gui
。 将此清单复制到/ usr / lib / firefox / chrome。
现在,您可以在GUI模式下打开x终端,并输入如下所示的命令:
linux-d8rx#: firefox –chrome chrome://sample/gui/sample.xul
样本的结果如图2所示。
图2. Firefox中的示例输出
您之所以这样做,主要是因为JavaScript和UI在单个线程上,所以当一个忙时,另一个就被阻塞了,尤其是在加载XPCOM组件并调用其公开的方法时。 如果此方法的持续时间很长,则UI可能显示为挂起,而对用户无响应。
尽管Firefox可能具有允许脚本长时间运行的配置设置,但让用户在执行您的应用程序之前配置浏览器并不是一个适当的解决方案。 为避免此问题,可以使XPCOM组件保持快速状态并返回到UI。 或尝试将C ++函数放入另一个线程。 当接口调用JavaScript时,它还可以提供回调结果函数。 请参阅相关主题的更多关于这个话题。
摘要
在本文中,您学习了如何创建,开发和测试XPCOM组件,Firefox可以将其用作应用程序的扩展。 您可以使用提供的样本来快速开始为Firefox开发组件。
翻译自: https://www.ibm.com/developerworks/java/library/os-xpcomfirefox/index.html