快速开发新浪微博的firefox插件(上)
在开发这个插件之前,自己对javascript的使用也就是web page中简单的操作dom,而对于firefox插件开发一无所知,OAuth连听都没有听过。所以对于我眼前要干得事情我有两个难点,第一就是firefox的插件机制,第二就是了解OAuth。
firefox的插件机制
对于一个firefox插件来说,我们首先需要了解的它的组织结构。打开一个firefox插件工程,你一般会看这么几个元素:chrome文件夹,defaults文件夹,chrome.manifest, install.rdf。
我们先从install.rdf说起,相比从文件名你就明白了这个文件是要干什么的。没错,这个文件就是firefox插件的安装文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | < rdf xmlns:em="http://www.mozilla.org/2004/em-rdf#"> < description about="urn:mozilla:install-manifest"> < em:id >flashcard@gmail.com</ em:id > < em:version >0.7</ em:version > < em:type >2</ em:type > < em:name >flashcard</ em:name > < em:description >Post the selected word to Sina weibo.</ em:description > < em:homepageurl >https://feihe.cnblogs.com</ em:homepageurl > < em:iconurl >chrome://flashcard/skin/icon.png</ em:iconurl > < em:creator >Fei He</ em:creator > < em:targetapplication > < description > < em:id >{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</ em:id > < em:minversion >3.0</ em:minversion > < em:maxversion >4.0.*</ em:maxversion > </ description > </ em:targetapplication > </ description > </ rdf > |
里面也就是对于你的firefox插件的一些描述信息,其中比较关键的两个<em:id>, 在根节点description下的<em:id>是你的firefox插件的id,也就是说这个东西必须唯一(至少在你的firefox所有插件中唯一),另一个位于<em:targetApplication>的<Description>下的<em:id>则是firefox的id,这个是不能修改的。再就是<em:minVersion>和<em:maxVersion>,它们用来描述你的firefox插件对于firefox版本的兼容性。
接着我们来了解defaults,一句话defaults就是你的firefox插件的preferences的default设置。
对于一个firefox插件最核心的部分就是chrome.manifest和chrome文件夹。chrome.manifest有点像.NET的project文件,基本上就是对于整个firefox插件所有元素的位置信息,而firefox本身就是通过这个这个manifest来定位具体的元素。那么一个firefox插件会包含那些元素呢?打开chrome.manifest你就一目了然了。
overlay chrome://browser/content/browser.xul chrome://flashcard/content/overlay.xul content flashcard chrome/content/flashcard/ skin flashcard classic chrome/skin/classic/flashcard/ locale flashcard en-US chrome/locale/flashcard/en-US/ style chrome://global/content/customizeToolbar.xul chrome://flashcard/skin/skin.css
这里面的结构基本都是行结构的,每一个行的头就是具体的firefox插件元素名称,而后面的是告诉firefox去那个位置查找这个元素。而这其中包含了这么几个元素:
- overlay: 指向你的firefox插件的一个UI元素,包括contextmenu,toolbar,navigator bar之类。在上面的manifest中你看我这里指向了一个后缀名是xul的文件,其实它的全称是Xml User Interface。顾名思义就是使用xml的格式来描述UI。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | < overlay id="flashcardOverlay"> < menupopup id="contentAreaContextMenu"> < menuseparator id="separator_flashcard"> < menuitem image="chrome://flashcard/skin/word.png" class="menuitem-iconic" id="menuitem_flashcard_add"> </ menuitem ></ menuseparator ></ menupopup > </ overlay > |
上面是我用的一个overlay.xul, 我这里是给firefox的contextmenu加了一个新的menuitem,并使用separator和原有的menuitems分隔起来。这里的文件名是可以随意取的,那是你起的名字必须在chrome.manifest中应用。到这里很多人好奇,那么你加的menuitem相应的行为在那里呢?细心的你也许发现我这个xul中引用了一些javascript,而对于我们menuitem,firefox提供了2中方法去关联行为:第一种就是在control的oncommand中直接指定control的行为;第二中是javascript中使用document.getElementByID来获取control从而绑定行为:
1 | < dialog buttons="accept,cancel" windowtype="DialogWindowType" persist="screenX screenY width height" title="FlashCard" id="dialog_login"> </ dialog > |
1 | document.getElementById( "menuitem_flashcard_add" ).addEventListener( "click" , function (event){}, false ); |
- content: 就是你的firefox插件的核心,包括javascript脚本和XUL
- skin:即使皮肤,你可以在给你的插件做不同的皮肤,我的mainifest中指定了我使用classic的皮肤,所以我在skin文件夹下就应classic的文件夹来对应。
- locale:国际化,对于我们firefox插件中需要需要国家化的UI control, 我们可以使用它的label属性,同时在chrome.manifest中指定的culture文件夹下定义dtd文件来对应,例如:
overlay.xul中的control定义:
12
<
menuitem
image="chrome://flashcard/skin/word.png" class="menuitem-iconic" label="&flashcard.menuitem.label;" id="menuitem_flashcard_add">
</
menuitem
>
dtd文件的定义:它们之间使用control的label属性关联,而overlay.xul具体和chrome.manifest指定culture文件夹下的那个dtd文件关联,你可以看到在我的overlay.xul中有这么一句定义:1 - style: 也就是overlay的一些样式,比如css。
你的firefox插件有了UI,也有了相应的行为和样式,那么你还需要什么呢?需要存储,也就是你需要存储一些preferences信息或者其他的比如我这里我需要存储新浪微博中用户授权通过之后获得的Oauth_token的相关信息。firefox对于存储提供了很多方式,你可文件存贮在特殊位置,或者使用sqlite这种肖的文件数据库。另外一种最简单的也就是我采用的就是preferences的存贮。对于firefox插件,你可以在你的install.rdf中注明你的preference文件,这样你可以让用户使用的preference文件做一些设置,并保存。而我这里我指希望使用preferences来存贮,所以我并不希望用户看到它,那么我就不需要在install.rdf中注明。
preference文件也是一个xul文件,所以你也可以应用javascript和css,来对于你的preference中的control进行行为的绑定和样式的渲染。我这里的preference如下:
1 2 3 4 5 6 7 | < prefwindow > < prefpane label="Flash Card Preferences"> < preferences > < preference type="string" name="Sina.WeiBo.oauth.access_token" id="pref_access_token"> < preference type="string" name="Sina.WeiBo.oauth.access_token_secret" id="pref_access_token_secret"> </ preference ></ preference ></ preferences > </ prefpane ></ prefwindow > |
1 | Components.classes[ "@mozilla.org/preferences-service;1" ].getService(Components.interfaces.nsIPrefService).getBranch( "Sina.WeiBo." ) |
来获得所有name前缀为Sian.Weibo的preference,然后调用它的getCharPref('oauth.access_token')来获取值,或者通过setCharPref('oauth.access_token')设置值,对于preference,MDN上有详细的API介绍。对于在preference中引用javascript比较tricky的一点就是如果在你preference中使用prePanel,那么javascript的引用代码一定要在prePanel后面,否则你的prePanel就什么都看不到了。
最后一点,有些时候也许你希望你的firefox插件在完成某些行为之后给用户一个notification,在firefox3中你可以使用普通的notification或者alert,而在firefox4中你可以使用popupNotification,效果非常炫,而且还可以指定图片和相应的action,使得用户在得到这个notification之后可以做进一步的行为。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | PopupNotifications.show(gBrowser.selectedBrowser, "flashcard-add" , '"' + selectedWord + '" 已经成功加入你的单词本' , null , { label: "确定" , accessKey: "D" , callback: function () { } }, [ { label: "Reset" , accessKey: "R" , callback: function () { Browser.Preferences.clearUserPref( "oauth.access_token" ); Browser.Preferences.clearUserPref( "oauth.access_token_secret" ); } }, ]); |
1 2 3 | .popup-notification- icon [popupid= "flashcard-add" ] { list-style-image : url ( "chrome://flashcard/skin/icon.png" ); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?