本报告的工作内容是对firefox源代码中跟它的扩展(extensions)部分相关的代码进行研究,总结得到firefox的扩展(extensions)相关部分的架构,并尽量细致的分析extensions安装过程,启动过程和相关功能类之间的关系。
本报告将在第三节详细的介绍firefox扩展的结构和相关的代码关系。
Extensions添加新功能到Mozilla Firefox中。Extensions可以简单添加一个工具栏按钮,也可以实现一个完整的新功能。Extensions可以让firefox更加适合个人需要。
Extensions跟plugins(插件)不同。插件帮助浏览器显示特殊内容,例如播放多媒体文件。常见的插件是flash player。而Extensions也跟搜索引擎插件不同,搜索引擎插件只是在搜索栏里边多加入一个搜索引擎地址而已。
一个扩展,通常是一个XPI(Cross-Platform Installer Module)包,其实是一个zip类型的压缩包,里边包括必须的文件。在下边的图1中,显示了一个标准的扩展包括的文件和文件的目录结构。
图 1 firefox扩展目录结构
在图 1中,content 目录下面存放的是扩展的描述界面的 XUL 文件和增加行为的 JavaScript 文件。locale 目录存放的是本地化相关的文件。如果需要支持英文和中文,就可以在 locale 目录下面新建 en-US 和 zh-CN 目录来存放相应的本地化字符串。skin 目录存放的是一些 CSS 文件,用来定义扩展的外观。chrome.manifest 是 Chrome 注册的清单文件(参见 2.2节)。install.rdf 分别包含了扩展安装的信息。
Chrome 指的是应用程序窗口的内容区域之外的用户界面元素的集合,这些用户界面元素包括工具条,菜单,进度条和窗口的标题栏等。Chrome 提供者能为特定的窗口类型(如浏览器窗口)提供 chrome。有三种基本的 chrome 提供者:
l 内容(Content):通常是 XUL(XML User Interface Language) 文件。 而XUL文件将会指定扩展在Firefox中运行时表现的界面和功能。XUL文件是一种Javascript文件,设计的目的是为了描述窗口和对话框的内容。
l 区域(Locale) :存放本地化信息。
l 皮肤(Skin):描述 chrome 的外观。通常包含 CSS 和图像文件。
XULRunner 项目提供一套称为XULRunner的Mozilla运行支持包,用于启动基于XUL+XPCOM(见2.4节)的程序,例如Firefox,Thunderbird,Sunbird。它提供各种机制,包括安装,升级,卸除这些软件的功能。
而Firefox整个程序主界面和扩展的界面都是由XUL文件来描述的,所以在Firefox运行过程中,和扩展使用过程中,都是靠XULRunner来支持。
XPCOM(Cross Platform Component Object Model)是一种跨平台组件对象模型,类似于微软的 COM。它有多种语言系结(Language Binding),使 XPCOM 组件可使用并实现于C++、JavaScript、Java 及 Python。XPCOM 的界面是由称为 XPIDL (Interface Definition Language)所定义。
XPCOM 自身提供了一套核心组件和类别,例如,档案和内存管理、线程、基本数据结构(strings, arrays, variants)等。大多数 XPCOM 组件并非由核心组件所提供,而是由其他平台或应用程序、甚至是延伸套件所提供。
而在Firefox中,绝大多数的功能都基于XPCOM机制。例如Firefox为扩展开发提供的接口都是用XPCOM方式来实现的。
例如gRDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
.getService(Components.interfaces.nsIRDFService);
将获取RDF模块的nsIRDFService实例。
XPInstall(Cross-Platform Install)是Mozilla系列软件或者其他基于XUL的软件中用于安装extensions的技术。在Firefox2.0的源代码中,有一个名为xpinstall的文件夹,存放跟XPInstall模块相关的代码。
另外需要特别指出Firefox的扩展分为普通默认扩展和定制安装扩展。普通默认扩展没有定制的安装代码,整个安装过程由Firefox默认流程来执行。而定制安装扩展的安装包中有定制的安装过程信息。而这些信息是用专门的Javascript代码编写,调用XPInstall提供的API来实现。定制安装扩展,常见的例子是用Firefox打开扩展中心,点击某个扩展就可以在线安装,其中就是调用了XPInstall的API。
|
|
|
|
|
图 2 Firefox扩展结构图 |
图 2所示的是Firefox中跟扩展相关的部分的结构。其中Necko是Firefox处理网络相关的核心部分,而Gecko是Firefox解释网页的核心,或者说内核,也就是Layout Engine。而RDF模块实现了一系列XPCOM接口,用于在安装扩展或者处理扩展过程中,读取RDF文件信息。
扩展安装过程,主要有XPInstall负责,如果XPInstall发现扩展是普通默认扩展,将交由toolkit模块(图中没有画出)中负责扩展安装的相关部分来处理。
扩展的运行,需要Chrome注册模块支持。在Firefox运行时,扩展的chrome信息注册到注册模块中,再交给XULRunner来显示,其中涉及到所有内核提供的功能都涉及到XPCOM。而Firefox所有界面都是由XULRunner来实现的。所以,在扩展运行过程中,扩展已经和Firefox本身融为一体,扩展跟Firefox内核的交互,和Firefox主界面跟内核的交互,两种交互是一样的,都是在XULRunner的平台上,调用XPCOM来实现相应的功能。
扩展概要的安装过程如图 3所示:
Firefox截获安装事件
|
检测rdf安装信息
|
抽取安装代码
|
执行安装代码
|
图 3 扩展的概要安装过程 |
XPInstall检测扩展
|
解压xpi保存到相应文件夹(新建或者替换原有文件)
|
完成安装
|
普通默认扩展 |
定制安装扩展 |
合适安装 |
不合适安装 |
扩展安装主要由XPInstall负责,而XPInstall模块中处理扩展安装的最顶端入口是nsSoftwareUpdateRun.cpp。该文件中定义了RunInstall()函数,Firefox主程序可以设置参数使得RunInstall()单线程或者多线程调用RunInstallOnThread()进行安装。
图 4 XPInstall初始安装过程
图 4是XPInstall安装扩展开始的几步关键过程。其中,nsSoftwareUpdate类包含若干跟安装有关的操作,而nsIZipReader是一个XPCOM接口,有读取xpi包的接口,而installInfo类封装了当前安装的扩展的关键信息,而最后的nsExtensionManager也是XPCOM接口,由toolkit模块中负责Extension部分的代码实现,专门处理普通默认扩展。
上述过程中,有两步比较关键,OpenAndValidateArchive()验证当前的xpi符合规范,数字签名安全等等。另外Test(“install.rdf”),决定了当前这个xpi是否属于普通类型。如果install.rdf存在,则把当前扩展当作普通扩展来处理。再调用nsExtensionManager来进行下一步的安装。
接下来将重点总结一下nsExtensionManager中的安装过程。如图 5所示:
图 5 nsExtensionManager安装过程
在nsExtensionManager中,用Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService),获取nsIRDFService服务。调用getInstallManifest()在install.rdf中获取安装信息ds存到全局变量_metadataDS中。
再调用nsIRDFService的GetResource( )抽取每项安装信息,例如maxVersion,扩展的唯一标识guid等,并检查是否适合安装。
通过getInstallLocation获取安装位置后调用Install类的函数installFromFile()来完成安装。其中即调用_installExtensionFiles()和safeInstallOperation()来部署目标文件夹(window xp为例,是C:\Documents and Settings\UserName\Application Data\Mozilla\Firefox\Profiles\Profile\extensions),然后解压所有xpi中的文件放到指定位置。其中safeInstallOperation()保证了复制文件的过程中,如果出现错误能够完整的rollback。
nsExtensionManager最后调用_updateExtensionManifest()更新扩展信息表,把新扩展的文件信息加到汇总表中,以供Firefox启动的时候由toolkit来读取。详细见3.5节。
如果没有install.rdf,XPInstall将会调用图 6的过程。其中最重要的是InitXPInstallObjects( )。这个调用将会在nsJSInstall中调用到几乎整个XPInstall的类。由于本次分析主要集中在普通默认扩展上,对XPInstall具体过程不作深究。XPInstall的结构请见3.3节。
图 6 定制安装的扩展安装过程
表格 1 XPInstall主要类
类名 |
功能描述 |
nsInstall |
实现大多数xpinstall对外的API(详细见xpinstall的API说明,本报告中略), 在类中记录当前安装包的信息 |
nsInstallUninstall |
记录某个待删除的扩展 |
nsISoftwareUpdate |
Xpinstall中更新扩展或者插件用到的公共接口,例如InstallChrome( ) |
nsSoftwareUpdate |
实现nsISoftwareUpdate |
nsXPInstallManager |
管理待安装扩展的列表,并提供外围功能,例如下载扩展,完成安装等 |
nsInstallFolder |
安装过程中使用到的目录对象 |
nsInstallFile |
安装过程中使用到的文件对象 |
nsInstallInfo |
记录当前安装的扩展的详细信息,并有重要的函数 GetExtensionManager()。该函数返回nsIExtensionManager接口的实体,专门用于默认extension(如上边章节描述)的安装。 |
nsXPITrigger |
控制扩展安装开始 例如AllowInstall(),InstallChrome()等函数 |
nInstallVersion |
扩展的版本,实现前后对比等函数 |
nsInstallObject |
很基本的基类 |
nsWinProfile |
负责读写extensions.ini等文件 |
nsInstallResources |
读取资源时辅助性的功能 |
XPInstall主要类图如图 7所示。
图 7 XPInstall主要类结构图
表格 2 RDF模块主要类和接口
类名 |
功能描述 |
对应接口 |
RDFServiceImpl |
整个rdf模块的核心。实现大部分对外的接口。特别是GetDataSource() |
nsIRDFService |
RDFContentSinkImpl |
存储rdf内容的类,并对内容有相应的操作和一些辅助性函数 例如GetResourceAttribute() |
nsIRDFContentSink |
RDFXMLDataSourceImpl |
负责操作xml类型的rdf文件 |
nsIRDFXMLSource nsIRDFDataSource |
nsRDFXMLParser |
一个辅助类,用来解释xml文件 |
nsIRDFXMLParser |
RDFContainerUtilsImpl |
建立RDFContainer的类 |
nsIRDFContainerUtils |
RDFContainerImpl |
管理数据源 |
nsIRDFContainer |
rdfutil |
简单的对字符串操作的辅助类 |
|
nsRDFXMLSerializer |
用于把rdf串行化 |
nsIRDFXMLSerializer |
RDF模块主要类与接口的关系如图 8所示。
图 8 RDF模块主要类与接口的关系图
Toolkit获取extensions.ini内容 |
nsChromeRegistry处理每个扩展的manifest文件 |
nsXULDocument获取有效扩展的XUL信息 |
XULRunner启动 |
图 9 扩展启动过程 |
扩展的启动过程如图 9所示:
在这一次的研究过程中,由于时间和能力有限,研究的广度和深度还是很皮毛。在日后,我们将会围绕着扩展来逐步深入对Firefox源代码的研究。例如首先的研究方向是XPCOM的具体结构和XULRunner这部分。