Mozilla 的 XUL 用戶界面語言(XML User Interface Language,XUL)是一種可用於開發獨立應用程序和浏覽器擴展的通用語言。在 本系列第 1 部分 中,我展示了 XUL 擴展的基本構建塊,本文則介紹如何將這些構建塊組裝為一個跨平台的 Firefox 擴展。由於對浏覽器中的 XUL 文件存在較高的安全要求,第 1 部分中的代碼功能非常有限,但是擴展則沒有這麼多限制,因此我還將在本文中提供一些代碼中缺失的功能。
基本的擴展結構
第一步是使用前一篇文章中生成的 XUL 和支持文件構建擴展。如果您已安裝了其他 Firefox 加載項,您可能注意到它們隨附在文件擴展名為 .xpi 的包裡,它表示 XUL 的跨平台安裝包(XPI)格式(讀作 “zippy”)。XPI 定義了一些包,這些包均為 .zip 文件,並且都具有特定的布局和一些清單(manifest)文件。這類似於 Java 的 JAR 格式,後者也在 .zip 內容中包含了清單文件。清單 1 顯示了 .zip 文件目錄,用於放置我在上一篇文章中使用 XUL、JavaScript 和 CSS 生成的擴展。
清單 1. 用於 Firefox 擴展 XPI 包的 .zip 文件目錄
Archive: /Users/uche/stats.xpi
Length Method Size Ratio Date Time CRC-32 Name
-------- ------ ------- ----- ---- ---- ------ ----
0 Stored 0 0% 10-08-07 23:10 00000000 Chrome/
0 Stored 0 0% 10-08-07 23:10 00000000 Chrome/content/
271 Defl:N 184 32% 10-09-07 00:13 44bb6d65 Chrome/content/overlay.JS
464 Defl:N 284 39% 10-09-07 00:13 1d873c24 Chrome/content/overlay.xul
909 Defl:N 366 60% 10-09-07 00:13 67869f05 Chrome/content/stats.JS
1581 Defl:N 544 66% 10-09-07 00:13 49def584 Chrome/content/stats.xul
0 Stored 0 0% 10-08-07 23:10 00000000 Chrome/skin/
230 Defl:N 147 36% 10-09-07 00:13 a732aecb Chrome/skin/stats.CSS
181 Defl:N 105 42% 10-09-07 00:13 f7317c0a Chrome.manifest
0 Stored 0 0% 10-08-07 23:10 00000000 defaults/
0 Stored 0 0% 10-08-07 23:10 00000000 defaults/preferences/
948 Defl:N 440 54% 10-09-07 00:13 50ee7971 install.rdf
0 Stored 0 0% 10-08-07 23:10 00000000 locale/
0 Stored 0 0% 10-08-07 23:10 00000000 locale/en-US/
43 Stored 43 0% 10-09-07 00:13 d9c38400 locale/en-US/overlay.dtd
-------- ------- --- -------
4627 2113 54% 15 files
請仔細檢查文件布局,因為開發擴展過程中最大的難題可能就源於這裡。您可能會注意到,有幾個新文件並沒有出現在上一篇文章裡,我將在下一小節介紹這些文件。
XUL 覆蓋
在上一篇文章中,新的 XUL 窗口是完全獨立的,但是現在它被綁定為一個擴展,必須以某種方式從 Firefox 中調用。這通常要對浏覽器用戶界面進行調整,以為用戶提供調用擴展的方法。XUL 通過提供稱為覆蓋(overlay)的方式可以輕松地對用戶界面進行擴展。清單 2(overlay.xul)顯示了一個覆蓋,它添加了一個菜單項來啟動 stats 查看器窗口。
清單 2 (overlay.xul). XUL 覆蓋添加了一個菜單項
<?XML version="1.0"?>
<?XML-stylesheet href="Chrome://stats/skin/overlay.css" type="text/CSS"?>
<!DOCTYPE overlay SYSTEM "Chrome://stats/locale/overlay.dtd">
<overlay XMLns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="stats-overlay">
<script src="overlay.JS"/>
<menupopup id="menu_ToolsPopup">
<menuitem id="stats" label="&statsmenuitemname;"
oncommand="Stats.onMenuItemCommand(event);"/>
</menupopup>
</overlay>
我將在稍後一節中進一步討論 chrome URL。請注意文檔類型聲明,它用於加載一個 DTD,其中包含所有應進行本地化的字符串(基本上包括呈現給用戶的所有自然語言字符串)。下一節將更詳細介紹這種在 Mozilla Chrome 應用程序中實現本地化(l10n)的好方法。在覆蓋 XUL 內部,我指定了一個新增的菜單項。outer 元素根據 ID 指定 Firefox 中的現有目標菜單項 — menu_ToolsPopup 是 “Tools” 菜單。新的菜單項在默認情況下作為最後一個菜單項添加。新的菜單項 stats 以 DTD 實體的形式提供,對用戶看到的標簽使用一個經過本地化的字符串。oncommand 屬性非常關鍵,因為它可以確定當用戶選擇菜單項時發生什麼操作。指定的 JavaScript 函數通過獨立的腳本文件 overlay.JS 提供,如清單 3 所示。
清單 3 (overlay.JS). 腳本為所添加的菜單項提供操作
var Stats = {
onLoad: function() {
//You can place set-up code for the extension here
},
onMenuItemCommand: function() {
window.open("chrome://stats/content/stats.xul", "", "Chrome, resizable=yes");
}
};
window.addEventListener("load", function(e) { Stats.onLoad(e); }, false);
腳本的主要內容是定義了一個單獨的結構 Stats,使用它封裝所有函數。您還需要將所有全局變量放入該結構中。這將減少擴展的內存占用,因為它在全局名稱空間(outer 結構本身的名稱)中只會占用一個名稱,並且對於所有表現良好的擴展來說,這是一項標准行為,可以避免與其他擴展甚至是內容腳本發生對象名沖突。您已經了解了如何使用 JavaScript 代碼片段 Stats.onMenuItemCommand(event); 訪問清單 2 中的封裝對象,這段代碼將訪問清單 3 中的第二個結構項。為保持完整性,我包括了一個 Stats.onLoad 函數,但是擴展並沒有實際使用到它。您將使用它運行任何擴展初始化代碼。腳本的最後一行對 Stats.onLoad 函數進行了設置,以便在 chrome 完成加載後立即運行該函數。Stats.onMenuItemCommand 函數使用上一篇文章中的 XUL 示例 applet 啟動窗口。它的第二個屬性是我沒有使用到的窗口 ID。第三個屬性為標志列表 chrome,表示新窗口具有自己的 Chrome,並且不應該在浏覽器的 Chrome 內打開(因此,新窗口不能繼承 Firefox 的菜單、工具欄、狀態條、選項卡等等)。resizable=yes 為用戶重新調整窗口大小提供控件。
Chrome 資源
您現在已經了解到,圍繞該擴展新增了很多文件,並且 XPI 的一個重要方面就是在包內查找文件。這時就需要使用到清單文件 Chrome.manifest,如清單 4 所示。
清單 4. 針對 Stats 擴展的 Chrome 清單文件
content stats Chrome/content/
overlay chrome://browser/content/browser.xul Chrome://stats/content/overlay.xul
locale stats en-US locale/en-US/
skin stats classic/1.0 Chrome/skin/
第一行確定主要內容的位置,例如 XUL 文件和腳本。它使用 stats 別名指代包的 “chrome/content/” 文件夾,因此,舉例來說,如果使用的 chrome URL 為 chrome://stats/content/stats.xul,則將通過映射 stats 別名對 URL 進行解析並在 “chrome/content/” 文件夾查找 “stats.xul” 文件。第二行使用 overlay.xul(清單 2)對主浏覽器 Chrome 建立一個覆蓋。第三行確定包含自然語言字符串的 DTD 的位置,為本地化(l10n)提供支持。示例中僅包含這些字符串的英文版本,但是您可以將這些字符串翻譯為法文然後放到 fr-FR 文件夾中,或者翻譯為德文放到 de-DE 文件夾中,等等。浏覽器將自動使用和用戶設置的首選語言相對應的語言。最後一行為 CSS 文件建立了一個位置,這些文件將與標准浏覽器 Chrome 結合在一起。
安裝程序設置
XPI 的另一個關鍵信息文件是 install.rdf,如清單 5 所示,其中包含了擴展安裝說明。
清單 5 (install.rdf). Stats 擴展的安裝參數
<?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">
<!-- Required Items -->
<em:id>xulapp@example.com</em:id>
<em:name>Stats vIEwer</em:name>
<em:version>1.0</em:version>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>1.5</em:minVersion>
<em:maxVersion>2.0.0.*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Optional Items -->
<em:creator>Uche Ogbuji</em:creator>
<em:description>
Opens a window in which you can vIEw simple stats about Web pages
</em:description>
<em:homepageURL>http://www.ibm.com/developerworks/web/</em:homepageURL>
</Description>
</RDF>
大部分內容的含義不言自明,因此就不多做說明,但是需要提到的一點是,我使用 em:targetApplication 元素指定所有的 Firefox 版本大於 1.5 而小於 2.0.1。較長的 em:id 字符串是 Firefox 自身的應用程序全球惟一標識符(UUID)。
您應該能夠將上一篇文章中的示例文件放入清單 1 中指出的位置,惟一所做的修改是將 “stats.xul” 中的 CSS 鏈接(用於假定 “stats.css” 位於同一個目錄中)改為使用 chrome 鏈接 Chrome://stats/skin/stats.CSS。
完善擴展的功能
由於安全限制,我不得不去掉了 stats.js XUL 文件中的一些功能。現在我已經將它打包為一個擴展,可以補充一些缺失的內容。清單 6 展示了更新後的 stats.JS,它實際執行了擴展所期望的計數操作。
清單 6. Stats 查看器窗口的完整 JavaScript 代碼
//Invoked in response to a click on the "Go!" button
function change_url(event)
{
//Variables for convenIEnt Access to specific elements in the XUL
var urlbox = document.getElementById("url");
var contentview = document.getElementById("contentvIEw");
var wordcountbox = document.getElementById("Wordcount");
var charcountbox = document.getElementById("charcount");
var elemcountbox = document.getElementById("elemcount");
alert(urlbox.value);
contentvIEw.contentDocument.location.href = urlbox.value;
//Use Firefox XPath to get the raw text of the document
var doctext = contentvIEw.contentDocument.evaluate(
"string(.)", document, null, XPathResult.STRING_TYPE, null).stringValue;
var Wordcount = doctext.split(" ").length()
alert(Wordcount);
wordcountbox.value = String(Wordcount);
charcountbox.value = String(doctext.length());
//Use Firefox XPath to count elements in the document
var elemcount = contentvIEw.contentDocument.evaluate(
"count(//*)", document, null, XPathResult.NUMBER_TYPE, null).numberValue;
elemcountbox.value = String(elemcount);
}
這一次,我對用戶加載到 contentvIEw 的 Web 頁面使用了兩個 XPath 表達式。通過第一個表達式 string(.),我獲得了字符計數(計算字符串長度)和單詞數(使用空格將字符串斷開並計算結果)。通過第二個表達式 //*,我獲得了元素的總數。隨後我將使用這些值更新相應的文本框。
結束語
創建 Firefox 擴展是項復雜的工作,但幸運的是,社區提供了大量擴展,可從多方面提供支持。側欄 列出了其中的一些。在本系列的兩篇文章中,您已了解到只要稍加處理,您也可以開發 Firefox 擴展,並且您還可以使用這些知識輕松地開發完整應用程序的主要部分。XUL、JavaScript 和 Mozilla 平台強強結合的威力讓人印象深刻。除標准的 Web 浏覽器以外,您還可以將更多的平台作為目標。通常,對諸如 Songbird 音樂管理應用程序和 Flock 社會 Web 浏覽器這樣的 Mazilla 應用程序(通常都基於 XUL )采用 Firefox 擴展並不困難。