--- title: XPCOM 接口 slug: Mozilla/Tech/XUL/Tutorial/XPCOM_接口 translation_of: Archive/Mozilla/XUL/Tutorial/XPCOM_Interfaces ---
本章简单的看看 XPCOM (跨平台组件对象模型)——Mozilla 使用的对象系统。
通过XUL我们可以建立一个复杂的用户界面,我们可以通过脚本来修改界面及处理任务。然而有很多事件是不能有JavaScript直接完成的,比如,我们像创建一个邮件程序,我们需要写一个脚本来连接到服务器发送好接收邮件,而JavaScript没有这个能力。
处理这种事情的唯一手段就是用本地代码写一个获得邮件的模块,同时也需要一个用脚本调用本地对象的简单方法。 Mozilla 通过了这样的模型—— XPCOM (Cross-platform Component Object Model)。
Mozilla 是有一系列组件构成的。每一个组件都处理特定的任务。比如,有一个处理菜单,按钮及元素的组件。组件又建立在一系列定义(接口)上。
Mozilla 中的接口定义了在一个组件中需要实现的功能,组件是它的代码实现,每一个组件都要实现接口描述的功能。一个组件可以时多个接口,多个组件也可以实现同一个接口。
让我们来以‘文件’组件为例,需要创建一个描述‘文件’属性及功能的接口文件。文件需要有文件名、数据、大小等属性;删除、移动、复制等方法。
文件接口用字符文件描述而无需实现。实现的工作留给组件来完成,组件需要返回文件名、数据、大小的代码,以及复制、删除等代码。
我们不关心组件如何实现,只要它实现了接口。当然我们有不同的实现方法,并且对于不同的平台也会不同。然而,他们必须实现同样的接口,这样我们就可以利用从接口中得到的信息使用组件。
Mozilla中接口通常以'nsI'或 'mozI'开头,这样就很容易找出接口。比如 nsIAddressBook
用于地址表, nsISound
用于声音文件 , nsILocalFile
用于本地文件,Mozilla中的接口参见 Interfaces.
XPCOM 组件是一种本地实现,也就是说他们可以做 JavaScript 不能做的事情。我们可以调用接口定义的由组件属性的任意方法,比如,如果我们有一个组件,它实现了 nsISound
接口我们就可以用它来放声音。
调用 XPCOM 组件分三步
如果我们做了前两步,最后一步可以无限制的使用,例如,我们想要重命名文件,这个方法定义在nsILocalFile 接口中。第一步得到文件组件。第二步,我们获取它实现了的nsILocalFile 接口,最后,调用接口通过的方法。这个接口用于代表单个文件。
我们知道接口长椅 'nsI' 或 'mozI'开头。组件通常像使用URI一样来引用,Mozilla 储存了当前注册的组件列表。像插件一样,用户可以添加新的组件。
Mozilla 通过了一个文件组件,它实现了 nsILocalFile
。可以通过'@mozilla.org/file/local;1'
引用,这个字符串称作协议ID,语法如下:
@<internetdomain>/module[/submodule[...]];<version>[?<name>=<value>[&<name>=<value>[...]]]
其他组件也按相似的方法引用。
组件的协议 ID 用于获取组件,可以用下面的脚本得到组件。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();
一个文件组件返回并储存在变量 aFile 中,上例中的 Components
引用一个提供相关功能的全局对象。 这里,使用 classes
方法获得组件组,classes
方法返回可用组件的数组。得到不同的组件只需把双引号中的需要ID换成你需要的,最后用createInstance()
方法创建实例。
你需要检查 createInstance()
的返回值以确保非空,如果为空则表示所需组件不存在。
当然,到此为止,我们只引用了文件组件本身,为了使用它的函数,我们需要得到它的接口,本例为 nsILocalFile
。我们再加一行。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(); if (aFile) aFile.QueryInterface(Components.interfaces.nsILocalFile);
QueryInterface()
是一个所以组件都通过的函数来返回特定的接口。函数有一个参数,你想得到的接口Components
的 interfaces
方法包含一个可用接口的数组,这里把 nsILocalFile 作为参数传给 QueryInterface()。结果是
aFile会引用它所实现了 nsILocalFile 接口的那部分。
上面的两行脚本可以用于获取任何组件及接口,只需更换组件名及接口名。下例,获得一个 sound 接口。
var sound = Components.classes["@mozilla.org/sound;1"].createInstance(); if (sound) sound.QueryInterface(Components.interfaces.nsISound);
XPCOM 接口可以继承其它的接口。这种接口拥有它自己的功能及它所继承的接口的功能所有的接口都继承自顶级接口nsISupports,它有一个函数由于支持
JavaScript—— QueryInterface()
。因为所以的组件都实现 nsISupports 接口所以QueryInterface()
对每个组件都有效。
一些组件可能会实现相同的接口。他们可能是原件的子类但不是必须的。组件可能都会实现 nsILocalFile接口,另外一个组件可能会实现多个接口。因为这个原因我们在调用函数之前需要得到定义函数的接口。
这里有一个简写形式,不过一般的我们把它分成多行。
var aLocalFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
这一行代码完成了上面两行所做的事。
如果你使用 QueryInterface()
引用一个本组件没有实现的接口,会抛出一个异常。如果不确定组件支持哪个接口请使用 instanceof
操作符检查。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(); if (aFile instanceof Components.interfaces.nsILocalFile){ // do something }
如果aFile实现nsILocalFile 接口 instanceof
返回真,在块中就可以调用 QueryInterface()
。
现在你有了一个关联到 nsILocalFile 接口组件的一个引用,你就可以调用nsILocalFile通过的函数。下面列出了 nsILocalFile提供的属性及方法。
为了删除文件,需要将其赋值给 nsILocalFile。我们可以使用initWithPath()
方法指明所需文件,然后调用 remove()
函数,它带一个参数;是否递归删除,如下例。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(); if (aFile instanceof Components.interfaces.nsILocalFile){ aFile.initWithPath("/mozilla/testfile.txt"); aFile.remove(false); }
以上代码会定位 /mozilla/testfile.txt 并删除它。试着把它与事件处理相连。你可以把文件名改为一个需要删除的文件并把它删除。
上面的函数中 copyTo() 及moveTo()可以用于复制及移动文件。注意他们移动的目标目录不是保存在字符串中而是nsILocalFile,也就是说你需要两个文件字节,下面是一个复制文件的例子。
function copyFile(sourcefile,destdir) { // get a component for the file to copy var aFile = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); if (!aFile) return false; // get a component for the directory to copy to var aDir = Components.classes["@mozilla.org/file/local;1"] .createInstance(Components.interfaces.nsILocalFile); if (!aDir) return false; // next, assign URLs to the file components aFile.initWithPath(sourcefile); aDir.initWithPath(destdir); // finally, copy the file, without renaming it aFile.copyTo(aDir,null); } copyFile("/mozilla/testfile.txt","/etc");
一些 XPCOM 组件是被称为服务器的特殊组件,你无需创建他们因为他们只能存在一个副本。服务器提供了类似于读取,设置全局数据或操作其他对象的方法。我们使用getService()代替
createInstance()
方法来获取服务器组件,除此以外,服务器并没有与其他组件相异之处。
如Mozilla通过了一个书签服务器,它允许你向当前用户的书签列表中添加书签。如下例。
var bmarks = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService(); bmarks.QueryInterface(Components.interfaces.nsIBookmarksService); bmarks.addBookmarkImmediately("http://www.mozilla.org","Mozilla",0,null);
首先组件 "@mozilla.org/browser/bookmarks-service;1"被返回,并保存在变量 bmarks
中,我们使用 QueryInterface()
得到 nsIBookmarksService 接口。这个接口通过的addBookmarkImmediately()
函数用于添加书签。函数的前两个参数是书签的 URL 和标题。第三个参数是书签类型通常是 0 ,最后一个参数是书签页的字符集,可为空。
下一章看看Mozilla为我们通过的接口。 interfaces provided with Mozilla that we can use.
。