Hello World ——开发你的第一个Firefox扩展 刘文懋 Why Firefox?如果有人问我为什么用Firefox[1],首先毫无疑问的是它代表了开放和自由的精神,其次嘛,我会说是它的可扩展性。 Firefox最激动人心的特点就是它提供了开放的接口,你可以使用这些接口来做各种应用,完成各种各样的功能。你应该不曾给IE添点什么吧,微软不见得这么大方能告诉你什么有用的东西。他最慷慨的事就是给你一个COM组件,记得我对IE的最高级的应用,就是在一次网络大作业中,把整个IE嵌在我的程序里,仅此而已。而Firefox则不同了,当你看到了Fireftp就知道我在说什么了。
请合上你的下巴,这没有什么值得惊讶的。这就是Firefox,中间那个cool的ftp工具只是它的一个扩展(Extesion)而已。 想做一个这样的东东吗?我不会告诉你到底有多难,因为这不是我开发的,有兴趣的话你可以自己试一试,网址是http://fireftp./ 。 类似的扩展还有很多,你可以到mozilla的扩展库去看看,据官方统计,到 以前我在99%的时间内使用Firefox,另外1%的时间使用IE浏览一些不遵从W 好了,说了这么多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.rdfinstall.rdf是一个扩展的身份证。这么说并不为过,请看下面就是我们helloworld的install.rdf文件: <?xml version="1.0"?> <RDF xmlns="http://www./ xmlns:em="http://www.mozilla.org/2004/em-rdf#"> <Description about="urn:mozilla:install-manifest"> <em:name>Hello World</em:name> <em:id>{ <em:version>0.4</em:version> <em:type>2</em:type> <em:description>A test extension</em:description> <em:creator>Marvel</em:creator> <em:contributor>LiuWenmao</em:contributor> <em:homepageURL>http://marvel./</em:homepageURL> <em:targetApplication> <Description> <em:id>{ec <em:minVersion>0.9</em:minVersion> <em:maxVersion>1.5</em:maxVersion> </Description> </em:targetApplication> </Description> </RDF> 可以看得出,这是一个xml格式的文件。根节点为RDF。命名空间为http://www./1999/02/22-rdf-syntax-ns# ,前缀为em[4]。下面介绍每一个元素和属性的意义: <em:name>Hello World</em:name> <em:id>{ <em:version>0.4</em:version> <em:type>2</em:type> <em:description>A test extension</em:description> <em:creator>Marvel</em:creator> <em:contributor>LiuWenmao</em:contributor> <em:homepageURL>http://marvel./</em:homepageURL> em:name:扩展的名字,例如我的名字叫“刘文懋”,而我的扩展叫“Hello World”。J em:id:扩展的ID号,每一个扩展都会有一个不同的GUID,就如同你的身份证号,用于区分你的扩展与其他人的扩展,所以很明显,全球所有的Firefox插件的ID都不一样。所以,当你写自己的扩展的时候,需要获得一个全球唯一对GUID,Andy Hoskinson帮我们完成了这项工作,你可以登录 http://www./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的“工具”-〉“扩展”项的扩展列表的各项摘要中。 好,让我们到下一部分: <em:targetApplication> <Description> <em:id>{ec <em:minVersion>0.9</em:minVersion> <em:maxVersion>1.5</em:maxVersion> </Description> </em:targetApplication> em:targetApplication:本扩展可以被使用的应用程序,它的字节点是对这个应用程序的说明,我们的Hello world仅在Firefox下运行,所以只有一个em:targetApplication节点,如果你认为你开发的扩展可以在mozilla的其它应用程序中运行的话,可以在这里多写几个em:targetApplication。 em:id:em:targetApplication的ID号,正如Firefox的扩展有自己的ID号一样,Mozilla的每一个应用程序也都有自己的ID号。例如Firefox、Thunderbird等等,他们都需要有自己的ID号来进行标识。例如,本扩展使用的Firefox的ID号是{ec em:minVersion:扩展支持的应用程序的最低版本号,举个例子,Firefox1.5有很多的功能是FF em:maxVersion:说明同上,假设这个值为1.0+,那么你会发现Firefox1.5会自动将其禁用,解决方法是你可以将这个值手动改为1.5,前提是这个扩展可以在Firefox1.5下正常运行。 有一些支持Firefox旧版本的扩展还会有下列的东东: <em:file> <Description about="urn:mozilla:extension:file:xyzproxy"> <em:package>content/</em:package> <em:skin>skin/</em:skin> <em:locale>locale/zh-CN/</em:locale> <em:locale>locale/zh-TW/</em:locale> <em:locale>locale/en-US/</em:locale> </Description> </em:file> 这段东西是像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 <?xml version="1.0"?> <overlay id="helloworldOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/x-javascript" src="chrome://helloworld/content/hello.js"/> <menupopup id="menu_ToolsPopup"> <menuitem id="menu_Hello" label=" Click Me! " accesskey="C" position="1" oncommand="onShowMenu(); "/> </menupopup> </overlay> 下面我们来分析一下各个部分。 首先,定义了本文件唯一的ID号为“helloworldOverlay”。 接着,定义了控件触发事件的脚本文件为 chrome://helloworld/content/hello.js。 然后,我们开始定义控件。首先要找到控件的父控件——至少Firefox要知道该把它放在哪里。这里我们找到了“工具”菜单menu_ToolsPopup。至于我们怎么知道“工具”菜单的ID是menu_ToolsPopup的,我推荐使用Firefox自带的开发工具Dom Inspector,至少我是这么知道它的ID的。 找到父控件之后,我们就可以自定义控件了: <menuitem id="menu_Hello" label=" Click Me! " accesskey="C" position="1" oncommand="onShowMenu(); "/> 这句说明,我们的控件ID为menu_Hello,显示的文本为Click Me!,快捷键为C,位置在“工具”菜单的最上面,点击后触发的事件为onShowMenu()(onShowMenu事件在hello.js中定义,正如我们上面所说的)。 好了,定义完控件的外部属性,我们就需要处理它的触发事件了。打开chrome\content\ hello.js,输入: function onShowMenu() { window.alert("hello"); } 这样我们就完成了菜单项的工作。 如果你现在就像看看效果,那么请转到“部署Firefox扩展”部分;如果你还想看看其他的控件以及样式表的使用,可以继续。 添加工具栏接下来,我们会添加一个工具栏按钮。从这部分,我们可以知道如何使用工具栏控件,以及如何使用样式。这部分的实际效果如下图: 我们修改一下helloworld-Overlay.xul文件,添加一个叫tbarHello 的toolbarbutton,以及下拉菜单和菜单项。为了简单起见,我去掉了上一节的菜单项的部分: helloworld-Overlay.xul <?xml version="1.0"?> <?xml-stylesheet href="chrome://helloworld/skin/helloworld.css" type="text/css"?> <!DOCTYPE overlay SYSTEM "chrome://helloworld/locale/helloworld.dtd" > <overlay id="helloworldOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/x-javascript" src="chrome://helloworld/content/hello.js"/> <toolbox id="navigator-toolbox"> <toolbarpalette id="BrowserToolbarPalette" > <toolbarbutton id="tbarHello" tooltiptext="Toolbar Test" type="menu-button" label="Test" oncommand=" onTbarHello();"> <menupopup id="hello-popup-list" onpopupshowing="onShowMenu();"> <menu id="hello-tools-menu" label=" Manage "> <menupopup id="toolbutton-popup-tools"> <menuitem id="menuitem-add" label="Add" tooltiptext=" Add your name" oncommand="addName();"/> <menuitem id="menuitem-remove" label=" Remove " tooltiptext="Remove your name" oncommand="removeName();"/> </menupopup> </menu> <menuseparator /> <menuitem id="menuitem-state" label=" State " tooltiptext=" Show your state " oncommand="showState();"/> </menupopup> </toolbarbutton> </toolbarpalette> </toolbox> </overlay> 下面我们来分析这段代码: 1导入控件样式: <?xml-stylesheet href="chrome://helloworld/skin/helloworld.css" type="text/css"?> 我们需要对下面的控件进行修饰,所以需要引入css样式,这与HTTP的css类似。上面这句说明,我们可能会用到chrome://helloworld/skin/helloworld.css定义的控件样式,事实上的确如此。 2 定义控件 <navigator-toolbox>……</navigator-toolbox>就是定义控件的代码。首先我们找到navigator-toolbox节点,就是Firefox的导航栏。然后在上面添加一个按钮tbarHello,类型为menu-button,,触发事件为onTbarHello()。此外还包括了一个菜单、三个菜单项。具体的说明这里就省略了,你可以使用Dom Inspector来查看各个控件的类型和属性值。 3 定义控件样式 也许你会说,我只看到了你导入了样式文件,但是具体它是怎么使用的呢?或者说控件和它的样式是怎么管联起来的呢? 我们可以来看一看我们导入的CSS文件: Helloworld.css @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); #tbarHello { list-style-image: url("chrome://helloworld/skin/qq_big.png"); } toolbar[iconsize="small"] #tbarHello { list-style-image: url("chrome://helloworld/skin/qq_small.png"); } 我们可以看到,这个文件定义了一个tbarHello的list-style-image属性[6]。list-style-image的一般语法是:list-style-image: url(…)|none, 这个属性是用来显示特定list-item的图像的,即用后面url中的图像来显示该控件。这里我们使用的是chrome://helloworld/skin/qq_big.png。 但是仅仅有这句还是不够的。Firefox提供了两种图标的显示方法,一种是大图标,另一种是小图标。大图标默认的大小为24x24,小图标默认的大小为16x16。刚才我们定义的图标是大图标,所以我们需要定义小图标,方法为toolbar[iconsize="small"] #tbarHello{}这三行。 这样我们定义了toolbar的显示图像。当然还有其他的属性可以定义,但是这里我们只定义了它的图像。 4 实现控件功能 这部分尽管是最重要的,但是在本例这里不是重点。所以可以用最简单的方式实现xul文件中需要使用的onTbarHello、addName、removeName和showState函数。 我们的hello.js代码如下: Hello.js function addName() { window.alert("Add name"); } function removeName() { window.alert("Remove name"); } function showState() { window.alert("Show state"); } function onShowMenu() { window.dump("hello"); window.alert("hello"); } function onTbarHello() { window.alert("hello"); } 至此,我们完成了控件的样式的定义和使用。你可以加入更多的样式,完成更复杂的功能,只要你有足够的想象力。 实现本地化[7]好了,到目前为止,Firefox可以说“Hello World”了。但问题是世界上还有十三亿中国人,那么显然“Hello World”使他们认为Firefox是一个洋玩意儿,它们可能会给Firefox起一个名字叫“洋狐狸”[8]。更糟的是,这十三亿人中很多人只认识中文,不懂得abc,那么只有英文的Firefox不会是他们的选择。 在本节中,我们讲述的是如何在Firefox中实现扩展的本地化。我们需要重写helloworld-Overlay.xul : helloworld-Overlay.xul <?xml version="1.0"?> <?xml-stylesheet href="chrome://helloworld/skin/helloworld.css" type="text/css"?> <!DOCTYPE overlay SYSTEM "chrome://helloworld/locale/helloworld.dtd" > <overlay id="helloworldOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="apKlication/x-javascript" src="chrome://helloworld/content/hello.js"/> <menuKopup id="menu_ToolsPopup"> <menuitem id="menu_Hello" label="&btn_Hello.label;" accesskey="&btn_Hello.accesskey;" position="1" oncommand="onShowMenu(); "/> </menuKopup> </overlay> 注意到和第一个版本有什么不同吗?相信你已经发现了menu_Hello的label已经变成了&btn_Hello.label;,同样的accesskey也变成了这种形式。 如果你学过HTML语法,你可能会想到这可能是转义。例如,在HTML中“<”被转义成为了“<”。 对,这也是一种转义,但是略微不同的是,&btn_Hello.label;和&btn_Hello.accesskey;其实Firefox或者其他的标准都没有定义它们的含义,这是有你自己来决定的。 你需要在你导入的dtd文件中进行说明,本例中,你需要在chrome://helloworld/locale/helloworld.dtd添加相应的定义。 也许到这一步你会问,这个helloworld.dtd文件是哪一个呢?到底是chrome\locale\en-US\ helloworld.dtd 还是 chrome\locale\zh-CN helloworld.dtd呢? 其实,这是由Firefox决定的。如果你的Firefox的locale是en-US,那就是chrome\locale\en-US\ helloworld.dtd,如果你的Firefox的locale是zh-CN,那就是chrome\locale\ zh-CN \ helloworld.dtd。 你需要做的工作就是就是告诉Firefox你的en-US或zh-CN的locale文件夹在什么地方。现在你要在chrome\locale\en-US和chrome\locale\zh-CN下分别建立一个contents.rdf文件。chrome\locale\en-US下的contents.rdf如下: contents.rdf <?xml version="1.0" ?> - <RDF:RDF xmlns:RDF="http://www./ <RDF:Seq about="urn:mozilla:locale:root"> <RDF:li resource="urn:mozilla:locale:zh-CN" /> </RDF:Seq> - <!-- locale information -->
- <RDF:Description about="urn:mozilla:locale:en-US"> - <chrome:packages> - <RDF:Seq about="urn:mozilla:locale: en-US:packages"> <RDF:li resource="urn:mozilla:locale: en-US:helloworld" /> </RDF:Seq> </chrome:packages> </RDF:Description> </RDF:RDF> chrome\locale\en-US下的contents.rdf也类似,只不过把所有的zh-CN换成en-US即可。 最后,我们要在相应的locale目录下建立不同locale的helloworld.dtd。还是以英语为例,我们新建chrome\locale\en-US\helloworld.dtd,内容如下: helloworld.dtd(en-US) <!ENTITY btn_Hello.label "Click Me!"> <!ENTITY btn_Hello.accesskey "C"> <!ENTITY tbarHello.label "Test"> <!ENTITY tbarHello.tip "Name test"> <!ENTITY manage.label "Manage"> <!ENTITY manage.tip "Management"> <!ENTITY add.label "Add"> <!ENTITY add.tip "Add your name"> <!ENTITY remove.label "Remove"> <!ENTITY remove.tip "Remove your name"> <!ENTITY state.label "State"> <!ENTITY state.tip "Show your state"> 在本例中,当Firefox解析到helloworld-Overlay.xul文件中的“label="&btn_Hello.label;”时,并且locale为en-US,那么它就会到chrome\locale\en-US\helloworld.dtd文件中寻找相应的btn_Hello.label项,即“"Click Me!”。 这样,我们就实现了动态加载控件的label,同样的,我们可以加载tip。 类似的,我们可以填写chrome\locale\zh-CN下的helloworld.dtd文件。假如系统的locale为zh-CN,那么我们的helloworld就会说中文了。 这样,当Firefox每次启动的时候,会根据系统的locale来动态的加载不同的helloworld.dtd。 部署Firefox的扩展现在Firefox的部署方式一共有两种: 打包部署这种方式是最普遍的,可以得到Firefox的所有版本的支持。我们可以将完成的扩展目录压缩成zip格式的文件,后缀名为.xpi。 当涉及到扩展中有jar文件的时候,打包过程会比较复杂,因为存在多次压缩的过程。为了简单起见,你可以自己编写一个build.xml, 然后使用ant[9]来组装。下面就是一个文件的样例: <?xml version="1.0"?>
<project name="helloworld" default="createxpi">
<target name="createjar">
<zip destfile="helloworld.jar" basedir="."
includes="content/**" />
</target>
<target name="createxpi" depends="createjar">
<zip destfile="helloworld.xpi">
<zipfileset dir="." includes="helloworld.jar"
prefix="chrome" />
<zipfileset dir="." includes="install.rdf" />
</zip>
</target>
</project>
安装有两种方式,你可以将这个xpi文件上传到Web服务器上,但前提是该服务器能实现application/x-xpinstall功能。另一种方法是在Firefox中选择“文件”-〉“打开文件”,选择该xpi文件即可。 直接部署Firefox1.5支持这种部署方式。这种方式特别适合调试扩展。 你可以直接到Firefox的扩展的系统目录,一般为“%SYSTEM_DRIVER%:\Documents and Settings\%User%\Application Data\Mozilla\Firefox\Profiles\ default\extensions”。 建立一个文本文件,文件内容为你的扩展的位置。例如我的是“E:\My Documents\Visual Studio Projects\firefox\helloworld\chrome\content”,文件名为扩展的ID号,本例为{ 保存文件后,重启Firefox即可。 无论用哪种方式,Firefox启动之后,都会加载扩展。你可以到“工具”-〉“扩展项”中查看具体的内容,信息应该与你的install.rdf内容一致。 调试Firefox的扩展你可以在控制台中对firefox进行调试。Firefox的控制台类似于C语言的控制台,你可以将一些变量的值打印出来,或是可以打印出一些控制的信息,这些信息对你调试firefox的扩展都是十分有用的。 首先,你需要将这个功能打开,在地址栏中输入:about:config , 此时你会发现有很多firefox的配置项,怎么有点像windows的注册表呢?That’s right,这就是Firefox的注册表,你可以在这里放入一些定制的值,实现特定的功能。 我们需要新建一个Boolean类型的配置项 “browser.dom.window.dump.enabled”,它的值为true。 然后,在你的Firefox的快捷方式的属性中,把“目标”项添加一个“-console”,例如我的就改为了"C:\Program Files\Mozilla Firefox\firefox.exe" –console ,这样每次Firefox启动的时候都会出现一个控制台。当然你也可以在命令行中输入上面的命令,但是你应该知道最简单的方式工作的最好,不是吗? 最后,你可以在你的程序中添加window.dump()函数。例如: window.dump(“Hello world!\n”); 这样,当程序执行到这里的时候,控制台上就会出现“Hello world”的字样了。 获得Hello World的源代码你可以从以下地址获得本的实验的Hello World代码: http://marvel.:8080/text/firefox/helloworld.xpi 贡献Firefox社区如果你想写自己的Firefox扩展,或者想为Firefox做些东西,可以登录mozilla的开发网站:http://www./ 。希望你也能成为Firefox的一名开发者。 Contact Me本人能力有限,一定有地方讲的不正确或是不确切,欢迎来信交流。我的Email是liuwenmao@ 参考文献[1] [2] Brian Duff, Writing an Extension for Firefox , October 02, 2004, http://www./duffblog/archives/000536.html [1] Firefox的简称为FF,本文以后涉及到的所有“FF”,如无特别说明,均指Firefox。 [3] JAR 文件就是 Java Archive File,JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。 [4] Xml格式不在本文的讨论范围之内。如果读者想知道xml命名空间的更多内容,请参见 《了解 XML 命名空间》中的“使用命名空间”一节(http://www./A/2004-11-14/140015.html ) [5]参见《Firefox Hacks: Tips & Tools for Next-Generation Web Browsing》 :http://books.google.com/books?hl=en&id=PNEYS39cvRQC&dq=what+is+chrome+firefox&prev=http://www.google.com/search%3Fq%3Dwhat%2Bis%2Bchrome%2Bfirefox%26btnG%3D%25E6%2590%259C%25E7%25B4%25A2%26hl%3Dzh-CN%26newwindow%3D1&pg=PP1&printsec=0&lpg=PP1&sig=oUyWZLI9PC_KsnNAoF6msKBLSbI [6]参见 http://www./reference/css/classification/list-style-image.html 和http://www./css/pr_list-style-image.asp [7] 关于国际化和本地化的更多内容参见“开源软件国际化” http://i18n./ 和Linux 国际化本地化和中文化 http://www./doc/i18n-new.html [8] 我们家乡话就有很多带“洋”的词,例如“洋火柴”、“洋娃娃”等等。因为当时这些都是从西洋过来的。 [9] Ant可以从 http://ant./ 免费下载。 |
|