Hello World

来源:百度文库 编辑:神马文学网 时间:2024/04/29 15:52:21
——开发你的第一个Firefox扩展
刘文懋
Why Firefox?
如果有人问我为什么用Firefox[1],首先毫无疑问的是它代表了开放和自由的精神,其次嘛,我会说是它的可扩展性。
Firefox最激动人心的特点就是它提供了开放的接口,你可以使用这些接口来做各种应用,完成各种各样的功能。你应该不曾给IE添点什么吧,微软不见得这么大方能告诉你什么有用的东西。他最慷慨的事就是给你一个COM组件,记得我对IE的最高级的应用,就是在一次网络大作业中,把整个IE嵌在我的程序里,仅此而已。而Firefox则不同了,当你看到了Fireftp就知道我在说什么了。

 
请合上你的下巴,这没有什么值得惊讶的。这就是Firefox,中间那个cool的ftp工具只是它的一个扩展(Extesion)而已。
想做一个这样的东东吗?我不会告诉你到底有多难,因为这不是我开发的,有兴趣的话你可以自己试一试,网址是http://fireftp.mozdev.org/ 。
类似的扩展还有很多,你可以到mozilla的扩展库去看看,据官方统计,到2005年11月9日,(忘了告诉你了,2004年11月9日是Firefox的诞生日,也就是说它还只是一个小baby),用户至少可以使用700个扩展(Extension)或者附件(Add-on)。可见Firefox的扩展发展是多么的迅速!
以前我在99%的时间内使用Firefox,另外1%的时间使用IE浏览一些不遵从W3C标准而非用IE不可的垃圾网站,我们学校的BBS很不幸地成为其中一员。但是现在我在100%的时间内使用Firefox,这归功于一个ie tab[2]的Firefox扩展。所幸随着网络标准的日渐被重视,Firefox有可能会支持所有的网站。 :)
好了,说了这么多Firefox扩展的好处,是不是有点心动了呢?那么就用一个Hello World来开始我们的第一个Firefox扩展吧。别说我太不in了,毕竟hello world总是最容易让我们使所有编程语言的开场白,不是吗?
Here we go!
认识Firefox的扩展
Firefox扩展的功能
从功能来说,Firefox扩展应该是用户和浏览器内核交互的一种体系结构。扩展可以满足用户一些特定的需要,实现特定的功能。开发者可以使用内核提供的一些用户接口,编写自己的实现代码,完成自己设想的功能。
Firefox扩展的格式
从开发的角度来讲,Firefox的扩展是一个文件目录的集合,它们按照一定格式和规范编写和排布的。最终,发布给用户的是一个xpi包。别感到奇怪,这个xpi文件和那些jar[3]的文件一样,都是zip格式的压缩文件。所以这下你懂了吧,把你做完的文件按照zip格式压缩一下就成了我们的扩展包了。
那么究竟具体的xpi文件中是什么情景的?解开我提供的helloworld.xpi文件包,你会发现文件目录的排布如下所示。其中树型结构的叶子部分都是文件,其他的中间结点都是目录。
HelloWorld.xpi


├─chrome
│  ├─content
│  │  ├─ contents.rdf
│  │  ├─ helloworld-Overlay.xul
│  │  └─ hello.js
│  │
│  ├─locale
│  │  ├─ en-US
│  │  │   ├ contents.rdf
│  │  │   └ helloworld.dtd
│  │  │
│  │  └─ zh-CN
│  │       ├ contents.rdf
│  │       └ helloworld.dtd
│  │
│  │
│  └─skin
│      ├─ qq_small.png
│      ├─ qq_big.png
│      ├─ helloworld.css
│      └─ contents.rdf

├─ build.xml
├─ install.rdf
└─ chrome.manifest
通常,Firefox的扩展在根目录中,会有install.rdf文件,这个文件说明了扩展的基本信息,例如扩展的ID、作者、版本等信息。在Firefox1.5之前的版本,该文件还会包含扩展的文件分布信息,在Firefox1.5至后,这些信息都移到了chrome.manifest文件中。在调试的时候,build.xml可以帮助我们自动打包,这个文件对那些在xpi文件中还含有jar文件的扩展特别有用。
根目录下,总是会存在一个chrome目录。chrome下面的格式就不一定了,但是基本都会含有content、locale和skin三个目录。其中,
content目录是用来存放扩展的程序文件和控件格式的资源文件;
locale目录存放不同语言版本,用于扩展的本地化和国际化;
skin目录存放图片等资源文件。
扩展的格式并不一定要拘泥一格,但具体情况需要与install.rdf或chrome.manifest文件中的信息联系。记住,无论如何,良好的习惯总是对你有好处的:请把相应的文件放到它们应该放的地方。
扩展的基本内容
install.rdf
install.rdf是一个扩展的身份证。这么说并不为过,请看下面就是我们helloworld的install.rdf文件:

xmlns:em="http://www.mozilla.org/2004/em-rdf#">

Hello World
{12a1584b-2123-473d-8752-e82e74e3cb11}
0.4
2
A test extension
Marvel
LiuWenmao
http://marvel.hit.edu.cn/


{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
0.9
1.5




可以看得出,这是一个xml格式的文件。根节点为RDF。命名空间为http://www.w3.org/1999/02/22-rdf-syntax-ns# ,前缀为em[4]。下面介绍每一个元素和属性的意义:
Hello World
{12a1584b-2123-473d-8752-e82e74e3cb11}
0.4
2
A test extension
Marvel
LiuWenmao
http://marvel.hit.edu.cn/
em:name:扩展的名字,例如我的名字叫“刘文懋”,而我的扩展叫“Hello World”。J
em:id:扩展的ID号,每一个扩展都会有一个不同的GUID,就如同你的身份证号,用于区分你的扩展与其他人的扩展,所以很明显,全球所有的Firefox插件的ID都不一样。所以,当你写自己的扩展的时候,需要获得一个全球唯一对GUID,Andy Hoskinson帮我们完成了这项工作,你可以登录http://www.hoskinson.net/webservices/guidgeneratorclient.aspx 来获得一个GUID。点击一下Generate GUID按钮,很简单吧,但是这很重要,如果你不想惹麻烦的话!
Firefox1.5支持“User@Location”这种ID的格式。
请注意,当你在“扩展项”中查看扩展信息的时候,你能看到的只有扩展的name,ID你是看不到的,但是Firefox的确是依靠ID来工作的。正如我们在平时总是称呼各自的名字,但是等到登记信息的时候,完全是按照身份证号来区分的。Firefox也是一样的。
em:version:顾名思义,这是扩展的版本号,没什么多说的。
em:type:这是类型。Firefox的插件很强,它支持的不只扩展一种。例如:type=2时表示扩展(Extensions), type=4时表示主题(Themes),type= 8时表示地区(Locale),这和本地化有关,type=16时表示插件(Plugin)。
em:description:扩展的描述,对扩展的简单说明。
em:creator:扩展的创建者。
em:contributor:扩展的贡献者。可以有多个em:contributor,毕竟可以有很多贡献者。
em:homepageURL:扩展的主页。
以上几项,除了ID之外,都会出现在Firefox的“工具”-〉“扩展”项的扩展列表的各项摘要中。
好,让我们到下一部分:


{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
0.9
1.5


em:targetApplication:本扩展可以被使用的应用程序,它的字节点是对这个应用程序的说明,我们的Hello world仅在Firefox下运行,所以只有一个em:targetApplication节点,如果你认为你开发的扩展可以在mozilla的其它应用程序中运行的话,可以在这里多写几个em:targetApplication。
em:id:em:targetApplication的ID号,正如Firefox的扩展有自己的ID号一样,Mozilla的每一个应用程序也都有自己的ID号。例如Firefox、Thunderbird等等,他们都需要有自己的ID号来进行标识。例如,本扩展使用的Firefox的ID号是{ec8030f7-c20a-464f-9b0e-13a3a9e97384}。
em:minVersion:扩展支持的应用程序的最低版本号,举个例子,Firefox1.5有很多的功能是FF1.0.7使用不了的,或者FF1.0.7的一些功能已不被FF1.5支持了,这样的话,每一个扩展都应该有一个Firefox的适用的版本的范围。最大值和最小值的意义也在于此。
em:maxVersion:说明同上,假设这个值为1.0+,那么你会发现Firefox1.5会自动将其禁用,解决方法是你可以将这个值手动改为1.5,前提是这个扩展可以在Firefox1.5下正常运行。
有一些支持Firefox旧版本的扩展还会有下列的东东:


content/
skin/
locale/zh-CN/
locale/zh-TW/
locale/en-US/


这段东西是像Firefox说明了扩展的目录分布。Firefox1.5已经不使用这部分了,对应的,将这部分的内容转移到了chrome.manifest文件中,只有当chrome.manifest文件中没有相应的目录分布的时候,才会回来找。
如Firefox扩展的格式所说,扩展包是由content、skin、locale三部分组成的。这段就说明了这三部分的分布情况,具体的对应内容参见下部分chrome.manifest。
需要注意的是,上面每一个节点中的值都是以“/”结尾的,如果漏掉了这个东西,Firefox就会找不到对应得目录!
理解Chrome[5]
在分析chrome.manifest文件之前,我们必须理解Chrome这个概念。作为Firefox,它的底层是使用了高效并且不能被修改的运行时引擎(runtime engine),在此之上是比较“厚”的可读可修改的解释层。
Chrome代表了Firefox提供的所有用户接口——XUL、CSS、JavaScript、图像、 RDF、文本和HTML文档。RDF和XUL是最重要的文档。
从物理层面上说,Chrome是Firefox数据库中的数据。Firefox需要获得扩展的信息,所以它会在启动的时候,读入RDF文件,完成对扩展的注册,将扩展的信息存放到Firefox的内存数据库中。所以一个扩展只有在Firefox的扩展搜索范围中,并且被Firefox注册了,它才能称为Chrome。
在逻辑层面上说,Chome是一组URL的集合。正如你可以使用http://www.google.com 来访问Google的网页一样,你可以chrome://URL 的方式来访问Chrome的资源。事实上,这是一种映射关系。例如你的电脑中根目录下存放有一个扩展,其中有一个xul文件:/tests/content/package.xul,你可以在浏览器中的地址栏中输入:file:///tests/content/ package.xul ,这样你就可以查看该文件了,但是在Chrome中,如果你已经将conten目录注册了,那么你同样可以在地址栏中输入chrome://test/content/package.xul,这样就可以浏览这个文件了。所以从这个角度来说Chrome可以看作一种Firefox自己定义的协议,不是吗?
说道Chrome,不得不说一下Jar文件。Firefox支持将Chrome的内容全部放在一个zip格式的文件中,这个文件的后缀名为.jar。这个文件可以包括窗口内容、皮肤主题、本地化代码,以及前三个的任意组合。
至于jar文件中内容的引用有所不同。例如有一个/test/hello.jar中还有一个helloworld.xul文件,那么该文件引用地址应该为:
jar:file:///tmp/example.jar!/example.txt。
请注意jar后面有一个感叹号。特别注意的是jar文件的目录分布。
例如正常没有压缩的目录排布为:
test/content/…
test/locale/…
test/skin/…
但是如果要将其压缩为jar文件,那么需要将其重新排列为:
/content/test
/locale /test
/skin /test
这种设计的一个合理解释是Firefox可以在运行的时候更快地搜索该压缩文件。
chrome.manifest
这个文件是Firefox1.5引入的,用于对一些扩展内容、结构的说明和映射。我的Hello World的chrome.manifest文件内如如下:
content    helloworld                                         chrome/content/
locale      helloworld                    en-US                   chrome/locale/en-US/
locale      helloworld                    zh-CN                   chrome/locale/zh-CN/
skin        helloworld                    classic/1.0              chrome/skin/
overlay    chrome://browser/content/browser.xul   chrome://helloworld/content/helloworld-Overlay.xul
style   chrome://global/content/customizeToolbar.xul  chrome://helloworld/skin/helloworld.css
先看前四行:
content    helloworld                                         chrome/content/
locale      helloworld                    en-US                   chrome/locale/en-US/
locale      helloworld                    zh-CN                   chrome/locale/zh-CN/
skin        helloworld                    classic/1.0              chrome/skin/
根据上一节的叙述,这四行是为了让Firefox在启动的时候,将本地的目录注册到Chrome的数据库中。
我们的Hello world扩展包中包含的内容有content、locale、skin三个部分。其中locale包含了美式英语和简体中文的文件,所以一共有四项。
Content的格式为:
content     Name    Location
其中,
content:chrome包中的类型,这里为content
Name:chrome包的名字
Location:chrome包文件的位置。注意最后的“/”,别忘了,否则扩展是无法加载的!
所以,第一行的意思就是:一个叫sample的chrome包, 我们可以从位置chrome/content/找到它的content文件。这里的路径是相对于chrome.manifest文件的路径而言的相对路径。
类似的,下面三句定义了locale和skin的位置。
现在,我们已经将本地物理文件与Firefox逻辑上的Chrome的URL建立一个映射,例如:content-〉chrome/content/。我们的扩展有一个content.rdf文件,位置在/chrome/content下,那么我们就可以在浏览器的地址栏中输入 “chrome://helloworld/content/content.rdf”来查看该文件,事实上,Firefox也是按照这个URL来寻找该文件的。
这四行可以与上一节的em:file对照一下,它们在Firefox中实现的功能是一致的。只不过这种写法是Firefox 1.5引入的。假如你的扩展包中没有chrome.manifest而只有install.rdf文件,那么Firefox会解析install.rdf文件,之后,生成一个chrome.manifest文件。
接下来的两行:
overlay     chrome://browser/content/browser.xul                                                    chrome://helloworld/content/helloworld-Overlay.xul
style chrome://global/content/customizeToolbar.xul                 chrome://helloworld/skin/helloworld.css
这两行也是向Firefox注册,但这次注册的是你需要重写控件的代码文件(customizeToolbar.xul和helloworld-Overlay.xul)和控件的样式文件(helloworld.css)。Firefox启动的时候会将helloworld-Overlay.xul 合并到browser.xul,从而实现自定义控件的加载。
这几个文件的详细内容我们在接下来的部分进行讨论。
让Firefox说“Hello World”
添加控件接下来,我们需要实现一些基本功能。首先添加一个菜单,用户点击之后,可以弹出一个“Hello World”的窗口。接下来,我们熟悉一下其他的控件,例如状态栏等。
Firefox的控件是由前台的xml格式的文件xul和后台的javascript脚本的js两部分内容组成的。前台的xul文件定义控件的外观和触发事件,后台的js文件实现具体的事件,从而实现了表现和实现的分离。
添加菜单项
首先,我们来实现菜单的菜单项。本例的效果是添加一个菜单项,如下图的“Click Me!”项:

记得上一节我们在chrome.manifest文件中将browser.xul 和helloworld-Overlay.xul注册了吗?我们使用了overlay       chrome://browser/content/browser.xul   chrome://helloworld/content/helloworld-Overlay.xul,从而Firefox在加载默认浏览器文件browser.xul的同时,会将helloworld-Overlay.xul也加载上去。
所以,我们需要再helloworld-Overlay.xul中加入自己的控件。好了,我写了一个简单的控件menu_Hello:
helloworld-Overlay.xul

xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">