摘要:FireBreath is a framework that allows easy creation of powerful browser plugins. A plugin built on FireBreath works as an NPAPI plugin or as an ActiveX control (windows only) and support could be added for other plugin types built in C++ as well.
开源项目FireBreath,是一个支持NPAPI的插件开发框架,它的诞生大大缩短了插件开发周期。了解更多:http://www.firebreath.org/。
在此框架基础上进行插件开发,可以不用考虑插件对各大浏览器的兼容性,只要浏览器商支持NPAPI就可以正确的运行你的插件。遗憾的是,Chrome从2014年开始已经开始向PPAPI转移,高版本的Chrome浏览器将无法使用FireBreath插件;另外,对国内而言,360浏览器使用的确实是Chrome内核,它应该也存在问题(未测试)。
但是,IE、火狐、QQ浏览器都能较好的支持NPAPI,FireBreath框架表现的非常完美。(注:2017年3月测试正常)。
[2017-5-1注:Chrome 42以上版本,火狐52以上版本不再支持NPAPI协议插件开发]
FireBreath作者——Richard Bateman,以及开源项目贡献者,他们都在朝着PPAPI努力,让FireBreath走的更远。
目录:
需要从github上下载FireBreath框架:https://github.com/firebreath/FireBreath。下载release版本,一般下载最新版。
FireBreath插件INFO文件是采用Python来配置的。由于Python3对Python2强制不兼容因素,这里推荐安装Python2最高版本(2.7):https://www.python.org/downloads/。
安装时,如果有添加Path的,直接勾上。如果没有,请确保将bin路径添加到Path中,便于后面方便执行Python命令。
FireBreath文件是用CMake来组织的,至于为什么?作者原话:“对于那些质疑我使用CMake是一个大缺陷的人,请你们先弄明白为什么要使用CMake,以及你有更好的代替方式的时候,再来谈论此问题。”
也对,在整个插件开发过程中,我们并不需要了解CMake是什么,更不需要知道它是如何工作的。你需要做的仅仅是:安装它!https://cmake.org/download/。这里需要叨叨几句,CMake下载时,你最好:
A. 下载x86版本
这里解释一下,可略过。FireBreath生成build文件时,需要使用类似于这样的命令:prep2013.cmd(prep2013x64.cmd为生成64位插件),一般为了兼容性,都使用prep2013.cmd,也就是x86这样就造成了CMake x64可能不能正常工作,实践证明确实是这样。
B. 下载.msi版本
Msi版本相对于zip版本有一个好处,可以勾选Add Path,也就是添加\bin路径到系统环境。不用多解释了吧。
对于只是学习FireBreath玩玩的看官,不必安装WiX。因为WiX它的全称叫做Windows Install Xml。从VS2010开始,Microsoft Visual Studio(VS)已经开始推荐使用WiX制作.msi安装包,微软后续的IDE已经逐渐不支持“生成->发布”来制作msi。
WiX顾名思义,旨在以配置XML来生成安装包,其可定制性极高。这里推荐下载v3.6版本:http://wixtoolset.org/releases/,因为高版本WiX的\bin目录下缺失setupbld.exe,可能FireBreath是更改了生成策略,不需要该exe或者是有了其他替代品。无论如何,FireBreath框架中需要用到setupbld.exe。
WiX是静默安装的,一般安装在:C:\Program Files (x86)\WiX Toolset v3.6,而且会自动配置系统环境变量,具体见下表:
变量名 | 变量值 |
---|---|
WIX | C:\Program Files (x86)\WiX Toolset v3.6\ |
但,它并没有更改Path变量。
根据FireBreath官网上说法,需要将v3.6后的反斜杠“\”删掉。同时,为了后面方便使用candle.exe、light.exe,最好继续配置Path,添加 : %WIX%\bin
至此,万事俱备。(当然,认为你VS2010或者更高版本已经安装好了,笔者是VS2013)
(以管理员运行命令提示符)该命令用于配置插件信息,同时会在FireBreath-master根目录下生成文件夹:projects。见下图,具体描述,见下表:
此命令将下载boost.tar.gz文件,同时在FireBreath-master根目录下生成build文件夹,并自动使用7-zip对boost.tar.gz文件解压归档。好了,至此基本完成框架配置。
点击FireBreath-master->build->FireBreath.sln,开启插件开发之旅。
此处,注意两点:
整个插件开发过程中,真正需要我们关心的只有以下几部分:
[2017-5-1修正:这里的插件生命周期,上图描述有问题,具体生命周期还是比较复杂,具体可以自行打印Log分析]
首先,将生成插件试试。点击->生成解决方案。
报错了,“错误 10 error : Failure in VariantWideStringTest: Unhandled exception: Not enough space d:\firebreath-master\tests\scriptingcoretest\variant_test.h 123 1 UnitTest_ScriptingCore”。
注意到是test内容,和框架无关。定位到variant_test.h文件,直接注释掉[Ctrl+A->Ctrl+K+C],再次“生成解决方案”。
显示:生成: 成功 17 个,失败 0 个,跳过 1 个。
经过上述生成,该dll已生成。位置:.\FireBreath-master\build\bin\DemoPlugin\Debug。接着,使用:regsvr32 npDemoPlugin.dll命令,注册该dll。(注:卸载regsvr32 \u npDemoPlugin.dll)
FBControl.htm是FireBreath官方给的一个HTML+JS调用插件的范例,位于:.\FireBreath-master\build\projects\DemoPlugin\gen。打开后如下图所示。
点击->允许阻止的内容->弹出Plugin loaded!,即表示插件加载成功。(使用IE、火狐、QQ浏览器等打开,都能正常运行。)
<a href="#" onclick="javascript:pluginValid()">Click me!a> <br />
方法pluginValid(),用于检测插件是否正常工作。(valid应该是FireBreath框架中的一个属性,使用get_、set_进行注册。)
function pluginValid(){
if(plugin().valid){
alert(plugin().echo("This plugin seems to be working!"));
} else {
alert("Plugin is not working :(");
}
}
<a href="#" onclick="javascript:testEvent()">Fire a test eventa> <br />
方法testEvent(),是DemoPluginAPI.h中暴露的一个方法,旨在提供一个Js直调的接口,具体内容在testEvent实现体中。以下为.h中的注册方式:
registerMethod(“testEvent”, make_method(this, &DemoPluginAPI::testEvent));
function testEvent(){
plugin().testEvent();
}
<a href="#" onclick="javascript:addTestEvent()">Activate click countera> <br />
这个非常重要,是FireBreath框架中,实现异步事件的核心。
function addTestEvent(){
addEvent(plugin(), 'echo', function(txt,count){
alert(txt+count);
});
}
DemoPluginAPI.h中,使用FB_JSAPI_EVENT来注册事件(可以类比成Java中的setOnClickListener()),示例echo如下:
FB_JSAPI_EVENT(echo, 2, (const FB::variant&, const int));
这里需要注意两点:
void DemoPluginAPI::testEvent(){
fire_test();
}
方式1:如,
registerMethod("testEvent", make_method(this, &DemoPluginAPI::testEvent));
// Method test-event
void testEvent();
方式2:如,
// Event helpers
FB_JSAPI_EVENT(test, 0, ());
void DemoPluginAPI::testEvent(){
fire_test();
}
假设你的插件需要依赖大量dll以及xxx.h文件,那么你可以这样,打开:.\FireBreath-master\projects\PoCPlugin\Win\ projectDef.cmake,添加如下(注意末尾无分号):
include_directories("D:/code/abc/include")
target_link_libraries(${PROJECT_NAME} debug "D:/code/efg/zhangsan.lib")
target_link_libraries(${PROJECT_NAME} release "D:/code/efg/zhangsan.lib")
这里谈到的调试,主要有两种:Log日志和浏览器调试。
FireBreath框架Log日志功能十分强大,也极易使用。三个步骤就可搞定:
add_firebreath_library(log4cplus)
void getLoggingMethods(FB::Log::LogMethodList& outMethods){
// The next line will enable logging to the console (think: printf).
outMethods.push_back(std::make_pair(FB::Log::LogMethod_Console, std::string()));
// The next line will enable logging to a logfile.
outMethods.push_back(std::make_pair(FB::Log::LogMethod_File,"D:\\debuglog\\baz.log"));
// Obviously, if you use both lines, you will get output on both sinks.
}
FBLOG_FATAL("something()", "balabala");
在D:\debuglog\baz.log文件中去查看你的日志吧~
打开VS2013->在code中打上断点->点击“调试(D)”->“附加到进程(P)…”->选中需要调试的浏览器。
Windows Installer XML (WiX) 平台是一组工具与规范,使您能够创建 Microsoft Windows Installer 数据库文件(MSI 和 MSM)。WiX 工具承袭了根据源代码创建可执行文件所使用的传统编译和链接模型。WiX 的源代码是使用 XML 文件编写的。WiX 源代码经过预处理、编译与链接,以创建 Windows Installer 数据库。摘自:http://baike.baidu.com/item/wix/61705
FireBreath框架已经自动生成了一部分.wxs内容,下面根据自己的需要对其进行修改,位置:.\FireBreath-master\projects\DemoPlugin\Win\WiX\DemoPluginInstaller.wxs
两步,
<Component Id="UniqueComponentId" Guid="{2EBB19F5-1FA9-4028-8229-D0E28E80BC34}">
<RegistryValue Root="HKCU" Key="SOFTWARE\${COMPANY_NAME}\${PLUGIN_NAME}\${FBSTRING_PLUGIN_VERSION}" Name="Others" Type="string" Value="${FBSTRING_PLUGIN_VERSION}" KeyPath="yes" />
<File Id=" UniqueId1" Source="D:\code\xyz\zhangsan.dll"/>
<File Id=" UniqueId2" Source="D:\code\xyz\lisi.dll"/>
<File Id=" UniqueId3" Source="D:\code\xyz\wanger.exe"/>
<File Id=" UniqueId4" Source="D:\code\xyz\mazi.jpg"/>
Component>
<Feature Id="MainPluginFeature" Title="${FBSTRING_ProductName}" Level="1">
//…省略
<ComponentRef Id="UniqueComponentId"/>
Feature>
注意事项:
ApplicationProgramsFolder表示开始栏,由于插件没有启动exe,这里只添加了个Uninstall。
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="{AE43936B-93B7-455C-8309-A66690FC8F43}">
<Shortcut Id="UninstallDemoPlugin" Name="Uninstall" Target="[SystemFolder]msiexec.exe" Arguments="/x [ProductCode]" Description="Uninstall Demo Plugin" />
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall" />
<RegistryValue
Root="HKCU"
Key="SOFTWARE\${COMPANY_NAME}\${PLUGIN_NAME}\${FBSTRING_PLUGIN_VERSION}"
Name="Others"
Type="string"
Value="${FBSTRING_PLUGIN_VERSION}"
KeyPath="yes" />
Component>
DirectoryRef>
<PropertyRef Id="NETFRAMEWORK30" />
<PropertyRef Id="NETFRAMEWORK35" />
<PropertyRef Id="NETFRAMEWORK40CLIENT" />
<PropertyRef Id="NETFRAMEWORK40FULL" />
<Condition Message='This product requires the .NET Framework 3.0 or higher version installed.'>
Condition>
WiX本身提供了五种安装界面:WixUI_Advanced、WixUI_FeatureTree、WixUI_InstallDir、WixUI_Minimal和WixUI_Mondo。
WixUI_Advanced提供了与WixUI_Minimal类似的一键安装形式。不过其在协议界面提供了配置高级选项的按钮。在高级选项中,我们可以选择该产品是安装给当前用户还是给所有用户的,在Wix文件中需要一个Id为“WixAppFolder”的属性(Property),指定默认选择当前用户(WixPerUserFolder)还是所有用户(WixPerMachineFolder)。另外,需要一个Id为“ApplicationFolderName”的属性来指定产品所安装在的默认文件夹。
<Property Id="ApplicationFolderName" Value="HelloWorld"/>
<Property Id="WixAppFolder" Value="WixPerUserFolder"/>
"WixUI_Advanced"/>
WixUI_FeatureTree与WixUI_Mondo的区别是WixUI_FeatureTree省略了安装类型对话框。协议同意对话框之后就直接到部件(Feature)选择对话框了。当我们的产品默认是安装所有部件时,更推荐采用WixUI_Feature而不是WixUI_Mondo。其用法是:
<UIRef Id="WixUI_FeatureTree"/>
WixUI_InstallDir不支持用户选择安装的部件,但是其增加了让用户选择安装目录的对话框。在Wix文件中需要一个Id为“WIXUI_INSTALLDIR”的属性来产品要安装的目录的ID(ID必须是全部大写,大写的目的是为了能够让用户在对话框中选择的自定义目录能够回写到相对应ID的Directory元素)。其在下面的用例中的用法是:
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER"/>
<UIRef Id="WixUI_InstallDir"/>
WixUI_Minimal是最小的预定义对话框集合,其将Welcome对话框和协议同意对话框结合,选择协议后就直接安装了。这种对话框集合适用于我们的产品没有自定义部件以及不支持变更安装目录的情况。其用法是:
<UIRef Id="WixUI_Minimal"/>
WixUI_Mondo提供了Welcome、协议同意、安装类型选择、部件选择等自定义安装的对话框,但其不支持安装目录的变更。当我们的产品默认情况下不安装全部部件时,这样典型安装(typical)和完全安装(complete)之间就有个明显的区别,此时推荐使用WixUI_Mondo。其用法是:
<UIRef Id="WixUI_Mondo"/>
这里注意:需要准备两张图片,
图1:[493px x 58px],位置:./Win/Bitmaps/bannrbmp.bmp;
图2:[493px x 312px],位置:./Win/Bitmaps/dlgbmp.bmp。
<WixVariable Id="WixUIBannerBmp" Value="${CMAKE_CURRENT_SOURCE_DIR}/Win/Bitmaps/bannrbmp.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="${CMAKE_CURRENT_SOURCE_DIR}/Win/Bitmaps/dlgbmp.bmp" />
<UI Id="UserInterface">
<Property Id="WIXUI_INSTALLDIR" Value="TARGETDIR" />
<Property Id="WixUI_Mode" Value="Custom" />
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="9" Bold="yes" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="FatalError" />
<DialogRef Id="UserExit" />
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1Publish>
UI>
<UIRef Id="WixUI_Common" />