https://addons.mozilla.org/en-US/developers/docs/reference
首先给出firefox插件开发的官方文档列表,如果没有列表,那么开发firefox插件几乎不可能,光看中文资料也是会死的说,从老大给了任务,到基本完成任务,中间花了一周时间,这一周是痛苦而又极有成就感的一周,我的插件其实功能还算简单,就是在界面上添加一个按钮,点击按钮打开我们的主页,右键菜单上在加上一个菜单项,点击可以把当前页面存到我们的server上(HTTP接口)
开发环境:ubuntu10.10 firefox 7.0
基础知识:
firefox插件是一个xpi文件,例如firefox-thinkernote.xpi,安装方式有两种,前者直接拖拽到地址栏,就会提示你安装,再者选择菜单:工具/附件管理 打开附加组件管理窗口,窗口右上侧的搜索栏前有一个扳手的图标,点击选择从文件安装附加组件,就会打开一个选择窗口,找到咱的xpi所在目录,点击即可,也会提示咱们安装
xpi文件其实就是一个zip文件,把我们的开发目录压缩成一个zip压缩包,再重命名就是我们的安装插件了,而我们的插件开发目录其实是有要求的:
firefox-thinkernote/
chrome/
overlay.xul
overlay.js
locale/
en-US/
overlay.dtd
zh-CN/
overlay.dtd
skin/
classic/
icons/
overlay.css
install.rdf
chrome.manifest
顶层目录没有要求,叫什么都成,我叫这个名字是表示这个插件是我们项目的firefox插件,不过底下的结构就有要求了,install.rdf是一个XML文件,描述的插件的开发者信息,谁开发的插件,插件版本,不过需要注意的是:targetAppliction/Description/em:id必须是{ec8030f7-c20a-464f-9b0e-13a3a9e97384}这个表示,该插件是为firefox浏览器开发。另一个em:id 则用于描述插件ID,所以该ID也不能重复,你想很多插件装在一个浏览器上,浏览器怎么区分,必须有一个不重复的ID,因此这个ID可以取邮件地址,因为邮件是全世界唯一的,或者取GUID,这个我不懂,所以我取邮件地址作为插件ID
<?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"> <!-- 指出扩展的ID。该ID应该是一个Email格式的字符串,或者是一个GUID。注意,使用Email的作用是保证该ID的唯一性,并不要求这个Email地址是有效的。必填。 --> <em:id>[email protected]</em:id> <!-- 指出插件的版本号。必填。 --> <em:version>1.0</em:version> <!-- 在这里的值必须是2。该标签说明该应用的Firefox扩展,而不是其他XUL应用程序。Firefox插件的代码是2,如果是Firefox主题则会是4。必填。 --> <em:type>2</em:type> <!-- 安装扩展的目标应用程序以及最高版本和最低版本。必填。 --> <em:targetApplication> <Description> <!-- Firefox的ID,这个值不能改变。必填。 --> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- 指明能使用该扩展的Firefox最低版本。这里是指1.5。必填。 --> <em:minVersion>1.5</em:minVersion> <!-- 指明能使用该扩展的Firefox最高版本。这里是指3.0.x。结合minVersion,即是说本扩展只适用于1.5和3.0.x之间的版本。必填。 --> <em:maxVersion>10.0</em:maxVersion> </Description> </em:targetApplication> <!-- 扩展的元数据。 --> <!-- 扩展名字。必填。 --> <em:name>ThinkerNote</em:name> <!-- 扩展的描述。这里的描述将出现在Firefox的工具-附加组件的描述栏。选填。 --> <em:description>thinkernote 行客记事</em:description> <!-- 扩展作者。选填。 --> <em:creator>thinkernote.com</em:creator> <!-- 扩展主页的URL。选填。 --> <em:homepageURL>http://www.thinkernote.com/</em:homepageURL> </Description> </RDF>
chrome.manifest 定义开发包名称和资源位置的,其语法是
directoryName packageName positionInDirectoryOfPackage
content thinkernote chrome/content/ locale thinkernote en-US chrome/locale/en-US/ skin thinkernote classic/1.0 chrome/skin/classic/ overlay chrome://browser/content/browser.xul chrome://thinkernote/content/overlay.xul style chrome://global/content/customizeToolbar.xul chrome://thinkernote/skin/overlay.css
overlay chrome://browser/content/browser.xul chrome://thinkernote/content/overlay.xul
这一行是说把我们的xul文件追加到系统的xul里去,浏览器在启动的时候会启动browser.xul也会顺道启动我们的overlay.xul,这样我们的插件就和浏览器一起启动,并能做一些事情了,注意我们定义了content目录以后,就可以通过chrome://packageName/directoryName/fileName来引用文件了,例如overlay.xul其实是在 firefox-thinkernote/chrome/content/下面
xul文件也是一个XML文件,它描述的是我们的插件启动以后,界面是什么样的,不过大多数的插件没有界面只是在工具栏增加了按钮,右键菜单增加了菜单项
<?xml version="1.0"?> <?xml-stylesheet href="chrome://thinkernote/skin/overlay.css" type="text/css"?> <!DOCTYPE overlay SYSTEM "chrome://thinkernote/locale/overlay.dtd"> <overlay id="thinkernoteOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/x-javascript" src="chrome://thinkernote/content/jquery.min.js"/> <script type="application/x-javascript" src="chrome://thinkernote/content/common.js"/> <script type="application/x-javascript" src="chrome://thinkernote/content/common_net.js"/> <script type="application/x-javascript" src="chrome://thinkernote/content/overlay.js"/> <script type="application/x-javascript" src="chrome://thinkernote/content/domtohtml.js"/> <script type="application/x-javascript" src="chrome://thinkernote/content/action.js"/> <menupopup id="menu_ToolsPopup"> <menuitem id="thinkernote-menubutton" insertafter="devToolsSeparator" label="&thinkernote.label;" oncommand="gotoHomePage()"/> </menupopup> <popup id="contentAreaContextMenu"> <menuitem id="thinkernote-context" image="chrome://thinkernote/skin/icons/16x16.png" label="&thinkernote.desc;" insertafter="devToolsSeparator" class="menuitem-iconic" oncommand="doAction()"/> </popup> <toolbarpalette id="BrowserToolbarPalette"> <toolbarbutton id="thinkernote-toolbarbutton" class="toolbarbutton-1 chromeclass-toolbar-additional" oncommand="gotoHomePage()" label="&thinkernote.label;" tooltiptext="&thinkernote.label;" /> </toolbarpalette> </overlay>
我们这个XUL文件是在chrome.manifest文件里指定的,让系统启动时加载的XUL文件,这里定义了菜单项,按钮,和右键菜单,他们的oncommnd事件和HTML里的onclick没有区别,意思就是说当它们被点击时取执行指定的函数。这些函数被定义在JS文件里,所以XUL文件的开头部分就定义了script引用(注意他们的src)OK,能引用JS就能引用CSS,CSS进来是干嘛的呢,他们来描述我们定义的按钮,例如按钮需要一张图片,他们就来告诉浏览器图片在哪里,那么怎么告诉呢?用按钮的ID,在CSS文件里定义一个和按钮ID名相同的CSS样式定义,系统会自己查找匹配
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); #thinkernote-toolbarbutton { list-style-image: url("chrome://thinkernote/skin/icons/24x24.png"); } toolbar[iconsize="small"] #thinkernote-toolbarbutton { list-style-image: url("chrome://thinkernote/skin/icons/19x19.png"); } #saveFormWindow{ background-color:white; }
OK,说了JS,CSS是干嘛的,那么DTD又是干嘛的呢,DTD其实是为国际化开发做准备,我们把各种语言的字符串放到不同的DTD文件里,定义如下,中间的部分是ENTITY-NAME,名字的thinkernote没有特殊的意思,就已名字的一部分而已,使用的时候就可以用"& ENTITY-NAME" 来引用
<!ENTITY thinkernote.label "行客记事"> <!ENTITY thinkernote.desc "保存到行客记事"> <!ENTITY title "保存到行客记事">
特别要说明一点的是,即便是在CSS样式里定义了按钮图片,DTD文件里定义了按钮文字,按钮还是不会出来,orz 因为我们还少了一步,用代码把按钮设置到工具栏上去
function installButton(toolbarId, id, afterId) { if (!document.getElementById(id)) { var toolbar = document.getElementById(toolbarId); var before = toolbar.firstChild; if (afterId) { let elem = before = document.getElementById(afterId); if (elem && elem.parentNode == toolbar) before = elem.nextElementSibling; } toolbar.insertItem(id, before); toolbar.setAttribute("currentset", toolbar.currentSet); document.persist(toolbar.id, "currentset"); } } function firstRun(extensions){ let extension = extensions.get("[email protected]"); var first = extension.firstRun; if(first){ installButton("nav-bar", "thinkernote-toolbarbutton", "urlbar-container"); }//第一次安装运行时first才等于true } var Application = Components.classes["@mozilla.org/fuel/application;1"].getService(Components.interfaces.fuelIApplication); if (Application.extensions) firstRun(extensions); else Application.getExtensions(firstRun);
JS的代码编写
在编写页面的时候,我们会使用DOM对象来访问页面上的元素,例如document,window对象,因此如果要没这些可以直接拿来用的顶层全局对象整个就没法玩,因此我们要找在这个控件里可以直接拿来用的对象,我发现有两个对象gBrowser对象和document对象,gBrowser表示浏览器,document表示装载的页面,不过假设用户通过地址栏打开了baidu.com我们想访问这个页面还不能直接使用document对象,而是要这么获得
function getDocument() { return document.getElementById('content').selectedBrowser.contentDocument; } function gotoHomePage() { pageDocument = getDocument(); pageDocument.location = serverUri; } function gotoLogin() { gBrowser.selectedTab = gBrowser.addTab(serverUri + "/login"); }
firefox插件资料比较少,baidu文档里有一些,如果您在看本文可以去看看,最重要的还是在于看官方的文档!
附免打包的开发环境配置
邮件地址作为ID:[email protected] 在firefox的插件目录下建立一个和邮件名同名的文件
$ cd .mozilla/firefox/g911mow7.dev/extensions
$ touch [email protected]
$ vi [email protected]
写入代码所在目录:/home/shujunli/firefox-thinkernote/
补充信息:
上次配置开发环境时查看的资料说需要建立一个dev的环境,然后找dev的extensions目录,其实这不是必需的的,实际上firefox在主目录生成了一个隐藏文件夹.mozilla 并且这个隐藏文件夹下有一个目录是用来放置安装插件的,在我的系统里这个目录是 ~/.mozilla/firefox/i7lsshse.default/extensions 在这个目录里能看见浏览器已经安装了的插件源码(每个源码是一个文件夹或者是email或者是uuid)按照上面描述的免打包开发环境的配置,在这个目录下放一个文本文件