2007年7月31日星期二

Firefox扩展开发参考资料

入门网址:
 
 
可用源码:
 
 

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

一招搞定FireFox的吃内存问题(转)

一招搞定FireFox的吃内存问题(转)

2006-04-11 星期二 09:08 :: Firefox
Firefox

FireFox自从1.5版开始,占用内存严重的问题似乎让不少忠实的FFFs(FireFox Fans)感到郁闷。虽然Mozilla官方一再否认存在这样的问题,但我相信用户并不是白痴,也不是没事闹着玩偏要去说FireFox的坏话。我自己的第二浏览器就是FireFox,虽然我不是FireFox的忠实爱好者,但相比之处,基于IE核心的TheWorld浏览器在速度及内存占用方面更有优势。但如果你真的不想放弃FireFox,下面有一个非常简单的方法可以让你把FireFox的内存占用降到最低。我自己试了一下似乎挺管用的,分享如下。

  这次也是Ryan,他基本上是一位大师级的FireFox专家,经常带来第一手FireFox动态及相关技巧,强烈推荐FireFox爱好者去他那里看一下(记得要开代理啦)。言归正传,这个方法非常的简单,无需安装任何额外的软件,只需几个步骤。我以FireFox1.5.0.1简体中文版为例:

1.打开FireFox,在地址栏里输入about:config
2.按右键,选择新建――>布尔
3.在弹出的框里输入config.trim_on_minimize
4.然后选True
5.重新启动FireFox

  重启后将FireFox最小化,现在按CTRL+ALT+DEL调出任务管理器看看FireFox的内存占用是不是低于10MB了?对,这个简单的优化能让你在最小化、最大化FireFox的时候,把它占用的多余内存都释放出来;并且当你刷新网页的时候,一部分内存也会被释放掉,达到占用更少内存的目的。其实这是IE7以及Opera早就默认具备的特性,只是FireFox还没有加上而已,相信以后的版本会改善这种情况的。

  当然,如果你不觉得自己的FireFox吃很多内存又或者你的电脑内存有2G,那么不必进行这种优化。

 

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

Writing an Extension for Firefox

Writing an Extension for Firefox

One of the best things about Firefox and Thunderbird is that they have a well defined extension mechanism. If there's some feature you feel is completely missing, you can go ahead and add it. It's relatively easy to do ― you don't have to fiddle about with a C compiler because extensions are mostly written in a combination of XML and ECMAScript.

I've recently been getting up to speed with the mechanics of writing an extension for Firefox and Thunderbird. I thought it might be a good idea to share what I'm learning in my blog. Hopefully the information might be useful to others trying to learn how to write Mozilla extensions. In this blog, I'll take a look at installing a really simple extension into Firefox that adds a "Hello World" menu item to the Tools menu.

Hello World item in the Firefox Tools Menu

It doesn't do anything useful yet, but over subsequent blogs, I'll introduce more advanced and useful techniques.

Creating contents.rdf

contents.rdf is a Resource Description Framework (RDF) file that describes the contents of an extension. RDF is an XML grammar that provides a data model that can be easily processed by an application. You don't need to know much about RDF to write simple extensions, but if you're interested, you can get more information at the W3C.

To get started, create a directory called content. As its name suggests, this will contain the main content of your extension. contents.rdf should live inside this directory. You should end up with a directory structure something like this:

c:\myextensions\  +- helloworld     +- content        +- contents.rdf

Here's the code for 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:package:root">      <RDF:li resource="urn:mozilla:package:helloworld"/>    </RDF:Seq>        <RDF:Description about="urn:mozilla:package:helloworld"      chrome:displayName="Hello World"      chrome:author="Brian Duff"      chrome:authorURL="http://modev.dubh.org/helloworld"      chrome:name="helloworld"      chrome:extension="true"      chrome:description="A simple demonstration firefox extension.">    </RDF:Description>       <RDF:Seq about="urn:mozilla:overlays">      <RDF:li resource="chrome://browser/content/browser.xul"/>    </RDF:Seq>          <RDF:Seq about="chrome://browser/content/browser.xul">      <RDF:li>chrome://helloworld/content/helloworld-Overlay.xul</RDF:li>    </RDF:Seq>    </RDF:RDF>

The important parts of the file (and the parts that would usually change for each extension) have been highlighted in bold. First, we provide a package for our extension. This just distinguishes it from other extensions. For our simple extension, we choose the package helloworld:

<RDF:Seq about="urn:mozilla:package:root">    <RDF:li resource="urn:mozilla:package:helloworld"/>  </RDF:Seq>

Next, we provide a description of our extension:

<RDF:Description about="urn:mozilla:package:helloworld"    chrome:displayName="Hello World"    chrome:author="Brian Duff"    chrome:authorURL="http://modev.dubh.org/helloworld"    chrome:name="helloworld"    chrome:extension="true"    chrome:description="A simple demonstration firefox extension.">  </RDF:Description>

Next, we tell mozilla which parts of the product we want to extend. All of the user interface elements of Firefox and Thunderbird are described in a user interface definition language called XUL. Collectively, these user interface components are known as "chrome". You can extend most of the user visible parts of the two products. In this case, we want to extend the main browser interface of Firefox, defined in chrome://browser/content/browser.xul. Here's how:

<RDF:Seq about="urn:mozilla:overlays">    <RDF:li resource="chrome://browser/content/browser.xul"/>  </RDF:Seq>  

Now we've described what we want to extend, we have to provide a XUL file that actually installs our custom user interface into the browser window. We will define this later in a separate XML file called helloworld-Overlay.xul. We must tell mozilla where this file is and what it extends:

<RDF:Seq about="chrome://browser/content/browser.xul">    <RDF:li>chrome://helloworld/content/helloworld-Overlay.xul</RDF:li>  </RDF:Seq>

We've completed the first step of creating an extension. The next task is to describe the user interface elements we want to install into the main window.

Overlaying User Interface Elements

XUL is the XML User interface Language. XUL provides a mechanism called dynamic overlays that allows you to modify the user interface elements of a window or control without having to modify the original XUL files. This way, the definition of extension user interface is de-coupled from the XUL files used to define the main interface elements in Firefox and Thunderbird.

We start of by creating helloworld-Overlay.xul. This should live in the same directory as contents.rdf.

<?xml version="1.0"?>    <overlay id="helloworldOverlay"     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">      <menupopup id="menu_ToolsPopup">      <menuitem label="Hello World" position="1" />    </menupopup>    </overlay>

This simple overlay installs a "Hello World" menu item at the top of the Tools menu. Our menu item is defined inside a menupopup element. In XUL, a menupopup represents a container of menu items, for example a popup menu or the drop down of a main menu. Here, we put our item inside a menupopup with the id menu_ToolsPopup. This id, menu_ToolsPopup, is a predefined in browser.xul and corresponds to the drop down part of the Tools menu.

Creating an Install Manifest

Recent versions of Firefox and Thunderbird have a new extension manager which can be used to easily install and manage extensions. To tell this extension manager about your extension, you must write another RDF file called install.rdf. This file should be at the same level in the directory tree as the contents directory, i.e. you should have a structure like this:

c:\myextensions\  +- helloworld     +- install.rdf     +- content        +- contents.rdf        +- helloworld-Overlay.xul     

Here's the code for the install manifest with the interesting parts highlighted:

<?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-e82e74e3cb1b}</em:id>      <em:version>0.1</em:version>        <em:targetApplication>        <Description>          <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>          <em:minVersion>0.9</em:minVersion>          <em:maxVersion>1.0</em:maxVersion>        </Description>      </em:targetApplication>         <em:file>        <Description about="urn:mozilla:extension:file:helloworld.jar">          <em:package>content/</em:package>        </Description>      </em:file>      </Description>    </RDF>

First, we provide a description of the extension. This information will be displayed in Firefox's extension manager:

<em:name>Hello World</em:name>  <em:id>{12a1584b-2123-473d-8752-e82e74e3cb1b}</em:id>  <em:version>0.1</em:version>  

The content of the em:id element is a globally unique identifier (GUID). This is used to distinguish your extension from every other extension. When writing your own extensions, you should aways generate a new GUID for each distinct extension you write. Andy Hoskinson provides a GUID generator web service you can use for this.

Next, we describe which application we are extending:

<em:targetApplication>    <Description>      <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>      <em:minVersion>0.9</em:minVersion>      <em:maxVersion>1.0</em:maxVersion>    </Description>  </em:targetApplication>  

Each extensible application released by Mozilla has its own GUID: you must specify the correct one here for the application you are extending. In this example, we're using the GUID for Firefox. We also describe the minimum and maximum versions of Firefox this extension will work with.

Finally, we tell the extension manager which files to install. Later, I'll describe how we package the extension up so that it can be automatically installed into Firefox:

<em:file>    <Description about="urn:mozilla:extension:file:helloworld.jar">      <em:package>content/</em:package>    </Description>  </em:file>

Packaging an Extension Installer

Our extension is now complete. But to make it easy for users to install the extension, we should package it up in such a way that Firefox can install it easily. To do this, we bundle our files up into an XPI (Cross platform installer) file. An XPI file is just a normal zip file with files organized in a special way. Our XPI file should contain the following structure:

helloworld.xpi  +- install.rdf  +- chrome/     +- helloworld.jar

helloworld.jar is another zip file containing all the files we created in the content directory:

helloworld.jar    +- content/       +- contents.rdf       +- helloworld-Overlay.xul  

You can easily use this information to create helloworld.xpi using your favorite zip tool. Alternatively, you can use a build utility like Ant to assemble the files correctly. Here's an example Ant buildfile I used:

<?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>

If you create this buildfile named build.xml and place it at the same level as install.rdf, you will be able to just run ant in this directory to generate your .xpi. Once you have an .xpi file, it can be easily installed using File->Open... in Firefox.

Hello World item in the Firefox Extension manager

Download the source code (24KB ZIP file).

 

2007-07-31

Firefox扩展开发

           最近主要做Firefox插件的开发,决定把每天的工作情况写下来,希望对其他有需要的同人有所帮助了。
         要搞这个冬冬最起码要掌握以下几门技术,XUL用来开发界面的,JavaScript用来做事件处理的,CSS用来做样式单的,DOM用来操作节点的,原 本打算也研究一下XPCOM的,但是老板说时间紧,任务重,所以就先把这个pass过去了。这几样技术单独看虽然都不是很复杂,但是绞和在一起真是可以把 人折磨死。
        先说JavaScript吧,这个东西一开始看也没觉着什么,类定义的不多,所以没什么库需要学的,但实际开发的时候,才发现类定义少了会更郁闷,想用个 什么都要自己动手,实在是类,然后变量引用,名字空间,运行上下文,prototype chain,一堆东西把人搞到云里雾里的。之前搞Java那套,放在这还真不太适用,感觉要重头再来一样,挫折啊,郁闷啊。有的时候为了省事,想拿别人的 库来用吧,才发现AJAX不但带旺了互联网,还搞红了JavaScript,一堆框架prototype,dojo...让你学的不亦乐乎。
 
          开发插件实在是很痛苦的一件事情,因为他需要用到Firefox的chrome系统,而每次更新了代码之后,都需要重新载入chrome才能起作用。 一般情况下要重启chrome就需要重新启动firefox,还好有人已经发现这种痛苦,所以开发了一个Extension Developer Manager,里面提供了好几个工具,用的最多的是reload chrome,用来在不重启firefox的情况下,重新载入chrome,第二个是javascript shell,他可以访问任意一个当前的firefox窗口。而要完成插件开发,一般还要用到Console,因为很多调试信息都需要通过这看,如果没这玩 意,那估计每次都得用alert了。至于DOM Inspector一般用不上,因为如果你要象greesemonkey那样,获取当前页面的HTML DOM的话,一般都只能通过事件来实现,然后hack到她的appcontent里面,这东西在DOM Inspector里面没有,所以,你通过这种方式修改了DOM树,通过DOM Inspector是看不出来的,而为了验证你有没有修改成功,现在我的方法就是靠javascript shell。
        关于DOM,我搞了足足两天,才算搞出个所以然来,由于firefox是用XUL写的,而XUL也定义了DOM,所以,一般情况下你在扩展里头,或者 javascript shell里面通过document获取的仅仅是XUL的DOM,要获取browser里面的DOM,我现在探索出来的有两种,一种是 greesemonkey那样,用事件,每次都监察DOMContentLoad事件,这个方法很危险,例如如果一个网页他有多个frame,那么这个事 件就会发生多次,而由于greesemonkey只是在加载的时候用user script对DOM做一次处理,所以这个问题不是特别大。不过,如果我要做的是能够多次操作DOM树,所以,就必须要得到当前窗口的当前正在显示的 DOM,为了获取这冬冬,我足足用了2天,google了所有能想到的关键词,把XUL planet和MDC上面的文章都翻了个遍,最后终于在moznet的irc上面问到人了,原来要得到这东西根本不需要用事件,只要通过 content.document就可以得到了。实在不容易啊。
 
        javasript是灵活的,但是在原始的javascript上裸开发的 好像比较少,一般大家都会选择某个库进行。js方面的lib,一开始比较有名的应该是prototype,网上关于这个的介绍和讨论也很多,但是现在好像 大家用这个的也比较少了,因为prototype大量的修改了javascript的基础类,所以对javascipt的名字空间的污染比较大,很容易发 生名字空间方面的冲突。后续的lib大都吸取了这方面的教训,一般把自己的方法和类定义在一个单一的名字空间下,不过这样带来的另外一个问题是,这些 lib更多时候象一个toolkit,当需要某些功能时,就调用这些toolkit中的功能类,因此,面向对象的特性不太明显。
        对于这些lib,现在我用的比较多的是dojo,因为他提供了比较类似于java的语法,所以,用起来比较顺手。不过用在extension里面开发时也 会遇到一些问题,因为dojo在设计的时候是针对html的dom的,但是扩展使用的是xul的dom,虽然两者之间有很多的相似之处,但是毕竟不太一 样,因此要把dojo引入扩展当中,还需要对dojo的源代码进行修改。


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



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

开发你的第一个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 万 同 时 在 线,众 人 追 捧 梦 幻 西 游