Mozilla's getting a new look. What do you think? https://mzl.la/brandsurvey

创建_XPCOM_组件/使用_XPCOM_组件

创建一个新的 XPCOM 组件, 特别是在我们设计一个供别人使用的组件接口的时候, 最好方式是参照已有的组件. 我们在编写 Starting WebLock 这个例子的时候, 也是这么做的.

Mozilla 浏览器应用是复杂的, 模块化的 XPCOM 客户程序. 实际上, 基本上所有与浏览器相关的功能都被定义成了组件的形式, 包括网页间的跳转, 窗口管理, cookie 管理, 书签, 安全, 搜索, 润色等等的其他功能, 这些功能都是由组件的接口提供的. Mozilla就是一堆 XPCOM 组件.

本章将讨论 Mozilla 是如何使用象 CookieManager 这样的 XPCOM 对象, 然后根据这些例子我们定义 WebLock 组件的访问接口.

组件的例子

可以在这里 XPCOM API Reference 找到下面要描述的组件. 我们要了解的是象本节中所给出的组件是如何被 Mozilla 浏览器获取和使用的.

Cookie 管理是以组件形式向 Mozilla 浏览器提供支持的众多组件之一, 这些组件可以被重用在需要类似功能的应用中. 当用户通过 Cookie 管理器对话框来观察, 组织, 或者删除 cookies 的时候, Cookie 管理器在背后默默的工作. Cookie 管理器对话框负责向用户提供 Cookie 管理器的 UI 界面[cookie-manager-ui].

Cookie 管理器对话框

Image:cookie_mgr_dlog.png

对话框是用 XUL (XML UI 语言) 和 JavaScript 语言编写, 使用称为XPConnect 的组件无缝连接到 Cookie 管理器组件(参看下面的 从接口连接到组件). XUL 只是一种暴露 Cookie 管理器功能的方式, 但是却是 Mozilla 环境下最有用的方式之一.

CookieManager 组件的功能通过 nsICookieManager 接口提供, 接口的方法如下:

nsICookieManager 接口

removeAll 删除 cookie 列表中所有的 cookies.
enumerator 通过 cookie 列表枚举.
remove 从列表中删除某个 cookie .

XPCOM 中所有的接口必须固定, 虽然组件对接口的实现会有所变化. 接口都是public 的, 相对的, 接口实现是 private 的[private-xpcom-interfaces]. 当用户选中 cookie 列表中的一个 cookie, 点击 Remove 按钮, nsICookieManager 接口的 Remove 方法被调用. CookieManager 组件执行该函数, 选中的组件就被删除了.

下面的从 JavaScript 中访问 CookieManager 组件代码, 展示了如何从 JavaScript 中调用 Remove() 方法:

从 JavaScript 中访问 CookieManager 组件

// xpconnect to cookiemanager
// get the cookie manager component in JavaScript
var cmgr = Components.classes["@mozilla.org/cookiemanager;1"]
                     .getService();
cmgr = cmgr.QueryInterface(Components.interfaces.nsICookieManager);

// called as part of a largerDeleteAllCookies() function
function FinalizeCookieDeletions() {
  for (var c=0; c<deletedCookies.length; c++) {
    cmgr.remove(deletedCookies[c].host,
                deletedCookies[c].name,
                deletedCookies[c].path);
  } 
  deletedCookies.length = 0;
}

从接口连接到组件

Mozilla 中使用的从 JavaScript 访问 XPCOM 组件的技术称为XPConnect, XPConnect 也是一个组件.

XPConnect 把应用程序代码与 Mozilla 浏览器, 基于 Gecko 的 XUL, 和象 xpcshell 这样的 JavaScript 环境绑定在一起.

xpcshsell 是 Mozilla 内嵌的 XPCOM 工具, 它是 JavaScript 的命令行解释器.

参看 http://www.mozilla.org/scriptable/, 获取更多关于 XPConnect 和 JavaScript 的信息.

上面展现的技术当然并不是 XPCOM 的全部, 但是却是一个重要的方面. XPCOM 强加的契约打开了一扇通往二进制互操作技术的大门. - 这是一种能够在运行时刻访问, 使用, 重用 XPCOM 组件的技术, 这种技术能够保证用某种语言编写的组件能够被其他的语言所访问.

在 Mozilla 浏览器中, 组件常常通过接口在 JavaScript 中访问, 搜索 Mozilla 的源代码, 会发现 CookieManager 组件只是在 JavaScript 中被调用. 在本教程中, 我们也使用这种方式来访问它[教程中使用的 coocki 管理器].

JavaScript 与 Mozilla

JavaScript 是 Mozilla 浏览器的喉舌, 它把自己与 XPCOM 紧紧地绑定在一起. XPCOM 的这种可扩展能力 - 从 XPConnect 绑定的语言中访问组件的能力, 是 XPCOM 的一个关键属性.

WebBrowserFind 组件

组件的应用是广泛的: 在浏览这样的高级应用中, 会有 nsWebBrowserFind 这样的接口, 它提供 find()findNext() 方法用于在网页上查找特定内容. 在一些低级应用中, 会提供数据管理这样的功能. 虽然 Mozilla 并不能将所有的 API 都写成 XPCOM 组件的形式, 但是绝大多数浏览器的典型功能都是用 XPCOM 的组件形式实现的, 因此可以被嵌入和扩展.

除了 CookieManager 组件, 这里还要介绍一个 WebBrowserFind 组件. 它实现的 nsIWebBrowserFind 接口见下表 nsIWebBrowserFind 接口.

nsIWebBrowserFind 接口

findNext 找到字符串出现的下一个位置.
findBackwards 布尔类型属性值, 控制 findNext() 方法向前/向后搜索.
searchFrames 布尔类型属性值, 标识是否搜索当前页面的子框(subframes).
matchCase 布尔类型属性值, 标识是否按照大小写匹配搜索网页.
entireWord 布尔类型属性值, 标识是否匹配整个词.

一旦我们使用接口来获的了某个组件, 我们就可以询问该组件是否支持其他的接口. 这种基本服务由 nsISupports 接口提供, 会由所有的 XPCOM 组件继承; 它允许我们查询组件的接口, 并在接口之间进行切换; 它展现了 XPCOM 的运行时刻确定类型的能力. 它由 QueryInterface 方法实现, 我们将在后面什么是 XPCOM?一章中介绍. XPCOM API Reference 中提供了完整的 XPCOM 组件的索引.

WebLock 组件

现在我们把 WebLock 组件看成另一个 XPCOM 组件的例子. 在面向对象编程中, 通常是先设计接口 - 首先定义要提供的功能, 而不是考虑如何实现这些功能. 因此我们把实现这个组件的细节问题放到下一章, 这一章先考虑从外部如何看待这个组件. - 即定义 WebLock 组件的接口.

IWebLock 接口

lock 锁定浏览器到当前站点, 或者是磁盘上保存的某个白名单上的站点.
unlock 解开浏览器锁定, 开放访问所有站点.
addSite 添加一个新的站点到白名单.
removeSite 从白名单上删除某个站点.
sites 枚举白名单上的站点.

WebLock 组件就是要实现上面接口定义的功能. 它在浏览器启动的时候, 注册自己. 当用户或者管理员点击浏览器上的 weblock 图标时, 类厂会创建对象实例.

Mozilla 中使用的组件

那么我们应该如何获得组件, 然后如何在 Mozilla 中使用它呢? 我们在前面已经看到了一小段 JavaScript 代码, 但是我们并没有解释一般情况下该如何获得 XPCOM 组件.

这一节讨论 Mozilla 中实际使用的组件例子. 本节分成三部分: 一部分是关于该如何在 Mozilla 上找到组件. 其他两个部分是关于该如何访问这些组件.

查找 Mozilla 组件

本书试图向读者提供关于 XPCOM 组件和当前冻结的接口的索引信息. Mozilla 嵌入工程跟踪了当前冻结的接口.

Mozilla 包含了 Gecko 提供的查找和显示组件信息的工具 -XPCOM 组件观察器LXR.

提供 XPCOM 组件信息的主要问题是, Mozilla 接口在不断的发展, 试图选择一个冻结的断面是困难的. 组件观察器的实现并没有考虑组件是否已被冻结, 在 LXR 中我们会发现, 被冻结的接口会在头部标记 @status frozen.

XPCOM 组件观察器

组件观察器 是一个可选安装的浏览器插件.

XPCOM 组件观察器

Image:using-component-viewer.png

在上面的图中, 左列显示的是以gtx 字符串搜索契约 ID 得到的组件子集, 右列是左列选中组件实现的接口.

XPCOM 观察器在获取组件的大致信息的时候非常有用, 但是要知道组件观察器显示的是所有的组件, 有些组件并不稳定, 组件的接口可能会在后续版本中变化, 所以要慎重选取我们自己工程中使用的组件.

XXX mediawiki is t3h suxx0r XXX give me my C++

在 Cpp 代码中使用 XPCOM 组件

XPConnect 把对 XPCOM 组件作为 JavaScript 对象, 使得对 XPCOM 组件的访问变得非常简单, 从 C++ 代码中访问 XPCOM 要复杂一些.

从 Cpp 代码管理 Cookies 以 C++ 代码重新实现了从 JavaScript 中访问 CookieManager 组件的功能.

从 Cpp 代码管理 Cookies

nsCOMPtr<nsIServiceManager> servMan;
nsresult rv = NS_GetServiceManager(getter_AddRefs(servMan));
if (NS_FAILED(rv))
  return -1;

nsCOMPtr<nsICookieManager> cookieManager;
rv = servMan->GetServiceByContractID("@mozilla.org/cookiemanager",
                                     NS_GET_IID(nsICookieManager),
                                     getter_AddRefs(cookieManager));

if (NS_FAILED(rv))
  return -1;

PRUint32 len;
deletedCookies->GetLength(&len);

for (int c=0; c<len; c++)
    cookieManager->Remove(deletedCookies[c].host,
                          deletedCookies[c].name,
                          deletedCookies[c].path,
                          PR_FALSE);

XXX: In the original document, there were only the first three parameters to the |Remove| call. I added |PR_TRUE| as a fourth parameter because the interface seems to require it: http://lxr.mozilla.org/mozilla/sourc...Manager.idl#64 This problem also appears in the JavaScript version below, and I've added |false| as a fourth parameter there as well.

如果我们的应用是用 C++ 编写, 从 Cpp 代码管理 Cookies 这段代码向我们提供了很好的模板.

XPConnect: 在脚本中使用 XPCOM 组件

在本章开始我们讨论了CookieManager组件,他提供了一个很好的例子来说明如何使用javascript访问组件.在下面的代码片断里你可以看到如何通过getService()方法创建一个CookieManager组件对象,并且通过它提供的功能来让我们从用户界面来读取和删除cookies.

Managing Cookies from JavaScript

var cmgr = Components.classes["@mozilla.org/cookiemanager;1"]
                     .getService();
cmgr = cmgr.QueryInterface(Components.interfaces.nsICookieManager);

function loadCookies() {
  // load cookies into a table
  var enumerator = cmgr.enumerator;
  var count = 0;
  var showPolicyField = false;
  while (enumerator.hasMoreElements()) {
    var nextCookie = enumerator.getNext();
    nextCookie = nextCookie.QueryInterface(Components.interfaces.nsICookie);
    /* .... */
}
function FinalizeCookieDeletions() {
  for (var c=0; c<deletedCookies.length; c++) {
    cmgr.remove(deletedCookies[c].host,
                deletedCookies[c].name,
                deletedCookies[c].path,
                false);
  } 
  deletedCookies.length = 0;
}

XXX: In the original document, there were only the first three parameters to the |remove| call. I added |false| as a fourth parameter because the interface seems to require it: http://lxr.mozilla.org/mozilla/sourc...Manager.idl#64 This problem also appears in the C++ version above, and I've added |PR_FALSE| as a fourth parameter there as well.

除了CookieManager被调用的方法以外(也就是cookiemanager.remove(他会映射到remove()The nsICookieManager Interface),请注意那些在Javascript中反映XPCOM组件的专门的XPConnect对象和方法。

Components 是用来控制到组件连接的JavaScript对象, 而classes 是一组所有你可以根据契约ID来查询的对象。为了在Javascript中实例化XPCOM组件,你创建一个新的Component对象同时传入你所需要查询的组件契约ID,返回的可能是一个singleton或者一个实例。

var cmgr = Components.classes["@mozilla.org/cookiemanager;1"]
                     .getService();

cookiemanager 对象的结果提供组件的所有在IDL中编译好然后编译到类型库中的方法的入口。 使用CookieManager组件, 你可以写如下的代码来完成从系统中清除所有cookies的操作:

cmgr = Components.classes["@mozilla.org/cookiemanager;1"]
                 .getService();
cmgr = cmgr.QueryInterface(Components.interfaces.nsICookieManager);

// delete all cookies
function trashEm() {
   cmgr.removeAll();
}

这个例子所展示的另外一个关键的XPConnect特性的是可以在所有从XPCOM映射到javascript的对象上执行的QueryInterface方法。如同在C++中, 你可以使用这个方法询问给定对象的别的接口。

Services Versus Regular Instances

到底让客户把你的组件作为一个实例还是服务是一个设计问题,你应当在你的组件文挡中进行描述。实际上,例子中通过方法createInstance()调用getService()方法的方法实际上也可以是对组件对象调用并且缓存结果,并让他做为一个singlenton而不是实例。

用来建立服务的singleton设计模式在XPCOM Services进行描述。

请记住,QueryInterface让你查询一个对象所支持的接口。在The nsICookieManager Interface的代码片断中, QueryInterface方法被用来从eunumerator中获得nsICookie接口,从而, 比如说, JavaScript代码就可以获得每个cookie的valuename属性。

  1. Note: cookie-manager-ui
    注意接口不是组件的一部分. XPCOM通过Mozilla's Cross Platform Front End (XPFE)和其他的用户接口使使用CookieManager这样的组件变得容易,但是组件本身并不提供自身的UI。
  1. Note: private-xpcom-interfaces
    这方面也有例外. 一些XPCOM接口也可以是private并且不是作为公用的. Private接口和在IDL中公开的接口要求有所不同。
  1. Note: cookie-manager-in-tutorial
    CookieManager组件用来支持本教程所描述的网页所定功能。

Copyright (c) 2003 by Doug Turner and Ian Oeschger. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.02 or later. Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder. Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.

文档标签和贡献者

标签: 
 此页面的贡献者: fscholz, ziyunfei, Mgjbot, Eskimo root, Secure alex, Mars131, Wushli, Klp99
 最后编辑者: fscholz,