有一些东西想通过火狐扩展来实现;因为觉得可以省一些事。于是借此机会了解了firefox开发的整个体系。正如所想的,不仅可以开发开发基于火狐的扩展,同时也可以开发一个类似火狐的独立应用程序。
了解火狐技术体系的过程是个断断续续的过程;也同样经历过一个百川汇海的过程。
一. 几个术语
最开始的时候,总是面对着火狐的一些术语似是而非;最后梳理清楚,然后一切恍然大悟。
插件plugin附加组件addon 主题theme语言包language pack扩展extension 传统扩展traditional extensions自引导扩展bootstrapped extensionsadd-on sdk扩展(曾用名Jetpack,自引导扩展的一种)
1. 插件和扩展的区别
官方的插件和扩展的区别是这样解释的:
Extensions are small add-ons that add new functionality to your Mozilla program.
Plugins are programs that allow websites to provide content to you and have it appear in your browser. 扩展是能够给Mozilla增加一些新功能的附加软件,插件是允许网站向你提供内容并在浏览器中显示的程序).
通俗的讲,“扩展”是基于Firefox本身增加的一些实用功能,
而“插件”则是在Firefox之外独立编写的程序,用于显示网页中的特定内容如Flash、视频和Java 等.
不过初看时总是看不懂;后来了解了什么是插件和扩展的例子才明白其二者是大大的区别的,不过还是无法用文字来描述。不知道以下的解释能否行得通:
浏览器基本的功能是用来显示html网页的。网页的内容自身是可扩展的,这部分要显示就是通过内容的插件;浏览器对网页除了显示外,再做一些额外的事,那就是功能的扩展。
举例来说,基本的网页是没有flash的,那么显示flash内容,就要通过插件了。而对于网页,除了显示外,再附加额外的截图等功能,就是通过扩展了。
从底层来看,插件就是html内容中的<object>和<embed>标签;扩展与内容无关,只与浏览器有关。
算了,不解释了,越说越迷糊。
2. 附加组件第三者插足
有了扩展和插件,其他的一切都好理解。可是官方的很多文档都出来一个附加组件。这个插足的第三者,又是一个什么概念呢?不弄明白,总有一天碰到别人都不好打招呼,不怕认错人也怕叫做人。
官网上的某处,说附加组件包括插件、各种扩展、主题和语言包;这样看来,附加组件应该是全称。但是在火狐的“附加组件管理”中,附加组件一项只能搜索扩展和主题,不能搜索插件;所以还是采用上面的划分。
好了,不在名词之称上继续纠缠。我们的最终目的其实还是只要区分扩展和插件即可。
二. firefox的技术体系
如何开发插件、扩展;如何开发独立的类Firefox应用程序?这是我们的目标。要实现此目标,必须要了解相关的火狐可以提供哪些,在哪里提供。
总的来说,基于上面的技术体系,可以按图索骥找相关的线索。
火狐要解决的问题主要是一个基础三个扩展:
一个基础:客户端网页内容和js的处理扩展1-可扩展的网页内容:也就是如何方便插件的编写扩展扩展2-可扩展的浏览器界面:也就是如何方便扩展的编写扩展3-可扩展的浏览器功能:也就是如何方便第三方库的引入
解决上述问题,firefox真是抽象又细化,揉碎又提炼,获取了不同的收成。
1. 显示渲染消息层的收获----XUL
在解决基础和扩展1的问题时,都涉及到显示问题。
网页内容需要渲染显示,firefox的界面也需要渲染显示。基本的显示,控件,布局。html的控件有按钮、文本框;应用程序的界面控件有菜单、状态栏。html好像少点布局控件,但是第三方js和css界面库提供了很多;而应用程序的界面绝对少不了布局组件。网页可以通过js来进行消息扩展,应用程序的消息响应难道会少得了?
总之这就是一码事。将渲染显示层公共提出来,既用来显示网页,也用来显示界面;js既可以响应网页消息,也可以用来响应界面的消息。这就是XUL。
2. 功能扩展的收获-XPCOM
无论是插件还是扩展,都可能需要调用第三方的库。强有力的扩展,总是免不了调用一些已有的功能。python倘若不能调用C,那他就要像JAVA那样辛辛苦苦的提供全套解决方案。firefox如果全部功能都要自己实现,那永远赶不上兵强马壮的ie。所以,如何调用第三方的功能,还要支持跨平台的解决方案呢?
Windows上有微软的COM组件,但这用到微软的注册表;linux上没有注册表之说。firefox的解决方案就是XPCOM。不需要注册表的com。一切都很像,所以也就没有看起原理,只看了如何使用。
3. 可扩展的收获-XulRunner
Firefox最后的设计太过精致了。这么好的设计,这么好的扩展,这么好的可选的基础组件,只用来开发一个firefox太过可惜了。所以,还顺带做了其它几个类似的软件,例如Thunderbird。不过,独乐乐不如众乐乐。所以就有了给第三方的独立应用程序开发sdk或者简单的独立平台XulRunner。基于XulRunner可以以类似扩展的方式定制功能,但是独立于firefox而运行。
三. firefox插件的开发
firefox是无穷尽可扩展的,不过可扩展不是firefox一家的专利。现在甚至可以这么说,可扩展性是软件的基本要求。如果软件不具备可扩展性,都不好意思跟别人打招呼。
插件是火狐可扩展性的一个体现。不过,正如前面所说,插件其实是基于内容的。基于内容的插件,不仅火狐需要,IE也需要,chrome也需要,safari也需要。那么,插件也应该支持这些所有的浏览器。
这就是现在经常要解决的两个问题:纵向单个需要扩展;但是横向这个扩展又必须满足所有一切。浏览器的插件解决方案,最好的还是看FireBreath。他的插件可以满足上述要求。
1. 基于html标准的插件生命周期
浏览器调用插件,是通过插件关联的对象。浏览器遇到插件的对象有两种方式:
独立式:此时是打开特定后缀名的文件嵌入式:此时是网页中有特定mimetype类型的object标签
所以,一个插件,必须在内部声明自己关注的mimetype或者后缀。
此外,插件通常具备的功能:
可与Windows交互:该如何响应消息,如何显示自己可与当前网页交互:该如何获取插件所在网页的信息可与js交互:更多的是插件提供的方法如何共js在脚本中方便调用
运行期间,每个内容都会对应一个插件的实例。具体可以参见/display/documentation/Plugin+Lifecycle。
程序实现中要看看以下特点如何体现。
2. FireBreath 插件的程序结构
从这里下载/display/documentation/Download,注意分别下载源代码和FireBreath-boost。看这里来进行编译/display/documentation/Building+on+Windows。重点研究{$FireBreath}\examples\FBTestPlugin。编译调试查看源代码是最好的了解方法:
用prep.cmd examples生成vs的解决方案;
调试时通过附加进程进行。注意ie的进程就是iexplorer.exe;但是火狐4以上对应的进程不是firefox.exe,而是plugin-container.exe;
调试时,用浏览器打开
{$FireBreath}\examples\FBTestPlugin\test.html,即可看到效果。
分析源代码,发现插件的执行流程:
浏览器会维护一个mimetype和插件的列表;一个插件可以有多个mimetype。但是通常一个mimetype只会对应一个插件。在网页中,当遇到一个mimetype时,会找到对应的plugin,然后创建并交将控制权交给插件对象。
如何创建插件对象呢?每个插件必须要有固定的API,同时必须要有个工厂对象FB::FactoryBase。这是固定的约定。一个插件是一个dll,但是该dll内部其实可以有多个逻辑插件。通过FB::PluginCorePtr createPlugin(const std::string& mimetype)来实现mimetype逻辑和插件的对应。每个插件需要考虑消息与windows进行交互,也就是鼠标和显示等各种信息;这正是
FB::PluginCorePtr的拿手好戏。该类为了模拟Windows的消息分派机制,也采用宏实现。所以有宏,还是常见的消息名,看上去就像Windows Style了。
特别注意的是,这里终于看到了我一直想看的ondraw,原来HDC是这么获取的。有了HDC,那么后面可以遐想的故事就多了。不过还有一点疑惑,有了HDC,常见的Windows显示都出来了;但是那些Windows的显示风格会和HTML的渲染风格完全不一样,插件在此会和背景和谐吗?不过猜想,HTML的渲染也应该是HDC开始的,只不过做了很多很多的封装。如何将HTML渲染DC获取,或者插件中也使用XUL元素,进而实现组合定制控件,该如何实现呢?这一步继续下去,其实就是如何基于XUL来做自绘制控件......
插件还必须和JS交互。这是通过virtual FB::JSAPIPtr FB::PluginCore:createJSAPI();来实现。PluginCore和JSAPI各司其职,前者负责消息;后者负责给js提供方法。其实二者完全可以集合在一个类中,也就是双重继承。此时只需要createJSAPI时返回this即可。但是最佳的模式,还是将二者分开。
插件毕竟是在html中,如何获取html呢?交给它就行PluginCore::m_host->getDomWindow。
总的来说,强烈建议
从头到尾顺序看看官网/display/documentation/FireBreath+Home。
3. Firefox 插件的位置
firefox的插件在什么地方呢?firebreath只是推荐的最优插件开发方法,还有很多种开发方法。而插件的安装地方也有很多。firefox如何知道plugin都安装了何处呢?看如下参考:/en-US/docs/Gecko_Plugin_API_Reference/Plug-in_Basics#How_Plug-ins_Are_Used。对应windows来说,如下:
系统环境变量MOZ_PLUGIN_PATH对应目录
当前用户{$cur_user}\Application Data\Mozilla\Firefox\plugins
应用程序的plugin目录。一些应用程序可以自带火狐扩展,此时,只要将插件放在所在plugin目录即可
{$firefox}\plugins目录
也建议看看firefox plugin的相关信息/en-US/docs/Gecko_Plugin_API_Reference。
四. firefox扩展的开发
扩展的内容也很庞大。该如何下手呢?
扩展应该具备的功能,实际上和插件一样:
可以调用第三方库来增强功能可以增强firefox自身对网页显示的功能此外还有一条,扩展有自身的一套包格式
具备这三种特点,扩展也经过了三代的发展,发展的轨迹由功能最强到开发最简单。这条历史就像COM的发展史一样,由技术派走向了大众派。
1. 扩展的包结构
以下是一个传统扩展的逻辑包结构,或者目录结构。详细的信息参考/zh-CN/docs/XUL_School-840092-dup/Getting_Started_with_Firefox_Extensions:
xulschoolhello1
chrome.manifest(扩展的包结构和扩展方法描述)install.rdf(扩展的名称等配置信息)content(扩展的界面和脚本消息) browserOverlay.xulbrowserOverlay.jsskin(可选,扩展的外观配置) browserOverlay.csslocale(可选,扩展的语言配置) en-US browserOverlay.dtdbrowserOverlay.properties
注意,包是扩展的逻辑结构,实际目录是物理结构。逻辑上,一个扩展两个配置三个目录。
此外,了解一个扩展,最主要的方法就是查看chrome.manifest。因为这是查看扩展的结构,以及扩展的自定义方法的大纲视图。通常,类似如下:
;逻辑包结构 content myapp jar:chrome/myapp.jar!/content/ locale myapp zh-CN jar:chrome/myapp.jar!/locale/zh-CN/ skin myapp classic/1.0 jar:chrome/myapp.jar!/skin/ ;界面 overlay chrome://browser/content/browser.xul chrome://myapp/content/ui.xul overlay chrome://myapp/content/main.xul chrome://myapp/content/left.xul overlay chrome://myapp/content/main.xul chrome://myapp/content/right.xul overlay chrome://myapp/content/main.xul chrome://myapp/content/overlay/tab1.xul overlay chrome://myapp/content/main.xul chrome://myapp/content/overlay/tab2.xul ;自定义接口,供js调用 interfaces components/iTest1.xpt interfaces components/iTest2.xpt ;自定义接口的实现对象 binary-component components/myapp_XPCOM.dll
一个扩展可以使一个xpi文件;或者是一个目录。
上面的声明,充分体现了包的逻辑结构,也展示了实际目录既可以是物理目录,也可以是压缩目录(jar)。xpt后缀是自定义接口编译后的二进制文件;接口的实现对应着动态库(dll 或 so)。
通常,传统的扩展包括的包结构和自定义方法声明;这些是所有的扩展开发都具备的。传统的扩展安装后要重启火狐;这一点好像不是什么致命的问题,但是在实际过程中安装插件就想立刻使用。此时重启,可能打断用户的上下文场景,是叔可忍,婶不可忍。这点是分家后的chrome引以为戒,也是firefox不得不改的。此外,传统的扩展,采用XUL作为界面层。XUL相比于html,提供了更多的界面元素,特别是布局元素。但是引入一个新的语言,学习成本太高;这个是firefox开发希望酌情改进,降低难度的。
2. 扩展的开发模式转变
以上,说了火狐要改进的亮点:避免安装后重启,避免学习XUL。
自引导扩展bootstrapped extensions解决了前面一个问题;add-on SDK同时也解决了第二个问题。
自引导扩展参见“/zh-CN/docs/Extensions/%E5%BC%95%E5%AF%BC%E5%9E%8B%E6%89%A9%E5%B1%95”。
3. 最推荐的扩展开发方法:add-on SDK
add-on SDK扩展,也叫做jetpack,安装后不需要重启就可以使用;同时,主要使用JS+HTML+CSS来实现界面效果。详细的说明请看:“/en-US/developers/docs/sdk/latest/”。
先安装(/en-US/developers/docs/sdk/latest/dev-guide/tutorials/installation.html),再试用,通过例子学习。
安装过后,通过cfx init来创建基本的目录结构,同时可以打包cfx xpi和运行cfx run。
重点关注的是在add-on SDK下,是否也可以使用XPCOM。发现果然可以。功能组件还是按照老方法来创建,但是功能组件的调用,在SDK下更方便。
五. 开发独立的类firefox程序
火狐的扩展实际上是一个指定目录结构的包,火狐自身实际上也是一个指定目录结构的包。只要是指定目录结构的包,它可以独立运行(例如火狐自己),或者运行在火狐中(例如扩展)。独立运行的不一定只是火狐自己,任何一个这样的包都可以独立运行,成为一个独立的应用程序。此时,就是XULRunner出场了。
看一个例子可以明白:/en-US/docs/Getting_started_with_XULRunner。不过该例子下载后好像不能运行,可能是格式太老原因。下面这个例子就可以直接运行:/detail/problc/3370885。虽然简单,但是hello world是一切的开始。
如何运行了。下载XULRunner,最好将该目录添加到系统环境变量中。然后XulRunner.exe Application.ini即可。例如,xulrunner.exe "D:\Mozilla Firefox\application.ini"就可运行起火狐。
如果没有xulrunner,但是有firefox,想验证下应用程序的效果,可以用firefox来启动,例如:"D:\Mozilla Firefox\firefox.exe" -app "G:\MyProject\FF\myappa\application.ini"。
XULrunner程序的一切效果都如扩展,可以使用XUL,可以使用XPCOM,可以使用JS。这么看来,这天生就是一个支持JS脚本,且具备浏览器渲染的程序。目前很多directui,以及通过内嵌浏览器利用网页做界面的程序,实质上都可以下岗了。
不过,为什么360杀毒软件越做越漂亮,好像利用XUL难以达到效果。不过,这实际上是因为自定义效果的原因。但是总体来说,XUL内部已经具备开发一切界面效果的潜力,只要去挖掘。
相关的资料可以参考:/zh-CN/docs/XULRunner。