ffos (gecko):如何在gecko 中编写XPCOM组件(一)--javascript 实现方式
ffos (gecko):如何在gecko 中编写XPCOM组件(二)--C++ 实现方式
xpcom refference links XPCM相关
摘要
XPCOM是gecko中很核心的技术,本文尝试在gecko中添加两个简单XPCOM组件,展示XPCOM的基本开发示例,为后续工作提供一些参考。
1. XPCOM 技术
1.1 XPCOM是什么
XPCOM 的目标是使软件的不同部分分别开发, 相互独立. 为了是应用的不同组件之间能够互操作, XPCOM 把组件的实现与接口(后面讨论接口)分开. 同时 XPCOM 还提供了加载和操纵这些组件的库和工具以及服务, 以帮助开发人员编写跨平台的代码和组件版本管理; 因此组件可以在不破坏应用或者重新生成应用的同时被替换被更新. 通过使用 XPCOM, 开发人员创建的组件可以在不同的应用中被重用, 或者替换当前应用中的组件以改变应用的功能.
XPCOM 不只提供组件软件开发的支持, 它同时提供一个开发平台的大多数功能的支持:
组件管理
文件抽象
对象消息传递
内存管理
可以把 XPCOM 想象成一个 组件开发平台, 它提供了上面的这些特性。
Gecko 中应用地很核心技术,相当与microsoft 的COM技术(又是借鉴),用的很广,按我的理解,用于扩展gecko 本身,用js或实现的往上可提供webidl接口的实现文件来调用,往下则可以对更下层的进行封装。
1.2 XPCOM架构
Gecko 与XPCOM的关系可以简单粗暴理解成,gecko=一堆XPCOM组件。
2. XPCOM 组件开发
下面介绍gecko下XPCOM 组件的写法框架 。
2.1 javascript实现方式
涉及到的目录及文件及其作用原理:
2.1.1 js组件编写
1.js 组件最终目录结构:
gecko/dom/simplejs/
simplejs ├── moz.build ├── nsISimplejs.idl ├── nsIUseComponent.idl ├── Simplejs.js ├── Simplejs.manifest ├── UseComponent.js └── UseComponent.manifest |
2.组件实现:
(1)nsISimplejs.idl 接口定义文件
#include "nsISupports.idl" [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)] interface nsISimpleComponent : nsISupports { attribute string yourName; void write(); void changejs(in string aValue); }; |
(2)Simplejs.jsnsISimplejs.idl的实现文件
"use strict";
const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm");
function SimpleComponentImpl() { debug("yahaha,constructor SimpleComponentImpl constructor in simplecomponent.js################"); }
//dump("yahaha,dump,im SimpleComponentImpl in simplecomponent.js######################### before prototype");
SimpleComponentImpl.prototype = {
contractID : "@mozilla.org/simplejs;1", classID: Components.ID("{cc587afa-0696-469f-9eff-9dac0dd727fe}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleComponent]),
get yourName(){ return this.mName; }, set yourName(aName){ return this.mName = aName; }, write: function(){ dump("yahaha,SimpleComponentImpl.write()"); dump("Hello " + this.mName + "\n"); }, changejs: function(aValue){ this.mName = aValue; dump("Hello " + this.mName + "\n");
}, mName: "Anonymous!", };//the implement of interfaces in .idl
//dump("yahaha,dump,in simplecomponent.js###############before generateNSGetFactory"); this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SimpleComponentImpl]);//get factory //dump("yahaha SimpleComponent.js ooooooooooooooooooooooooooooooooooooooooo"); |
(3)Simplejs.manifest 组件注册清单文件
# Simplejs.manifest component {cc587afa-0696-469f-9eff-9dac0dd727fe} Simplejs.js contract @mozilla.org/simplejs;1 {cc587afa-0696-469f-9eff-9dac0dd727fe} category profile-after-change simplecomponent @mozilla.org/simplejs;1 |
(4)在gecko/b2g/installer/package-manifest.in
dom_simplejs.xpt是moz.build 指定生成的xpt模块。见moz.build部分。
添加:
@RESPATH@/components/dom_simplejs.xpt @RESPATH@/components/Simplejs.js @RESPATH@/components/Simplejs.manifest |
(5)moz.build
编译文件。写法如下:
# vim: set filetype=python: XPIDL_MODULE = 'dom_simplejs'
XPIDL_SOURCES += [ 'nsISimplejs.idl', ] EXTRA_COMPONENTS += [ 'Simplejs.js', 'Simplejs.manifest', ]
include('/ipc/chromium/chromium-config.mozbuild') ALLOW_COMPILER_WARNINGS=True
FINAL_LIBRARY = 'xul'
if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wshadow'] |
(6)在gecko/dom/ 目录下在moz.build中将simplejs文件夹添加进去
DIRS += [ ... 这里省略很多目录... 'simplejs', ] |
开始编译,通过,刷机。
以上,js 实现的组件的实现及注册添加完成。
2.1.2 js组件调用
理论上,可以在gecko 源码中任何组件中调用注册好的js 组件。这里我自己再写一个组件来调用刚刚写好的组件就好。
流程跟上面的完全一样。仍然在gecko/dom/simplejs/目录下
(1)nsIUseComponent.idl 其实可有可无,目的是调用我们写了的组件,具体的接口我没去实现。
#include "nsISupports.idl" [scriptable, uuid(6632e3ff-36f8-425f-94be-d85b26e634ee)] interface nsIUseComponent : nsISupports { void sayhi(); }; |
(2)UseComponent.js
接口实现文件,其实这里我没去实现,醉翁之意不在酒,主要为了调用写成功了的组件。
"use strict"; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm");
const USECOMPONENT_CONTRACTID = "@mozilla.org/usecomponent;1"; const USECOMPONENT_CID = Components.ID("{1ff24790-5e74-11e1-b86c-0800200c9a66}"); //////////////////////////////////////////////////////////////
//test of javascript component /////////////////////////////////////////////////////////// dump("yahaha,========>start: javascript component==================>"); var usecompo =Cc["@mozilla.org/simplejs;1"].createInstance(Components.interfaces.nsISimpleComponent); var result = usecompo.yourName;//the interface name in .idl dump("yahaha,...result: yourName = "+result); usecompo.changejs("yahaha,change after yourName");//call changejs() interface dump("yahaha,========>end: javascript component====================>\n ");
///Test of other existcpp component //////////////////////////////////////////////////////////// dump("yahaha,==========>start test1....instance of other cpp component in usecomponent.js "); var nfcservi =Cc["@mozilla.org/nfc/service;1"].createInstance(Components.interfaces.nsINfcService); dump("yahaha,nfc,,,,,,"+nfcservi); dump("yahaha,=========>end test1 ===================================>\n "); ///////////////////////////////////////////////////////////////////
//the implement part of this component itself function useComponentImpl(){ dump("yahaha,this is a null component in useComponentImpl"); } useComponentImpl.prototype={ //const USECOMPONENT_CONTRACTID = "@mozilla.org/usecomponent;1"; //const USECOMPONENT_CID =Components.ID("{1ff24790-5e74-11e1-b86c-0800200c9a66}"); contractID : USECOMPONENT_CONTRACTID, classID: USECOMPONENT_CID, QueryInterface: XPCOMUtils.generateQI([Ci.nsIUseComponent]), duuuump:function(){ dump("yahaha, hello world???"); }, } this.NSGetFactory = XPCOMUtils.generateNSGetFactory([useComponentImpl]);//part of register code |
(3)UseComponent.manifest
注册清单文件。
component {1ff24790-5e74-11e1-b86c-0800200c9a66} UseComponent.js contract @mozilla.org/usecomponent;1 {1ff24790-5e74-11e1-b86c-0800200c9a66} category profile-after-change usecomponent @mozilla.org/usecomponent;1 |
(4)在gecko/b2g/installer/package-manifest.in
添加:
@RESPATH@/components/dom_simplejs.xpt @RESPATH@/components/UseComponent.js @RESPATH@/components/UseComponent.manifest |
而两个组件所添加的合起来如下:
@RESPATH@/components/dom_simplecpp.xpt @RESPATH@/components/dom_simplejs.xpt @RESPATH@/components/Simplejs.js @RESPATH@/components/Simplejs.manifest @RESPATH@/components/UseComponent.js @RESPATH@/components/UseComponent.manifest |
(5)moz.build
编译文件。两个组件的编译文件如下:
# vim: set filetype=python:
XPIDL_MODULE = 'dom_simplejs'
XPIDL_SOURCES += [ 'nsISimplejs.idl', 'nsIUsecomponent.idl', ] EXTRA_COMPONENTS += [ 'Simplejs.js', 'Simplejs.manifest', 'UseComponent.js', 'UseComponent.manifest', ]
include('/ipc/chromium/chromium-config.mozbuild') ALLOW_COMPILER_WARNINGS=True
FINAL_LIBRARY = 'xul'
if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wshadow'] |
编译通过,刷机,adb logcat 看log.
最后的adb logcat结果:
adb logcat|grep -in yahaha
363:I/Gecko ( 180): yahaha,========>start: javascript component==================> 364:I/Gecko ( 180): yahaha,...result: yourName = Anonymous! 365:I/Gecko ( 180): Hello yahaha,change after yourName 366:I/Gecko ( 180): yahaha,========>end: javascript component====================> 368:I/Gecko ( 180): yahaha,==========>start test1....instance of other cpp component in usecomponent.js 369:I/Gecko ( 180): yahaha,nfc,,,,,,[xpconnect wrapped nsINfcService] 370:I/Gecko ( 180): yahaha,=========>end test1 ===================================> |
2.2 C++ 实现方式
2.2.1 C++ 组件编写
在gecko/dom/下新建一个文件夹,simplecpp,将simplecpp添加到gecko/dom/moz.build下。
DIRS += [ ... 这里省略很多目录... 'simplecpp', ] |
最后整个目录结构如下:
simplecpp/ ├── moz.build ├── nsISimplecpp.idl ├── Simplecpp.cpp └── Simplecpp.h |
(1)nsISimplecpp.idl接口定义
nsISimplecpp.idl #include "nsISupports.idl" [scriptable, uuid(fd306b58-02de-4dc7-a512-f1ade5cf6840)] interface nsISimplecpp : nsISupports { attribute string yourName; void write( ); void change(in string aValue); void usecomponent(in string aValue1); }; |
(2)Simple.h
nsISimplecpp.h是nsISimplecpp.idl 生成的头文件,它在objdir-gecko/dist/include/下。文件里面包含了组件实现文件的头文件模板和cpp文件模板。将/*Header file*/开始的代码复制到实现文件的头文件中,
/* Use the code below as a template for the implementation class for this interface. */ /* Header file */ 从这里开始 class nsSimplecpp : public nsISimplecpp { public: NS_DECL_ISUPPORTS NS_DECL_NSISIMPLECPP
nsSimplecpp();
private: ~nsSimplecpp();
protected: /* additional members */ }; |
完整头文件如下:
#ifndef mozilla_dom_simplecpp_simplecpp_h #define mozilla_dom_simplecpp_simplecpp_h
#include "nsCOMPtr.h" #include "nsISimplecpp.h"
namespace mozilla { class nsSimplecpp final: public nsISimplecpp//从nsISimplecpp 接口类中继承一个类 { public: NS_DECL_ISUPPORTS NS_DECL_NSISIMPLECPP //static already_AddRefed<nsSimplecpp> FactoryCreate(); nsSimplecpp(); private: virtual ~nsSimplecpp(); protected: /* additional members */ };//扩展时按需要添加函数进行扩展 }//naemspace mozilla
#endif |
(3) Simple.cpp将
/* Implementation file */开始到/* End of implementation class template. */之间的代码复制到你的实现文件Simple.cpp里去.
/* Implementation file */ NS_IMPL_ISUPPORTS(nsSimplecpp, nsISimplecpp)
nsSimplecpp::nsSimplecpp() { /* member initializers and constructor code */ }
nsSimplecpp::~nsSimplecpp() { /* destructor code */ }
/* attribute string yourName; */ NS_IMETHODIMP nsSimplecpp::GetYourName(char * *aYourName) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsSimplecpp::SetYourName(const char * aYourName) { return NS_ERROR_NOT_IMPLEMENTED; }
/* void write (); */ NS_IMETHODIMP nsSimplecpp::Write() { return NS_ERROR_NOT_IMPLEMENTED; }
/* void change (in string aValue); */ NS_IMETHODIMP nsSimplecpp::Change(const char * aValue) { return NS_ERROR_NOT_IMPLEMENTED; }
/* void usecomponent (in string aValue1); */ NS_IMETHODIMP nsSimplecpp::Usecomponent(const char * aValue1) { return NS_ERROR_NOT_IMPLEMENTED; } /* End of implementation class template. */ |
具体的实现文件如下:
#include "Simplecpp.h" #include "utils/Log.h" #include "nsISimplejs.h"//test of call js component in cpp,failed #include <cutils/properties.h> #include "mozilla/dom/ToJSValue.h" #include "mozilla/ModuleUtils.h" #include "nsAutoPtr.h"
#define NS_SIMPLECPP_CID \ {0x668abcee, 0x29cc, 0x43e9, \ { 0x8a, 0x97, 0x9d, 0x3c, 0x0b, 0x67, 0xae, 0x8b }} #define NS_SIMPLECPP_CONTRACTID "@mozilla.org/simplecpp;1"
using namespace mozilla::dom; namespace mozilla {
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Implementation file */ NS_IMPL_ISUPPORTS(nsSimplecpp, nsISimplecpp)
nsSimplecpp::nsSimplecpp() { MOZ_ASSERT(NS_IsMainThread()); //MOZ_ASSERT(!gnsSimplecpp); ALOGI("yohoho,yahahacpp,log from gecko dom simplecpp.cpp component,nsSimplecpp()"); /* member initializers and constructor code */ }
nsSimplecpp::~nsSimplecpp() { /* destructor code */ }
/* attribute string yourName; */ NS_IMETHODIMP nsSimplecpp::GetYourName(char * *aYourName) { ALOGI("yohoho,yahahacpp,log from gecko dom simplecpp component,GetYourName()"); //return "MarkTwin"; return NS_OK; }
NS_IMETHODIMP nsSimplecpp::SetYourName(const char * aYourName) { ALOGI("yohoho,yahahacpp,log from gecko dom simplecpp component,SetYourName()"); return NS_OK; }
/* void write (); */ NS_IMETHODIMP nsSimplecpp::Write() { ALOGI("yohoho,yahahacpp,log from gecko dom simplecpp.cpp component,Write()"); return NS_OK; }
/* void change (in string aValue); */ NS_IMETHODIMP nsSimplecpp::Change(const char * aValue) { ALOGI("yohoho,yahahacpp,log from gecko dom simplecpp.cpp,Change()"); nsCOMPtr<nsISimpleComponent> SimpleService= do_CreateInstance("@mozilla.org/simplejs;1"); if (!SimpleService) { ALOGI("yahaha,no Component available"); } SimpleService->Changejs("anonymous====>");// in .h unfinished ALOGI("yahaha,log from simplecpp.cpp after Changechange()"); return NS_OK; }
//use the js component, cannot use this function with unknown reason /* void usecomponent (in string aValue1); */ NS_IMETHODIMP nsSimplecpp::Usecomponent(const char * aValue1) { ALOGI("yohoho,yahahacpp,log from simplecpp.cpp component,Usecomponent()"); nsCOMPtr<nsISimpleComponent> SimpleService= do_CreateInstance("@mozilla.org/simplejs;1"); if (!SimpleService) { ALOGI("yahaha,no Component available"); } //SimpleService->yourName; //SimpleService->changejs("anonymous====>");//if cannot use idl interface ,what make u to use js component ALOGI("yahaha,log from simplecpp.cpp Usecomponent()"); return NS_OK; } /* End of implementation class template. */
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //register the component //以下是组件注册部分,十分重要,不进行注册,你的组件写的就是些废代码,编译不报错也没用,其他组件不能调用。这一部分也可以跟其他组件合一起集中注册,按此结构即可。 /* *NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsSimplecpp, * nsSimplecpp::FactoryCreate) //也可选SINGLETON模式,但要多写一个FactoyCreate() */
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSimplecpp)//构造你的类
NS_DEFINE_NAMED_CID(NS_SIMPLECPP_CID);//定义你的CID
static const mozilla::Module::CIDEntry knsSimplecppCIDs[] = { { &kNS_SIMPLECPP_CID, false, nullptr, nsSimplecppConstructor }, { nullptr } };
static const mozilla::Module::ContractIDEntry knsSimplecppContracts[] = { { NS_SIMPLECPP_CONTRACTID, &kNS_SIMPLECPP_CID }, { nullptr } };
static const mozilla::Module knsSimplecppModule = {//你的组件模块信息 mozilla::Module::kVersion, knsSimplecppCIDs, knsSimplecppContracts, nullptr };
}// namespace mozilla
NSMODULE_DEFN(nsSimplecppModule) = &mozilla::knsSimplecppModule;//注册你的组件 |
(4)moz.build
按以下格式来放文件,编译即可。
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: #idl接口文件放这里 XPIDL_SOURCES += [ 'nsISimplecpp.idl', ] #给你的组件模块取个名,会生成相应的.xpt,如这里的生成dom_simplecpp.xpt #在objdir-gecko/dist/bin/components/dom_simplecpp.xpt XPIDL_MODULE = 'dom_simplecpp'
EXPORTS.mozilla.dom.simplecpp += [ 'Simplecpp.h', ]
UNIFIED_SOURCES += [ 'Simplecpp.cpp', ]
include('/ipc/chromium/chromium-config.mozbuild') #放在哪个库里 FINAL_LIBRARY = 'xul'
if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wshadow'] |
至此,C++组件编写完成。
2.2.2C++ 组件调用
C++的组件注册完成之后同样用前面的js组件来调用。
基于上个组件,修改gecko/dom/simplejs/UseComponent.js 添加C++组件测试部分。
"use strict"; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm");
const USECOMPONENT_CONTRACTID = "@mozilla.org/usecomponent;1"; const USECOMPONENT_CID = Components.ID("{1ff24790-5e74-11e1-b86c-0800200c9a66}"); //////////////////////////////////////////////////////////////
//test of javascript component /////////////////////////////////////////////////////////// dump("yahaha,========>start: javascript component==================>"); var usecompo =Cc["@mozilla.org/simplejs;1"].createInstance(Components.interfaces.nsISimpleComponent);//创建@mozilla.org/simplejs组件的实例,之后通过Components.interfaces.nsISimpleComponent找到你定义的接口,然后再调用你定义在接口下的接口函数 var result = usecompo.yourName;//the interface name in .idl dump("yahaha,...result: yourName = "+result); usecompo.changejs("yahaha,change after yourName"); dump("yahaha,========>end: javascript component====================>\n ");
///test of other exist component //////////////////////////////////////////////////////////// dump("yahaha,==========>start test1....instance of other cpp component in usecomponent.js "); var nfcservi =Cc["@mozilla.org/nfc/service;1"].createInstance(Components.interfaces.nsINfcService); dump("yahaha,nfc,,,,,,"+nfcservi); dump("yahaha,=========>end test1 ===================================>\n ");
//test of c++ component //////////////////////////////////////////////////////////////// dump("yahahacpp,==========>start,test of cpp component==================>"); var Simplecpp =Cc["@mozilla.org/simplecpp;1"].createInstance(Components.interfaces.nsISimplecpp);
dump("yahahacpp,before change()"); var result_simplecpp = Simplecpp.change("daye"); dump("yahahacpp,============>end of cpp component test====================>\n ");
var atest = Simplecpp.usecomponent("Atest"); dump("yahahacpp,after Simplecpp.usecomponent() result of usecomponent:"+atest); /////////////////////////////////////////////////////////////////// //以下代码有疑问参看js 组件实现部分 function useComponentImpl(){ dump("yahaha,this is a null component in useComponentImpl"); } useComponentImpl.prototype={ //const USECOMPONENT_CONTRACTID = "@mozilla.org/usecomponent;1"; //const USECOMPONENT_CID = //Components.ID("{1ff24790-5e74-11e1-b86c-0800200c9a66}"); contractID : USECOMPONENT_CONTRACTID, classID: USECOMPONENT_CID, QueryInterface: XPCOMUtils.generateQI([Ci.nsIUseComponent]),
duuuump:function(){ dump("yahaha, hello world???"); }, }
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([useComponentImpl]); |
编译,刷机,测试。相应的组件都正常调用。最后的log如下:
357:I/Gecko ( 180): yahaha,========>start: javascript component==================> 358:I/Gecko ( 180): yahaha,...result: yourName = Anonymous! 359:I/Gecko ( 180): Hello yahaha,change after yourName 360:I/Gecko ( 180): yahaha,========>end: javascript component====================> 362:I/Gecko ( 180): yahaha,==========>start test1....instance of other cpp component in usecomponent.js 363:I/Gecko ( 180): yahaha,nfc,,,,,,[xpconnect wrapped nsINfcService] 364:I/Gecko ( 180): yahaha,=========>end test1 ===================================> 366:I/Gecko ( 180): yahahacpp,==========>start,test of cpp component==================> 367:I/ ( 180): yohoho,yahahacpp,log from gecko dom simplecpp.cpp component,nsSimplecpp() 368:I/Gecko ( 180): yahahacpp,before change() 369:I/ ( 180): yohoho,yahahacpp,log from gecko dom simplecpp.cpp,Change() 371:I/ ( 180): yahaha,log from simplecpp.cpp after Changechange() 372:I/Gecko ( 180): yahahacpp,============>end of cpp component test====================> 374:I/ ( 180): yohoho,yahahacpp,log from simplecpp.cpp component,Usecomponent() 376:I/ ( 180): yahaha,log from simplecpp.cpp Usecomponent() 377:I/Gecko ( 180): yahahacpp,after Simplecpp.usecomponent() result of usecomponent:undefined 378:I/Gecko ( 180): yahaha,this is a null component in useComponentImpl |