使用FireBreath写浏览器插件

来源:互联网 发布:java四则运算代码 编辑:程序博客网 时间:2024/04/30 20:39

一. FireBreath介绍

    FireBreath是一个跨平台和跨浏览器的插件开发框架,通过Firebreath写的插件可以运行在windows,linux和mac上的IE,Firefox,Chrome,Opera,Safari等浏览器上。

FireBreath的主要开发者是Richard Bateman, 他工作在Facebook,FireBreath里面使用了不少Facebook贡献的代码。Firebreath使用New BSD授权或LGPL授权,官网在http://www.firebreath.org, 代码可以从https://github.com/firebreath/FireBreath下载。

    FireBreath适合如下场合:

1. 需要提供新的功能给js调用(扩展js功能)

2. 需要实现类似于Flash或media player那样功能

3. 给Chrome写Extension 

4. 其他非扩展浏览器外壳的功能的场合

    在IE上,我们可能经常会看到如下的工具条:


    google工具条属于浏览器外壳部分的扩展,并不属于ActiveX插件,而是属于BHO(Browser Helper Object)。无论是ActiveX还是BHO,都是基于COM实现的。在浏览器中使用COM的好处是扩展能力超强,好被其他程序重用(例如QQ的聊天窗口的一部分就是用IE做的),坏处是可能导致启动速度变慢(需要合理优化)。BHO可以实现很强大的功能,例如Chrome Frame就是利用BHO将Chrome嵌到IE里面运行。Firefox实现了一套与COM类似的技术:XPCOM(Cross Platform Component Object Model),XPCOM虽然说是跨平台,但整体来说还是弱于COM。

    在IE中还有一种扩展js功能的是ie external对象(http://msdn.microsoft.com/en-us/library/ms535246(VS.85).aspx),现在很多程序就是用这种方法扩展js功能,例如迅雷,QQ等。

    Firebreath现在主要是支持桌面系统的浏览器,还不支持Android,Iphone等平台。Firebreath实现采用了boost库,也使用了异常处理机制,这导致Firebreath在Android和Iphone等平台移植时会稍微困难一点,不过网上已经有android版的boost库。我曾经专门问过Richard Bateman是否有计划支持Android,他表示暂时还没计划。其实在嵌入式设备上的简单的做法是:参考Firebreath的架构,而不是使用其代码,否则可能会出现效率问题。


二. Firebreath的实际应用

    去年我在给公司开发PC上Widget引擎,这个Widget引擎上面运行WebApp应用,界面和应用逻辑使用HTML+CSS+Javascript实现,而网页无法实现的功能则由CAR实现(CAR是与COM类似的技术,不过支持反射和AOP编程)。在这个引擎上开发的第一款应用是:类似于91手机助手的Android PC套件,整个PC套件的UI部分全部用HTML+CSS+Javascript实现,没有使用任何原生的控件(例如button),而JS无法完成的功能(例如获取联系人,安装apk包)则由CAR实现,然后在js中调用CAR。


    其实扩展js功能,还有一种方法是使用现在网站常用的web service:在程序中实现一个http server,然后在http server中实现JS无法完成的功能,例如PC套件里面的获取联系人,最后再使用Ajax去调用。但为什么不选用这种方法呢?主要是基于如下几点原因:

1. 管理同步调用非常麻烦

2. js得处理参数转换

3. webservice除了本进程可以调用外,其他应用可以调用,安全认证比较麻烦

4. http无状态,长时间执行的操作得用一些特殊方式处理

5.  保持长连接也挺麻烦,回调机制的实现比较难办

6.  对象的生命周期管理难以与js的垃圾回收同步

7.  与本地UI交互困难。例如由于网页只能打开特定的对话框,如果应用要打开特定对话框(例如选取文件夹),webservice实现比较麻烦 。

    在我看来,C++里面的对象对JS而言,就应该和JS里面的原生对象(例如Date)是一样的,可以new对象,可以和原来一样地调用C++对象里面的方法,可以注册事件和触发事件,可以被垃圾回收。基于这个原则,并利用CAR的反射和事件回调机制,可以将CAR实现C++类动态注入到JS引擎中,使得JS可以轻松地调用C++对象。举个例子:

    先写一个接口描述文件HelloWorld.car:

[plain] view plaincopy
  1. module  
  2. {  
  3.     interface IHelloRef {  
  4.         test([in] Int32 n, [out] AStringBuf<32> ret);  
  5.    }  
  6.     //回调函数  
  7.     callbacks JHelloRef {  
  8.         onTestCallback();  
  9.     }  
  10.   
  11.     class CHelloRef {  
  12.         interface IHelloRef;  
  13.         constructor([in] Int32 abc);  
  14.     }  
  15. }  


    然后用emake编译HelloWorld.car可以生成相关的.h和.cpp文件,修改cpp文件为如下:

[cpp] view plaincopy
  1. #include "CHelloRef.h"  
  2. #include "_CHelloRef.cpp"  
  3. #include <stdio.h>  
  4. #include <windows.h>  
  5. ECode CHelloRef::test(  
  6.     /* [in] */ Int32 n,  * [out] */AStringBuf* ret)  
  7. {  
  8.     printf("hello world\n");  
  9.     //触发回调  
  10.     Callback::onTestCallback();  
  11.     ret->Copy("hello world");  
  12.     ret->Append(n);  
  13.     //睡眠3秒后返回结果  
  14.     ::Sleep(3000);  
  15.     return NOERROR;  
  16. }  
  17. ECode CHelloRef::constructor(Int32 abc)  
  18. {  
  19.     return NOERROR;  
  20. }  


    在js中可以这么调用:

[javascript] view plaincopy
  1. var hello = new HelloWorld.CHelloRef(1234);  
  2. hello.onTestCallback = function(){  
  3.        window.alert('onTestCallback ’);  
  4. };  
  5. //调用test将触发onTestCallback,test的执行将耗时3秒以上,js引擎也会“卡死”3秒以上  
  6. var ret= hello.test(11);  
  7. //ret等于hello world11  
  8. alert(ret);  
  9. //异步调用,下面的js代码可以继续执行,而不至于js引擎卡死,结果返回时调用function  
  10. hello.test(32, function(ec, ret){  
  11.     //ret等于hello world32  
  12.     alert(ret);  
  13. });  


    最初我是采用在Webkit外壳上注入CAR的方式,但后面想在IE内核上跑应用时发现还得实现一遍注入,因为浏览器之间用的JS引擎接口不一样。即使是Webkit,也有多个JS引擎,例如V8和SquirrelFish,我不可能每个js引擎都实现一遍。但浏览器主要由两种插件体系:NPAPI(chrome里面加入了Pepper Plugin API )和ActiveX,一个体系下插件的接口是一致的,因此使用插件方式只需要编写两种方式的注入,有了Firebreath之后更简单,只需要实现一致方式的注入就可以在各个浏览器上跑了。下面是在chrome里面跑pc套件的截图:


    图上的url(http://moyang.org/pcwidget/widgets/application/com.kortide.app.pcSuite/jdy.html )里面运行的是一个测试版,还有一些缺点(例如图片没有按需加载,第一次使用需要等待图片加载完)。IE里面排版还有点问题(万恶的CSS浏览器兼容),推荐在chrome或firefox里面运行。有android手机的可以试试,好久没弄这个了,跑不起来我可不负责^_^。

    虽然插件是一种比较方便的方式,但在手机上通过插件来扩展并不是一种好的方式,而且插件在效率上确实有点损耗,所以应该尽量在浏览器外壳或js引擎里面注入,这样Webapp运行起来才有好的用户体验。插件将各个js引擎的API进行了统一,有优点也有缺点,属于一种穷人的解决方案。

好了,闲话讲完了,后面将接着介绍如何通过Firebreath写插件。

三. FireBreath Helloworld


首先需要创建一个自己插件的工程,首先要安装Python,然后进入命令行后,在Firebreath的源代码目录下执行:python fbgen.py,这是将提示输入一些信息:


Plugin Name:插件的名称,后面生成的dll将用np+这个名字的方式


Plugin Identifier:插件友好的名字,生成的入口cpp文件将会为这个名字


Plugin Prefix:前缀,主要用于visual studio工程


Plugin Mime type:这个比较重要,NPAPI接口的浏览器使用这个标志插件,网页中创建插件时要用。例如网页中创建flash的代码:


[html] view plaincopy
  1. <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="550" height="400" id="movie_name" align="middle">  
  2.     <param name="movie" value="movie_name.swf"/>  
  3.     <!--[if !IE]>-->  
  4.     <object type="application/x-shockwave-flash" data="movie_name.swf" width="550" height="400">  
  5.         <param name="movie" value="movie_name.swf"/>  
  6.     <!--<![endif]-->  
  7.         <a href="http://www.adobe.com/go/getflash">  
  8.             <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player"/>  
  9.         </a>  
  10.     <!--[if !IE]>-->  
  11.     </object>  
  12.     <!--<![endif]-->  
  13. </object>  


其中type="application/x-shockwave-flash"的application/x-shockwave-flash就是flash插件的mimePlugin Description:插件的描述信息,会生成在dll的属性里面Plugin has no UI:插件是否有UICompany Name:开发插件的公司名字CompanyIdentifier:公司标志Company Domain:公司的域名,主要是firefox浏览器需要完成后,会在projects生成对应的工程文件,其中有cpp和h文件,也有一个PluginConfig.cmake文件,这是一个cmake脚本。cmake是一个跨平台的编译工具。在windows上,cmake可以转换为vs工程后再编译。在windows上,编译插件主要依赖CMake,git和Visual Studio。需要将Python,CMake,git的目录加入到PATH,然后执行prep2005.cmd(如果用vs2008或其它版本的vs,执行对应的prep***.cmd即可)。为了简单,我写了一个bat

[vb] view plaincopy
  1. set PATH=D:\tools\Python;D:\tools\CMake\bin;D:\tools\Git\bin;%PATH%  
  2. call "%~dp0\prep2005.cmd"  



这之后就可以在build目录下看到一个FireBreath.sln,用vs打开就可以编译了。编译完后在build\bin\插件名\Debug目录下生成一个np****.dll的文件,在命令行执行regsvr32.exe np****.dll就可以注册插件了。


之后就可以用浏览器打开build/projects/插件名/gen/FBControl.htm这个页面来测试插件是否工作正常了。在FBControl.htm我们可以测试echo,事件注册和回调等。


更加详细的过程可以参考http://www.firebreath.org/display/documentation/Creating+a+New+Plugin+Project


Firebreath是使用cmake编译,PluginConfig.cmake是CMakeLists.txt的一个片段。如果想修改了PluginConfig.cmake文件或将三方工程加入编译,可以参考src\libs目录下各个工程的cmake.txt文件。


用Firebreath写插件需要有C++的基础,熟悉STL,对boost有一定的了解(主要是智能指针,bind和function)。


在正式实现插件之前,我们可以先看看np****.dll的导出函数:


 


NP_GetEntryPoints
NP_Initialize
NP_Shutdown
DllCanUnloadNow
DllGetClassObject
DllInstall
DllRegisterServer
DllUnregisterServer


其中NP开头的都是NPAPI浏览器需要的,并且NPAPI规定插件dll或so的名字必须为np开头,否则浏览器不认。Dll开头的是ActiveX插件需要的,也就是COM里面常见的导出函数。下面一节将会对NPAPI和ActiveX做专门的介绍。
1 0