Firefox 扩展开发初试

简述



Firefox的扩展开发主要使用JavaScript。界面的制作用XUL,并且可以配合CSS使用。Mozilla的核心XPCOM——跨平台组件模型,提供了一套组件和类,用于诸如内存管理,线程,基本数据结构等,这些都可以在扩展中使用。


Firefox中有Chrome的概念。Chrome 指的是应用程序窗口的内容区域之外的用户界面元素的集合,这些用户界面元素包括工具条,菜单,进度条和窗口的标题栏等。Chrome 提供者能为特定的窗口类型(如浏览器窗口)提供 chrome。有三种基本的 chrome 提供者:


  • 内容(Content):通常是 XUL 文件。
  • 区域(Locale) :存放本地化信息。
  • 皮肤(Skin):描述 chrome 的外观。通常包含 CSS 和图像文件。


开发环境


有一些Firefox的扩展是专门为开发者提供的,使用它们可以使开发更加方便。为了不影响FF使用环境,我们可以用-P参数启动FF创建一个新的配置文件。然后安装下面的扩展:



为了调试容易,还需要设置下面FF的环境参数,在地址栏中输入about:config,然后进行修改
javascript.options.showInConsole = true //把 JavaScript 的出错信息显示在错误控制台
nglayout.debug.disable_xul_cache = true //禁用 XUL 缓存,使得对窗口和对话框的修改不需要重新加载 XUL 文件
browser.dom.window.dump.enabled = true //允许使用 dump() 语句向标准控制台输出信息
javascript.options.strict = true //在错误控制台中启用严格的 JavaScript 警告信息

在 Windows 上,为了可以在js中使用 dump 调试,在FF启动时还需要增加启动参数 -console。

扩展的目录结构


FF的扩展都是以.xpi的文件形式发布,xpi文件实际上就是一个zip文件更改了后缀名。可以通过这个 Extension Wizard来生成一个目录结构。如下:
exname
| build.sh # 两个sh的脚本在windows下没有什么用处,可以直接删掉
| chrome.manifest # Chrome 注册的清单文件
| config_build.sh
| install.rdf # 扩展安装信息
| readme.txt
|
+---content
| firefoxOverlay.xul # 覆盖的界面
| options.xul # 选项设置的界面
| overlay.js # 覆盖的程序
|
+---defaults
| \---preferences
| exname.js # 选项的默认值
|
+---locale
| \---en-US # 英语文件
| exname.dtd
| exname.properties
| prefwindow.dtd
|
\---skin
overlay.css # 样式文件

其中除了chrome.manifest和install.rdf的文件名是固定的,其他文件的名字都随意的。content目录下一般存放扩展界面和程序文件。defaults/preferences下存放扩展选项的默认值文件,FF会自动读取这个目录下所有js的文件,所以文件名是任意的。locale下是各种语言文件,如果扩展支持多语言,那么每一种语言对应到locale下的一个子目录,子目录的名字是像en-US,zh-CN这样的语言代码。skin目录下存放CSS文件,图标之类的。


把这个exname目录拷贝到FF扩展的目录下,一般是
C:\Documents and Settings\<用户名>\Application Data\Mozilla\Firefox\Profiles\<配置文件对应的子目录>\extensions
。然后把目录名由exname改为install.rdf中指定的扩展的ID,FF启动时会自动识别并加载这个新的扩展。


对扩展做了修改之后,不需要重启FF,只要安装了Extension Developer,在Tools菜单中选择
Extension Developer > Reload all Chrome
就可以了。



下载示例中的扩展 Content Filter。这个扩展的功能是对匹配url的网页,查找所有的find并替换为replace。

install.rdf


这个文件中是扩展的安装信息。
如:
<?xml version="1.0" encoding="UTF-8"?>
<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:id>contentfilter@adways.net</em:id>
<em:name>Content Filter</em:name>
<em:version>1.0</em:version>
<em:creator>Jedy</em:creator>

<em:homepageURL>http://my.homepage.net/</em:homepageURL>
<em:description>Content Filter</em:description>
<em:optionsURL>chrome://contentfilter/content/options.xul</em:optionsURL>
<!-- em:iconURL>chrome://contentfilter/content/xxx.png</em:iconURL-->

<!-- Firefox -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>3.0</em:minVersion>

<em:maxVersion>3.0.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>


这个文件里需要设置扩展的id,这也是扩展的目录名。设置了optionsURL的话,就可以在FF的扩展列表中使用选项按钮打开这里设置的选项界面。
{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
是Firefox的id,不用更改的,如果扩展还支持Thundbird等其他的Mozilla程序,那么这里会有另一段targetApplication的信息,这会使用其他程序的id。minVersion和maxVersion设置了扩展适合的程序版本,FF的扩展兼容性检查就是查这里的信息。所以如果有的扩展在FF升级后没有升级,结果不能通过兼容性检查,那么可以自己把xpi文件下载下来后改一下maxVersion之后再安装,有不少情况都是可以直接用的。

chrome.manifest


这个内容如下:
content   contentfilter   content/
overlay chrome://browser/content/browser.xul chrome://contentfilter/content/overlay.xul

content行告诉FF可以在content/目录下找到contentfilter的content文件,设置的目录路径是相对于chrome.manifest所在目录的路径。overlay行设置了对浏览器的覆盖文件。这里还可以设置skin,locale的目录等。


Mozilla通过overlay的方式使扩展可以直接更改浏览器的界面,比如可以在菜单栏上增加一组菜单,可以在状态栏上增加图标等。

overlay


通过overlay,在FF的tools菜单上增加了一个菜单项:
<?xml version="1.0"?>
<overlay id="contentfilter-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript;version=1.7" src="overlay.js" />

<menupopup id="menu_ToolsPopup">
<menuitem id="contentfilter-open" label="Content Filter..." insertafter="prefSep" oncommand="contentFilter.openConfig()" />
</menupopup>

</overlay>

扩展使用XUL来描述界面。XUL文件实际上就是一个XML文件,其中可以使用JS和CSS定义行为和样式。


上面的文件中,id为menu_ToolsPopup的menupopup就是FF中工具菜单,menuitem这句在菜单中插入了一个“Content Filter...”的菜单项,插入的位置是在prefSep分隔之后,点击时执行contentFilter.openConfig函数。这里使用的各个FF中组件的id,在安装了Chrome List扩展之后,都可以通过工具菜单的
Explore Chrome...
,打开
browser/content/browser.xul
查到。

XUL


写XUL和写HTML差不多,只是元素的名字不一样,格式上更严格一些。Mozilla的所有程序都是使用XUL描述界面的。它不仅用于扩展,还可以直接用于编写应用程序。有兴趣的可以看这些网站:

用XUL编写的程序需要XULRunner的运行环境,这跟java差不多。从FF3开始,FF中已经自带XULRunner,可以直接用Firefox的程序文件执行XUL的程序。如下载这个 例子,解压到一个目录中,然后运行
firefox.exe -app application.ini
就能执行这个程序。

选项菜单


在XUL中可以使用dialog标签创建一个选项对话框,如例子中的options.xul文件。
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<dialog
id="contentfilter-configwindow"
title="Content Filter"
buttons="accept, cancel"
οnlοad="init();"
ondialogaccept="save();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
...
</dialog>

dialog的buttons属性定义了不同的按钮,onload属性定义了对话框在载入时执行的函数,ondialogaccept属性定义了点击accept按钮时执行的函数。因为是XML格式的文件,所以如果要直接在文件中写js的代码需要使用
<![CDATA[ ]]>
包含起来。options.xul中定义了一系列的js函数,这些都对应到一些点击或按键的动作。还可以使用js在操纵XML的DOM树,方法和HTML中是基本一致的。如:

<script type="application/x-javascript;version=1.7">
<![CDATA[
function deletePattern() {
var list = document.getElementById("patternList");
var delItems = list.selectedItems;
for(var i = delItems.length - 1; i >= 0; i--)
list.removeChild(delItems[i]);
}
]]>
</script>
<listbox width="480px" id="patternList" seltype="multiple" οnkeypress="pressDel(event);">

在listbox上按键会触发pressDel函数,这个函数使用对象的removeChild方法移除DOM树中的一个子节点。

扩展功能


对于一个实用的扩展来说,通常只有一个选项对话框是不够的,还需要注册一些事件监听函数,或者菜单项提供一个事务处理的入口。见overlay.js:

var contentFilter = {
init: function() {
var appcontent = document.getElementById("appcontent");
appcontent.addEventListener("DOMContentLoaded", contentFilter.filter, true);
contentFilter.patterns.loadAsRegex();
},

filter: function(aEvent) {
var doc = aEvent.originalTarget;
if (doc.nodeName != "#document") return;
var docElement = doc.documentElement;
var p = contentFilter.patterns.patternList;
for (var i = 0; i < p.url.length; i++) {
if(p.url[i].test(doc.location)) {
docElement.innerHTML = docElement.innerHTML.replace(p.find[i], p.replace[i]);
}
}
},
...
};

window.addEventListener("load", function() { contentFilter.init(); }, false);



最后一行定义了一个window.load的监听器,当打开FF的窗口时触发。init函数的定义了页面加载时的监听器并调用了loadAsRegex方法载入配置。


这里定义的contentFilter变量是处于当前窗口的命名空间下,并不是全局的变量,所以在选项对话框中并不能直接调用。因此在options.xul的save函数中
var win= Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator).getEnumerator("navigator:browser");
while(win.hasMoreElements()) {
var browser = win.getNext();
browser.contentFilter.patterns.loadAsRegex();
}

使用了getEnumerator得到所有的浏览器窗口,然后让每一个窗口中的contentFilter对象重新加载配置。

配置


扩展的默认配置参数可以在defaults/preferences目录下定义,如prefs.js:

pref("extensions.contentfilter.url", "baidu");
pref("extensions.contentfilter.find", "hao123");
pref("extensions.contentfilter.replace", "kaixin");

可以在about:config中查找扩展的配置参数。一般命名的方法是extensions.<扩展名>.<参数名>,当没有定义用户的值时,FF会读取这些默认的值。


在程序中可以使用
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefService);

得到配置树,然后在找到某个分支
var branch = prefs.getBranch("extensions.contentfilter.");

配置中有4个值的类型:布尔值,整值,字符串和复杂类型。取一个字符串值可以使用getCharPref,如:
branch.getCharPref("name");


这里就是取得extensions.contentfilter.name的值,要注意的是getBranch不会做解析,所以
prefs.getBranch("extensions.contentfilter.na").getCharPref("me")

也是取extensions.contentfilter.name的值,因此,一般在使用getBranch时,branch都是以.结尾的。

发布


讲整个扩展目录下的文件压缩为zip格式,install.rdf为第一层,不要包含扩展本身的目录,然后改后缀为xpi。将这个文件拖到FF中,就会弹出对话框询问是否要安装了。

总结


开发一个功能简单的扩展是很容易的。不过感觉官方的文档不够详细,或者组织的不是很好,想实现个功能很难找到应该怎么做,使用什么API,很多细节的地方不知道该怎么处理。可能要把所有文档浏览一遍才能比较顺手吧。新手可以多看看 Code Snippets或其他人的扩展。

参考链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值