XPCOM在Gecko 2.0中的更改

 

XPCOM在Gecko 2.0中的更改

原文:https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0

引入于Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1

    Gecho 2中有几个更改,影响了XPCOM组件的兼容性。本文对它们作出详细介绍,并提供对更新代码的一些建议。

不再有冻结接口(frozen interfaces)

    从现在开始,所有的接口如有改变,恕不另行通知。文档将被更新,移除那些被标记为“冻结”和“未冻结”的接口的引用。

组件注册

    XPCOM组件注册的方式在Gecko 2中改变。在Gecko 2之前,在组件注册期间,会加载和调用所有的二进制和js组件文件,要求它们自我注册。如果你使用XPCOMUtils.jsm,有一些被隐藏,因而无法发现,但它还是存在。

    从Gecko 2开始,组件使用manifest文件进行注册,和chrome相似。事实上,使用相同的chrome manifest文件注册组件。

    现有的所有XPCOM组件都需要被更新以支持该注册方式。不过,这比较简单,实际上可以同时支持两种注册方式以保持向后兼容。

组件manifest

    现在,所有组件的注册都通过manifest文件进行。对于扩展,它和当前用来注册chrome的chrome.manifest相同。

XPT文件

    任何XPT文件的路径都必需在manifest中显式地用interfaces指令列出:

interfaces components/mycomponent.xpt

js组件

    js组件的注册信息不再位于组件自身;而被移动到manifest中。仅当XPCOM组件管理器需要创建组件时才会加载js组件。

chrome.manifest:

# The {classID} here must match the classID in mycomponent.js
component {e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8} components/mycomponent.js
contract @foobar/mycomponent;1 {e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8}
category profile-after-change MyComponent @foobar/mycomponent;1

    js代码必须导出一个NSGetFactory()函数,而不再是原先的NSGetModule()函数;NSGetFactory()接受一个类ID(CID)作为参数。

    例如,js代码:

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

function myComponent() {
}
myComponent.prototype = {
    // 必须和chrome.manifest中的内容相匹配
    classID: Components.ID("{e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8}"),

    QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMyComponent]),

    /* 此处实现nsIMyComponent */
    ...
};

// XPCOM使用下面的这行创建组件。
// 每个组件原型必须拥有一个.classID。我们将使用classID创建组件。
const NSGetFactory = XPCOMUtils.generateNSGetFactory([myComponent]);

    组件可实现向后和Gecko 1.9.2的兼容,这通过动态检测XPCOMUtils.jsm导出哪个符号、并导出对应的函数来完成:

/**
* XPCOMUtils.generateNSGetFactory 引入于 Mozilla 2 (Firefox 4, SeaMonkey 2.1)。
* XPCOMUtils.generateNSGetModule 引入于 Mozilla 1.9 (Firefox 3.0)。
*/
if (XPCOMUtils.generateNSGetFactory)
    var NSGetFactory = XPCOMUtils.generateNSGetFactory([myComponent]);
else
    var NSGetModule = XPCOMUtils.generateNSGetModule([myComponent]);

二进制组件

    二进制组件必需在manifest中显式地用binary-component指令列出:

binary-component components/mycomponent.dll

    组件中的C++代码必须更改:二进制组件不再导出一个NSGetModule()函数,而是导出一个NSModule数据符号,它指向一个mozilla::Module结构。关于mozilla::Module结构的更多信息,参考Module.h。实现动态module的最新示例,参考nsSampleModule.cpp。

    注意,nsIGenericFactory.h已被移除。nsIGenericFactory.h的引用应当被替换为mozilla/ModuleUtils.h。

    使用额外的宏NS_IMPL_MOZILLA192_NSGETMODULE,二进制组件可以兼容Mozilla 1.9.2和2.0,详细信息可参考nsSampleModule.cpp。

注意:对FF4以上的主发行版,二进制XPCOM组件必须重新编译。使用js-ctypes后,你将可以更加容易地过日子。

    还要注意,使用二进制组件的扩展现在在安装manifest中必须使用unpack属性。

平台指令

    过去在特定平台子目录下进行查找的component/chrome系统(如Windows上的platform/WINNT_x86-msvc/chrome.manifest)不再被支持。使用 OS 和 ABI chrome注册指令来获得相同的效果:

binary-component components/windows/mycomponent.dll ABI=WINNT_x86-msvc binary-component components/mac/mycomponent.dylib ABI=Darwin_x86-gcc3 binary-component components/mac/mycomponent64.dylib ABI=Darwin_x86_64-gcc3 binary-component components/linux/mycomponent.so ABI=Linux_x86-gcc3

    这也意味着平台偏好也再可行。如果你需要根据平台调整默认偏好,可以在首次运行时检查所在的平台然后更改偏好。

分类注册

    Gecko 2之前,扩展可以在启动期间监听 xpcom-startup 和 app-startup notifications,并执行一些动作。但现在不再如此。现在,扩展能够接收的最早的启动通知是profile-after-change(建议总是兼通它)。这是因为它是发生于配置文件夹(所以还包含偏好和其它服务)生效之后的最早的通知之一。

需要更改的内容

    如果你的扩展监听了xpcom-startup 或 app-startup,那么你需要更新代码为监听profile-after-change。

    通常,扩展监听app-startup是因为,在过去,你需要用它来执行加载,以便能够注册对profile-after-change的监听。不过,在Gecko 1.9.1中,就不再如此;你现在可以使用分类管理器注册profile-after-change。详细信息参考接收启动通知。

    为添加一个分类实体,你必须在chrome.manifest中插入下面这行:

category profile-after-change MyComponent @foobar/mycomponent;1

重要:以前,分类实体的id拥有前缀"service,",如果组件被实现为一个服务的话。前一到chrome.manifest后,该前缀废弃。

分类名称变更

    XPCOM分类管理器用于注册特定的全局帮助对象。因为chrome.manifest是空格分隔的格式,因此,带有空格的分类名称无法被注册。于是,下面的分类被更改:

Old name New name
JavaScript global constructor JavaScript-global-constructor
JavaScript global constructor prototype alias JavaScript-global-constructor-prototype-alias
JavaScript global property JavaScript-global-property
JavaScript global privileged property JavaScript-global-privileged-property
JavaScript global static nameset JavaScript-global-static-nameset
JavaScript global dynamic nameset JavaScript-global-dynamic-nameset
JavaScript DOM class JavaScript-DOM-class
JavaScript DOM interface JavaScript-DOM-interface
XSLT extension functions XSLT-extension-functions

缘由

    以前,无论何时,只要Gecko检查到应用程序版本变更,或者是一或多个扩展被添加、移除、开启或禁用,都需要在其启动过程中抛弃所有已存在的组件注册,然后重启应用程序(我们称之“扩展管理器重启”)。这是必需的,为确保不再有效的任何组件都被正确地处理、并注册所有一切、加载所需的任何新组件。

    在理论上,这对用户不可见,但它是一个费时费力的过程,因为每个组件都需要在重启过程中被加载和执行,然后被卸载,然后再被重新加载。

    除此之外,当前正在进行让FF多线程工作,内容的处理要么需要根据每个进程注册组件,要么以某种方式和chrome进程共享一个组件缓存。

    组件注册模型的改变让这种叫作扩展管理器重启的玩意成为过去式。我们不再依赖于启动时的隐性组件缓存,而是从manifest文件读取应用程序的组件注册,然后加载组件。这可以加载足够的XPCOM,并运行;然后我们可以运行扩展管理器,执行必需的安装、反安装或更新任何已安装的扩展。

Electrolysis content processes can simply read the component registrations during startup.

XPCNativeWrapper的变更

无法从manifest禁止XPCNativeWrapper

    在manifest中指定xpcnativewrappers=no(即,XPCNativeWrapper自动化)不再被支持。它一直被拟定为一个短期的解决方法,在扩展的作者更改代码以使用XPCNativeWrappers时,仍然允许扩展继续工作。

    如果插件依赖于附加到内容对象的XBL绑定(比如,调用函数的能力或获取和设置XBL绑定创建的属性),你将需要使用XPCNativeWrapper属性wrappedJSObject来访问包装对象。

    如果你需要调用Web中定义的函数或访问Web中的属性,你也需要如此做。举个例子,可能的一种的情况是,你编写了一个扩展,为Web邮件服务添加了一个删除按钮,而你需要调用服务定义的window.delete()函数。

    另一方面,如果你仅仅访问DOM方法或属性,那么你无需使用xpcnativewrappers=no,应当从manifest中移除它。

XPCNativeWrapper杂项的变更

    对PCNativeWrapper的"expando"属性使用delete操作符不再抛出一个安全异常。

XPCOMUtils.jsm的变更

    代码模块XPCOMUtils.jsm已被更新,允许指定你想要注册组件于其中的应用程序的ID。

获取XPCOM服务

    现在可以使用Get函数获取若干常用的XPCOM服务(如:nsCOMPtr<nsIIOService> ioService = mozilla::services::GetIOService()),参考mozilla::services namespace。这让从C++代码中访问这些服务更加容易。

你可能感兴趣的:(XPCOM在Gecko 2.0中的更改)