当前位置: 首页 > 工具软件 > XPCOM > 使用案例 >

XPCOM (Cross Platform Component Object Model)

尹晟
2023-12-01

XPCOM,跨平台组件对象模型,让Gaia访问Gecko实现好的功能。

涉及技术有:组件管理、文件抽象、对象消息传递、内存管理等等。


一. nsISupports

  位置xpcom/base/

  所有XPCOM接口都是从这个nsIsupports继承出来的,包括这三个方法

  AddRef()、QueryInterface()、Release()。

  调用这些接口可以不用担心引用计数。QueryInterface之前需要先完成Release。

  nsISupports是所有XPCOM接口的基础,所有接口都要从这里继承,主要实现两个功能: runtime type查找和对象生命周期管理。
一个对象支持多个接口,如果一个指针指向一个接口的同时也希望获取其他接口,就可以用QueryInterface()。已知一个对象是A类型(A接口),然后确认是否是B类型(接口B),如果是,QueryInterface返回一个绑定在请求上的指针。因为XPCOM采用间接调用方式,有可能存在多个指针指向一个对象,调用者不容易区分指针的指向。

  在内存中长期存放对象容易导致内存泄漏。不仅接口有全局统一的ID号——IID,XPCOM的类也有自己的ID号——CID。另外,它们也有字符形式ID,称为contract ID。XPCOM对象实际是由组件管理器创建的,引用lib库(XPCOM模块)时首先要在组件管理器注册,通过注册表中的类ID和具体的类进行映射。

  所有的XPCOM对象都是在堆上创建,调用者通过指针来引用接口,比如静态类型的指针指向了抽象基类,而实际对象指向的是派生类。XPCOM对象也称为接口的实现,对像的引用称为接口指针。


二. CID和Contract ID

CID类或组件标识符,16个字节的数字。

如:{ 0x777f7150, 0x4a2b, 0x4301, 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22,
0xaa}

Contrct ID,方便阅读的字符串,可以用它访问一个组件

如: "@mozilla.org/network/ldap-operation;1"

三. XPConnect

关联XPCOM和JavaScript。XPConnect wrappers(封装器)是XPConnect重要的组成部分,

1. 简介一下最基本的XPConnect对象

(1) XPCWrappedNative
native XPCOM组件(C++)已经实现需要反射进JavaScript,就会创建XPCWrappedNative对象,包括所有的DOM对象(包括window)和chrome元素。
wrapper实现了调用过程中从JavaScript到C++的映射,比如调用toString()获取到了[xpconnect wrapped nsIFoo],就是从接口nsIFoo引用的一个XPCWrappedNative对象。这时XPConnect就自动创建了。
(2) XPCWrappedJS
它与XPCWrappedNative是相反,可以把一个JavaScript对象反射进C++,即当传递JavaScript对象给一个C++函数时,就会创建XPCWrappedJS。比如,现在已经用nsIFoo接口实现了一些组件,需要把这个JavaScript对象传递给C++,就需要创建XPCWrappedJS,C++经过XPCWrappedJS代码才能调到JavaScript的最终实现。
这个wrapper同样也是XPConnect自动创建,不必关心何时、如何调用。
(3) 二次封装
有一种情况,一个XPCWrappedNative封装了另一个对象,这种情况发生在一个JavaScript对象通过IDL接口进行传递,先创建了一个XPCWrappedJS对象,而此时需要通过其他的接口返回给JavaScript,为保持API的兼容性,会根据这个XPCWrappedJS创建一个对应的XPCWrappedNative对象。

2. Javascript调用native方法的大概过程

实际上就是调用wrapped native方法时把一个特殊的接口(JSObject)作为参数
(1) XPConnect首先会检查参数是否是nsIVariant类型,如果是就把传进的参数都放入nsIVariant container,然后发给调用的函数。如果不是,XPConnect要检查参数是否为null或undefined,如果是就把nsnull传给调用函数。
(2) 如果以上都不是,XPConnect再看参数是否是JSObject(只有JSObject才能代表一个接口),如果不是JSObject,就在JavaScript代码调用的地方抛出异常NS_ERROR_XPC_BAD_CONVERT_JS。有一种特殊情况是参数是0时,为了让JavaScript程序员更好地理解0!=null,抛出NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL。
(3) 如果确定参数是一个JSObject,再判断它是不是native object的一个wrapper(通常是C++实现,当然也有可能是Python, Java或其他语言实现),如果不是wrapper,就展开这个对象展开,然后QI接口的iid,把接口指针传给调用函数;如果不能QI到接口,就在JavaScript调用函数中抛出NS_ERROR_XPC_BAD_CONVERT_JS。
(4) 否则,XPConnect就在哈希表查找这个JSObject是否已经是一个XPCOM接口的wrap。如果是直接使用这个warpper,不是就创建一个新wrapper,无论哪种情况剩下的步骤都一样。
(5) XPConnect这些操作目的是让JavaScript程序员容易理解,用尽量少的声明和实现,尽量用断言描述对象。例如,首先一个JSObject已经当作nsIFoo,然后又把这个JSObect传给了nsIBar,此时XPConnect创建的wrap可以让native调用者在nsIFoo和nsIBar两者中都能获取到接口(QI),从外部来看它就像已经取到了全部方法。
(6)JSObject不一定要有QueryInterface方法,QI方法只是用来创建Assert,用来支持JSObject接口。JSObject创建warpper时也不是必须包含所有的属性和方法,只是在实际调用接口过程中才会去检查,此时XPConnect在这个对象上按名称进行查找,找到就调用,没找到就返回NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED错误码。这样的设计使JavaScript更灵活,JS接口的实现者可以只去实现需要的接口。


四. XPIDL

接口描述语言,事先用它定义好接口,之后可以用工具(xpidl)自动生成一下代码,比如C++头文件、xpt文件,甚至Java接口和Html。

https://developer.mozilla.org/en-US/docs/Mozilla/XPIDL


五. xpt文件

XPConnect typelib,XPCOM对象的runtime type信息。


六. Non-blocking IO

  异步IO,通过注册事件来触发对应的handler来执行。比起thread per connection的方式,它能更好的利用资源。因为回调的机制对于异步的通信方式来说减少了轮询或者其它强制同步机制的开销。

  可以通过observer pattern来理解一下。在observer pattern里,要触发事件之前,事件是需要注册进去的,在某些时候还要将注册的事件注销。比如一些简单的实现里,通过一个list来保存注册的handler。然后事件发生后也就是遍历这个list来回调这些方法。

  可以通过一个dispatcher针对不同的事件注册不同的回调处理方法。

另外,Gecko中有一个IO thread。比如RilClient

位置gecko/ipc/ril/Ril.cpp





 类似资料:

相关阅读

相关文章

相关问答