B2GOS通过webidl添加webAPI
Web IDL(Interface Definition Language,接口定义语言)该规范定义了一个OMG IDL 3.0的语法子集,用来规范定义的接口。Web IDL 是一个具有多种功能的 IDL 变量,便于规范Web平台中的常用脚本对象的操作。
对于ffos而言,webidl是gecko层写的接口,供gaia调用。
写一个这样的接口,需要写接口文件,实现类,控制调用方式
1.写接口文件
接口文件以 webidl 为后缀,必须放到 dom/webidl目录 下面,并在 dom/webidl/moz.build 中加入文件名,以使编译器能够看见它的存在。
注意:接口名字 需要与 实现类的头文件名字 和 cpp文件名字一致
示例1:Gecko/dom/webidl/ZTest.webidl
interface ZTest
{
void setTestData();
};
在 dom/webidl/moz.build 中添加文件名时,需要注意该文件中 WEBIDL_FILES 下的文件名 按字母升序排列,如果不按该顺序添加文件名会报错。
示例2:修改moz.build
WEBIDL_FILES =[
...
...
'ZTest.webidl',
...
]
2. 写实现类
在 gecko/dom 下 新建文件夹ztest,把所有需要的模块文件都放到该文件夹下,最简单的结构如下:
ztest/
├── moz.build
├── ZTest.cpp
└── ZTest.h
下面分别看这三个文件的写法
2.1 头文件 ZTest.h
// 预编译防止重复引用
#ifndef mozilla_dom_ztest_ZTest_h
#define mozilla_dom_ztest_ZTest_h
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
// 命名空间必须在 mozilla ::dom 下面
namespace mozilla {
namespace dom {
namespace ztest {
class ZTest MOZ_FINAL
:public nsISupports
,public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MozAaaTest)
ZTest(nsPIDOMWindow* aWindow);
virtual ~MozAaaTest();
// 如果你的接口不继承其他类,就需要执行GetParentObject,这样每一个 webidl object 就会关联到返回的特定窗口
nsPIDOMWindow* GetParentObject() const { return mWindow; }
// 这里只是定义了一下,具体实现在cpp文件
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
/* Impliment the WebIDL interface begin*/
/* 接口函数开始 */
// 函数名从 webidl 文件里得来,函数名首字母变为大写
NS_IMETHODIMP SetTestData();
/* Imppliment the WebIDL interface end*/
private:
protected:
nsCOMPtr<nsPIDOMWindow> mWindow;
};
}// namespace aaatest
}// namespace dom
}// namespace mozilla
#endif
说明:
- 继承关系
函数继承 nsISupports 和 nsWrapperCache。注意,如果要继承这两个接口,就需要把nsISupports放到nsWrapperCache前面。
nsISupports 是所有的XPCOM都需要继承的接口,nsISupports提供三个方法。第一个方法 QueryInterface 可以实时的根据接口 uuid 获得接口指针,从而利用指针调用接口方法,而 AddRef 与 Release 增加了实例的计数功能。
nsWrapperCache 如果你的接口不继承别的接口,就需要继承 nsWrapperCache,nsWrapperCache 把你的类挂载到 cycle collector,这样cycle collector就会对你的 wrapper cache 进行跟踪处理。
2.2 CPP文件 ZTest.cpp
cpp 文件的编写参照 webidl 接口文件和 头文件,实现其中申明的函数。需要注意的是 ZTestBinding.h 是 生成文件, 必须要包含否则报错。
#include "ZTest.h"
#include "mozilla/dom/ZTestBinding.h"
usingnamespace mozilla;
usingnamespace mozilla::dom;
usingnamespace mozilla::dom::ztest;
// cycle collector与isupports的宏定义
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ZTest)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ZTest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ZTest)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ZTest, mWindow)
ZTest::ZTest(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
{
printf("Enter ZTest\n");
}
ZTest::~ZTest()
{
printf("Enter ~ZTest\n");
}
// WrapObject的实现
JSObject*
ZTest::WrapObject(JSContext* aCx)
{
return ZTestBinding::Wrap(aCx,this);
}
// webidl中定义的接口的实现
NS_IMETHODIMP ZTest::SetTestData(){
printf("ZTest execute function setTestData: Successed\n");
return NS_OK;
}
2.3 moz.build
同样的,每个中括号中的文件名都必须 按字母升序排列,另外,FINAL_LIBRARY ='xul'这个库文件必须加上,不然会报错。
EXPORTS.mozilla.dom.ztest +=[
'ZTest.h',
]
SOURCES +=[
'ZTest.cpp',
]
LOCAL_INCLUDES +=[
'/dom/ztest',
]
// #
FINAL_LIBRARY ='xul'
3. 补充说明
3.1 文件夹 ztest 的添加需要在 dom/moz.build 中添加路径
DIRS +=['aaatest',]
3.2 配置文件 dom/bindings/Bindings.config, 为接口的执行添加入口
'ZTest':{
'nativeType':'mozilla::dom::ztest::ZTest',
},
括号中不添加信息,表示是默认值;
如果你的 C++ 不是 mozilla::dom::MyInterface 的格式存在,那么需要在 nativeType 中添加正确的路径,如 mozilla::dom::ztest::ZTest;
如果你的头文件路径不与 nativeType 中信息一一对应,即如不是 mozilla/dom/ztest/ZTest.h,那么你需要在 headerFile 中添加正确的头文件路径。
4. 修改 navigator 下内容
模块添加完毕,最简单的调用方式就是通过 navigator。因为 navigator 在 gaia 层的调用关系架构已经搭好。
同样,修改 navigator 需要修改的文件包括:
- gecko/dom/webidl/Navigator.webidl
- gecko/dom/base/Navigator.h
- gecko/dom/base/Navigator.cpp
修改原理:在 Navigator.webidl 中添加一个属性,然后在 cpp 中去实现这个属性,给我们的模块实例化。
4.1 Navigator.webidl
给 Navigator 添加了一个只读的属性。这里有个权限的检查,后面会介绍我们需要加权限,才能在gaia去用添加的接口。
partial interface Navigator {
[Throws,CheckPermissions="ztest"]
readonly attribute ZTest zTest;
};
4.2 Navigator.h
在头文件中声明了属性的实现函数 GetZTest,另外声明了一个成员mZTest用来保存ZTest 的实例
// 需要使用到 ZTest 类
namespace ztest {
class ZTest;
}
class Navigator MOZ_FINAL :public nsIDOMNavigator
,public nsIMozNavigatorNetwork
,public nsWrapperCache
{
public:
...
// 属性的实现
ztest::ZTest* GetZTest(ErrorResult& aRv);
private:
...
// 存储 ZTest 的实例
nsRefPtr<ztest::ZTest> mZTest;
}
4.3 Navigator.cpp
从属性的实现函数可以看出来,gaia 初始化 Navigator.ZTest,就会调用 GetZTest 函数,返回 ZTest 的实例 mZTest,如果实例没有被初始化,就调用 ZTest 的构造函数进行初始化。
#include "mozilla/dom/ztest/ZTest.h"
usingnamespace ztest;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mZTest)
// 属性的实现函数
ztest::ZTest*
Navigator::GetZTest(ErrorResult& aRv)
{
LOG("GetZTest");
if(!CheckPermission("ztest")){
LOG("GetZTest CheckPermission return false");
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if(!mZTest)
{
mZTest =new ztest::ZTest(mWindow);
}
else
{
LOG("mZTest exist just return");
}
return mZTest;
}
// 资源释放
void
Navigator::Invalidate()
{
...
if(mZTest){
mZTest = nullptr;
}
}
5 gaia调用
要调用 webidl 首先需要有一个权限,这个就是我们在前面用到的 ztest权限,接着需要在 app 的 manifest.webapp 中加入权限,最后才能使用 webidl 接口。
注意: 想要 gaia 能调用新加入的webidl,需要删除 out 目录, 重编全部代码,不然gaia 会找不到接口
5.1 定义权限
在 gecko/dom/apps/PermissionsTable.jsm 中加入代码
this.PermissionsTable = {
"ztest":{
app: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
}
5.2 给 app 加入权限
在 apps/system/manifest.webapp 中加入代码
"permissions":{
...
"aaatest":{},
...
},
6. 使用例子
HardwareButtonsBaseState.prototype.process = function (type) {
switch(type){
case'enter-button-press':
dump("ZTest " + type);
var _ztest = navigator.ZTest;
_ztest.setTestData();
...
}
}
注意: 这里必须先调用 ZTest 的构造函数,再利用 ZTest 的实例 _ztest 去调用webidl 中定义的函数,这里的函数名必须与 webidl 中的保持一致,而不是在 cpp 中的函数名。
7. 总结
总的修改流程如下:
- 添加 webidl 文件
- Gecko/dom/webidl/MozAaaTest.webidl
- Gecko/dom/webidl/moz.build
- 添加实现模块
- gecko/dom/ztest/
- ZTest.h
- ZTest.cpp
- moz.build
- gecko/dom/ztest/
- 修改配置文件为接口添加执行入口
- gecko/dom/bindings/Bindings.conf
- navigator 调用接口添加
- gecko/dom/webidl/Navigator.webidl
- gecko/dom/base/Navigator.h
- gecko/dom/base/Navigator.cpp
- gaia 调用
- gecko/dom/apps/PermissionsTable.jsm
- gaia/apps/system/manifest.webapp
8. 其他
webidl、xpidl、ipdl 区别
-
webidl:接口定义文件后缀为 .webidl,用于定义 webapi,比如 gecko/dom/webidl/FMRadio.webidl
-
xpidl:接口定义文件后缀为 .idl,用于定义xpcom组件接口,比如 gecko/dom/wifi/nsIWifi.idl
-
ipdl:接口定义文件后缀为 .ipdl,用于定义进程间通信(ipc)接口,帮助 gecko 中 webidl 接口的实现,比如 gecko/dom/fmradio/ipc/PFMRadio.ipdl
Gecko 内部使用最多的还是基于 XPConnect 上的 DOM Bindings,使用 ScriptableHelper 来帮助把 XPCOM 组件暴露在 JavaScript 上,但这种方式有一些内存管理和速度上的缺点,并且 XPIDL(XPCOM界面定义语言) 与 WebIDL(Web界面定义语言) 在语义上有一定的不同,会使各种加速方法和处理语义问题的 Wrapper 需要手工地针对不同 API 进行修改。
因为 XPIDL 的一些问题,Firefox OS 里还提供一种 DOM bindings 的方法。由于这个问题是在法国巴黎首次讨论的,所以最早被称为 “Paris Bindings”。因为这些 DOM API实际上不使用 XPCOM 的许多功能,所以它的速度比原有的 XPConnect 会快许多。通过 WebIDL DOM bindings 可以省略掉一些虚函数的开销,还可以节省掉 XPCOM 处理线程安全的花费,透过新的 JIT 技术,从 JavaScript 可以调用 DOM API,甚至直接调用 C++ 的实现。未来描述 DOM API 的语言可能统一为 WebIDL。
8.1 编写 MyTest.webidl
gecko/dom/webidl 目录下面编写webidl文件
[JSImplementation="@mozilla.org/mytest;1",
NavigatorProperty="mytest"
]
interface MyTest {
DOMString sayHello();
};
- @mozilla.org/mytest;1 contractname,之后和js实现绑定。
- NavigatorProperty 设置为navigator的属性
8.2 编写 MyTest.manifest
在 MyTest.manifest 文件中,将 webidl 接口与 javascript 实现绑定。
gecko/dom/mytest/MyTest.manifest
component {50CD146F-75EE-4C5E-88FA-FB3871362156} MyTest.js
contract @mozilla.org/mytest;1 {50CD146F-75EE-4C5E-88FA-FB3871362156}
定义 一个component,然后将其与 @mozilla.org/mytest;1绑定
8.3 编写 MyTest.js
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function MyTest(){
dump("zyt mytest constractor");
}
MyTest.prototype = {
classDescription: "zyt test xpcom",
classID: Components.ID("{50CD146F-75EE-4C5E-88FA-FB3871362156}"),
contractID: "@mozilla.org/mytest;1",
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports]),
sayHello: function(){
dump("this is zyt test to say hello");
return"this is my first webidl test";
},
};
this.NSGetFactory= XPCOMUtils.generateNSGetFactory([MyTest]);
8.4 编写 moz.build
dom/mytest/moz.build
EXTRA_COMPONENTS += [
'MyTest.js',
'MyTest.manifest',
]
注:该文件必须按字母序排列。否则编译出错
8.5 修改 dom/moz.build
PARALLEL_DIRS += [
'webidl',
'xbl',
'xslt',
...
+ 'mytest',
]
8.6 添加到 Firefox OS
修改 gecko/b2g/installer/package-manifest.in:
;my test
@BINPATH@/components/MyTest.js
@BINPATH@/components/MyTest.manifest

浙公网安备 33010602011771号