自引导型扩展

注意:所有使用 Add-on SDK 创建的扩展都是自引导的。其引导代码已经自动生成,所以你不必考虑该问题。没有使用 Add-on SDK?那就继续读下去吧……

传统形式的扩展包含覆盖接口程序段(overlay),应用程序可以从扩展的程序包中载入 XUL,并自动将其覆盖在自己的 UI 之上。这使得创建的扩展加入到应用程序的用户界面比较容易,但同时更新、安装或禁用扩展需要应用程序重启。

Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) 引入了自引导型(bootstrapped)扩展。这是种特别的扩展,它们不使用覆盖界面来将它们的用户界面应用到应用程序中,而是用程序将它们自己插入到应用程序。这是通过包含在扩展中的一个特制的脚本文件来实现,脚本中包含了一些函数可供浏览器调用,来指挥扩展的安装、卸载、启动和关闭。

应用程序所需做的就是调用该脚本文件;由扩展负责添加和去除它的用户界面,和处理任何其它所需的设置和关闭任务。

本文论述了自引导型扩展将如何工作。

作为题外话,所有用 Add-on SDKAdd-on Builder 创建的扩展都是自引导型的;但是,所有的引导代码都是为您特别生成的,所以实际上您并不需要真的去研究它。

启动和关闭程序(process)

自引导型扩展的一个关键特征是,它们必须按照应用程序的要求启动和关闭。当扩展的 startup() 函数被调用,它必须手动将它的用户界面和其它行为注入到应用程序中。同样,当它的 shutdown() 函数被调用,它必须移除添加到应用程序中的任何东西,也包括对它的所有对象的所有引用。

startup() 函数会在多个场景中被调用;比如:

  • 第一次安装扩展,假设它兼容该应用程序且被启用。
  • 使用附加组件管理器启用该扩展。
  • 应用程序启动,如果扩展已被启用且兼容应用程序。

shutdown() 函数会被调用的一些场景:

  • 卸载该扩展,如果它为启用状态。
  • 禁用该扩展。
  • 用户退出应用程序,如果该扩展已被启用。

修改应用程序用户界面的说明

引导型附加组件中的 chrome.manifest

您可以在自引导型附加组件中使用 chrome.manifest 文件来:

  1. 通过 chrome:// URL[在清单(manifest)中使用 contentlocaleskin 指令]使您附加组件的内容生效;
  2. 用您的内容替换已经存在的 chrome:// URI(使用 override 指令)。

不是所有的 chrome.manifest 指令都被自引导型附加组件支持,比如在自引导型附加组件中,您仍然不能注册 XUL 覆盖界面。详见 chrome.manifest 文档。

在 Firefox 10 及以上版本中,放在附加组件 XPI 根目录下的 chrome.manifest 文件(与 install.rdf 同级)会被自动加载。在 Firefox 8 和 9 中,您必须手动加载/卸载清单,使用 nsIComponentManager.addBootstrappedManifestLocation()nsIComponentManager.removeBootstrappedManifestLocation()。该特征在 Firefox 8 以前的版本中无效。

手动添加用户界面

如果您决定继续尝试开发一个修改应用程序用户界面的自引导型扩展,这儿有几个建议给您。

您需要在相关应用程序中调用 document.getElementById(),通过 UI 元素的 ID 来查找它们,然后操纵它们来注入您的 UI。例如,您可以通过 document.getElementById("main-menubar") 来访问 Firefox 的菜单栏。

确保在关闭时移除您添加的任何用户界面。

创建自引导型扩展

为标示一个扩展为可引导的,您需要添加下列元素到它的安装清单

<em:bootstrap>true</em:bootstrap>

然后您需要添加一个 bootstrap.js 文件,它包含所要求的函数;在扩展的包中,它应该与 install.rdf 文件同级。

向后兼容

因为旧版本的 Firefox 并不知道 bootstrap 属性或 bootstrap.js 文件,创建一个既能作为可引导扩展又能作为传统扩展来工作的 XPI 并不很困难。按照可引导扩展来创建您的扩展,然后同样地添加传统的覆盖界面。新版本的 Firefox 将使用 bootstrap.js 脚本并忽略部件(components)和覆盖界面,同时旧版本将使用覆盖界面。

引导切入点(entry points)

bootstrap.js 脚本应该包含几个特殊函数,浏览器调用它们来管理扩展。该脚本被放在一个有特权的(privileged)沙箱中执行,这个沙箱一直被保存(cached)到扩展关闭。

startup

当扩展需要启动它自己时被调用。它发生在应用程序启动时,或启用被禁用的扩展时(或在为安装更新而被关闭后)。可见,在应用程序的生命期内,它会被多次调用。

您的附加组件应当在这时注入 UI,启动任何可能需要运行的任务,等等。

void startup(
  data,
  reason
); 
参数
data
引导数据结构
reason
原因常量之一,表明扩展为什么被启动。可以是 APP_STARTUPADDON_ENABLE、ADDON_INSTALLADDON_UPGRADEADDON_DOWNGRADE 之一。

shutdown

当扩展需要关闭它自己时被调用,比如应用程序退出时,或准备更新/禁用扩展时。在这里必须移除所有已被注入的用户界面,关闭所有任务,并处理掉所有对象。

void shutdown(
  data,
  reason
);
参数
data
引导数据结构
reason
原因常量之一,表明扩展为什么被关闭。可以是 APP_SHUTDOWNADDON_DISABLE、ADDON_UNINSTALL、ADDON_UPGRADEADDON_DOWNGRADE 之一。

install

您的引导脚本必须包括 install() 函数,应用程序在安装、升级或降级扩展后,第一次调用 startup() 之前调用它。

注意:如果从未启动扩展,则不会调用该方法;例如,如果扩展虽被安装但并不兼容当前版本的应用程序,且在变为兼容之前被卸载,则 install() 始终不会被调用。但是,如果扩展被升级到一个兼容应用程序的版本,将在第一次调用 startup() 之前调用它的 install() 函数。
void install(
  data,
  reason
); 
参数
data
引导数据结构
reason
原因常量之一,表明扩展为什么被安装。可以是 ADDON_INSTALLADDON_UPGRADEADDON_DOWNGRADE 之一。

uninstall

该函数在卸载扩展的某个版本之前,最后一次调用 shutdown() 之后被调用。如果始终未调用 install(),也不会调用此函数。

注意:记住这一点很重要——即使对于当前被禁用或不兼容当前应用程序的扩展,都会被调用 uninstall()。鉴于此,在实现该函数时,优雅地处理那些也许并不存在于应用程序中的 API 变得非常重要。如果一个第三方应用程序在 Firefox 未运行时移除了该扩展,该函数也不会被调用。
void uninstall(
  data,
  reason
); 
参数
data
引导数据结构
reason
原因常量之一,表明扩展为什么被卸载。可以是 ADDON_UNINSTALLADDON_UPGRADEADDON_DOWNGRADE 之一。

原因常量

上述引导函数都接受一个 reason 参数,该参数向扩展解释为什么调用该函数。 这些原因常量如下:

常量 描述
APP_STARTUP 1 应用程序启动。
APP_SHUTDOWN 2 应用程序关闭。
ADDON_ENABLE 3 启用附加组件。
ADDON_DISABLE 4 禁用附加组件。(同时被错用于卸载
ADDON_INSTALL 5 安装附加组件。
ADDON_UNINSTALL 6 卸载附加组件。
ADDON_UPGRADE 7 升级附加组件。
ADDON_DOWNGRADE 8 降级附加组件。

引导数据

每个切入点被传入一个简单的数据结构,它包含一些关于引导附加组件的有用信息。调用 AddonManager.getAddonByID() 可以获得更多关于附加组件的信息。该数据是一个简单的 JavaScript 对象,它包括以下属性:

属性 类型 描述
id string 被引导附加组件的 ID。
version string 被引导附加组件的版本。
installPath nsIFile 被引导附加组件的安装位置。可能是一个目录或一个 XPI 文件,这取决于安装该附加组件时是否解包。
resourceURI nsIURI 附加组件文件根目录的 URI,可能是 jar:file: URI,这取决于安装该附加组件时是否解包。
oldVersion string 以前的安装版本,如果原因是 ADDON_UPGRADEADDON_DOWNGRADE,并且方法是 installstartup
newVersion string 将要安装的版本,如果原因是 ADDON_UPGRADEADDON_DOWNGRADE,并且方法是 shutdownuninstall

注意:附加组件可能会在程序启动时升降级,此时 startup 方法的原因是 APP_STARTUP,并且不会设置 oldVersion 属性。 另外需注意,在某些情形下有可能发生附加组件升降级但 uninstall 方法没有被调用的情况。

延伸阅读

文档标签和贡献者

 此页面的贡献者: Yaty, ma3r, xcffl, yfdyh000
 最后编辑者: Yaty,