2007年7月31日星期二

开发你的第一个Firefox扩展

 开发你的第一个Firefox扩展

刘文懋

Why Firefox

如果有人问我为什么用Firefox[1],首先毫无疑问的是它代表了开放和自由的精神,其次嘛,我会说是它的可扩展性。

Firefox最激动人心的特点就是它提供了开放的接口,你可以使用这些接口来做各种应用,完成各种各样的功能。你应该不曾给IE添点什么吧,微软不见得这么大方能告诉你什么有用的东西。他最慷慨的事就是给你一个COM组件,记得我对IE的最高级的应用,就是在一次网络大作业中,把整个IE嵌在我的程序里,仅此而已。而Firefox则不同了,当你看到了Fireftp就知道我在说什么了。

 

 

请合上你的下巴,这没有什么值得惊讶的。这就是Firefox,中间那个coolftp工具只是它的一个扩展(Extesion)而已。

想做一个这样的东东吗?我不会告诉你到底有多难,因为这不是我开发的,有兴趣的话你可以自己试一试,网址是http://fireftp.mozdev.org/

类似的扩展还有很多,你可以到mozilla的扩展库去看看,据官方统计,到2005119,(忘了告诉你了,2004119Firefox的诞生日,也就是说它还只是一个小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下面的格式就不一定了,但是基本都会含有contentlocaleskin三个目录。其中,

 

content目录是用来存放扩展的程序文件和控件格式的资源文件;

locale目录存放不同语言版本,用于扩展的本地化和国际化;

skin目录存放图片等资源文件。

 

扩展的格式并不一定要拘泥一格,但具体情况需要与install.rdfchrome.manifest文件中的信息联系。记住,无论如何,良好的习惯总是对你有好处的:请把相应的文件放到它们应该放的地方。

扩展的基本内容

install.rdf

install.rdf是一个扩展的身份证。这么说并不为过,请看下面就是我们helloworldinstall.rdf文件:

 

<?xml version="1.0"?>

<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

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

  <Description about="urn:mozilla:install-manifest">

    <em:name>Hello World</em:name>

    <em:id>{12a1584b-2123-473d-8752-e82e74e3cb11}</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.hit.edu.cn/</em:homepageURL>

    <em:targetApplication>

      <Description>

        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>

        <em:minVersion>0.9</em:minVersion>

        <em:maxVersion>1.5</em:maxVersion>

      </Description>

    </em:targetApplication>

  </Description>

</RDF>

可以看得出,这是一个xml格式的文件。根节点为RDF。命名空间为http://www.w3.org/1999/02/22-rdf-syntax-ns# 前缀为em[4]。下面介绍每一个元素和属性的意义:

    <em:name>Hello World</em:name>

    <em:id>{12a1584b-2123-473d-8752-e82e74e3cb11}</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.hit.edu.cn/</em:homepageURL>

 

em:name扩展的名字,例如我的名字叫"刘文懋",而我的扩展叫"Hello World"。J

em:id扩展的ID号,每一个扩展都会有一个不同的GUID,就如同你的身份证号,用于区分你的扩展与其他人的扩展,所以很明显,全球所有的Firefox插件的ID都不一样。所以,当你写自己的扩展的时候,需要获得一个全球唯一对GUIDAndy Hoskinson帮我们完成了这项工作,你可以登录 http://www.hoskinson.net/webservices/guidgeneratorclient.aspx 来获得一个GUID。点击一下Generate GUID按钮,很简单吧,但是这很重要,如果你不想惹麻烦的话!

Firefox1.5支持"User@Location"这种ID的格式。

请注意,当你在"扩展项"中查看扩展信息的时候,你能看到的只有扩展的nameID你是看不到的,但是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>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>

        <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:idem:targetApplicationID号,正如Firefox的扩展有自己的ID号一样,Mozilla的每一个应用程序也都有自己的ID号。例如FirefoxThunderbird等等,他们都需要有自己的ID号来进行标识。例如,本扩展使用的FirefoxID号是{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旧版本的扩展还会有下列的东东:

    <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扩展的格式所说,扩展包是由contentskinlocale三部分组成的。这段就说明了这三部分的分布情况,具体的对应内容参见下部分chrome.manifest

需要注意的是,上面每一个节点中的值都是以"/"结尾的,如果漏掉了这个东西,Firefox就会找不到对应得目录!

理解Chrome[5]

    在分析chrome.manifest文件之前,我们必须理解Chrome这个概念。作为Firefox,它的底层是使用了高效并且不能被修改的运行时引擎(runtime engine),在此之上是比较"厚"的可读可修改的解释层。

Chrome代表了Firefox提供的所有用户接口——XULCSSJavaScript、图像、 RDF、文本和HTML文档。RDFXUL是最重要的文档。

从物理层面上说,ChromeFirefox数据库中的数据。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 Worldchrome.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扩展包中包含的内容有contentlocaleskin三个部分。其中locale包含了美式英语和简体中文的文件,所以一共有四项。

Content的格式为:

content     Name    Location

其中,

contentchrome包中的类型,这里为content

Namechrome包的名字

Locationchrome包文件的位置。注意最后的"/",别忘了,否则扩展是无法加载的!

所以,第一行的意思就是:一个叫samplechrome包, 我们可以从位置chrome/content/找到它的content文件。这里的路径是相对于chrome.manifest文件的路径而言的相对路径。

类似的,下面三句定义了localeskin的位置。

现在,我们已经将本地物理文件与Firefox逻辑上的ChromeURL建立一个映射,例如: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.xulhelloworld-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。至于我们怎么知道"工具"菜单的IDmenu_ToolsPopup的,我推荐使用Firefox自带的开发工具Dom Inspector,至少我是这么知道它的ID的。

找到父控件之后,我们就可以自定义控件了:

 

<menuitem id="menu_Hello" label=" Click Me! "        accesskey="C" position="1" oncommand="onShowMenu(); "/>

 

    这句说明,我们的控件IDmenu_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样式,这与HTTPcss类似。上面这句说明,我们可能会用到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");

}

我们可以看到,这个文件定义了一个tbarHellolist-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文件中需要使用的onTbarHelloaddNameremoveNameshowState函数。

我们的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_Hellolabel已经变成了&btn_Hello.label;,同样的accesskey也变成了这种形式。

如果你学过HTML语法,你可能会想到这可能是转义。例如,在HTML中"<"被转义成为了"&lt;"。

对,这也是一种转义,但是略微不同的是,&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决定的。如果你的Firefoxlocaleen-US,那就是chrome\locale\en-US\ helloworld.dtd,如果你的Firefoxlocalezh-CN,那就是chrome\locale\ zh-CN \ helloworld.dtd

你需要做的工作就是就是告诉Firefox你的en-USzh-CNlocale文件夹在什么地方。现在你要在chrome\locale\en-USchrome\locale\zh-CN下分别建立一个contents.rdf文件。chrome\locale\en-US下的contents.rdf如下:

contents.rdf

  <?xml version="1.0" ?>

- <RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:chrome="http://www.mozilla.org/rdf/chrome#">

<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目录下建立不同localehelloworld.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;"时,并且localeen-US,那么它就会到chrome\locale\en-US\helloworld.dtd文件中寻找相应的btn_Hello.label项,即""Click Me!"。

这样,我们就实现了动态加载控件的label,同样的,我们可以加载tip

类似的,我们可以填写chrome\locale\zh-CN下的helloworld.dtd文件。假如系统的localezh-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号,本例为{12a1584b-2123-473d-8752-e82e74e3cb11}

    保存文件后,重启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.hit.edu.cn:8080/text/firefox/helloworld.xpi

 

贡献Firefox社区

如果你想写自己的Firefox扩展,或者想为Firefox做些东西,可以登录mozilla的开发网站:http://www.mozdev.org/ 。希望你也能成为Firefox的一名开发者。

 

Contact Me

本人能力有限,一定有地方讲的不正确或是不确切,欢迎来信交流。我的Emailliuwenmao@hit.edu.cn

参考文献

[1] Mozilla Developer Center,     Building an Extension, 15 November 2005, http://developer.mozilla.org/en/docs/Building_an_Extension

[2] Brian Duff, Writing an Extension for Firefox , October 02, 2004,       http://www.orablogs.com/duffblog/archives/000536.html

 

 
 
 

--
海阔天空,做什么就记什么。主要是 Ubuntu Linux 和 Latex 及编程相关的东东: http://hai-kuo.blogspot.com/ 



劲 爆 150 万 同 时 在 线,众 人 追 捧 梦 幻 西 游

没有评论: