上节我们做了一个hello world,然而并没有什么卵用。这节我们实现一个具有实用功能的扩展吧!
创建我们的扩展开发目录,其结构如下:
sessionstore
|——content
| |——overlay.js
| | ——overlay.xul
| | ——prefs.xul
|——defaults
| |——preferences
| |——sessionstore-prefs.js
|——chrome.manifest
|——install.rdf
各文件的实现如下:
install.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"> <!-- Unique ID for extension. Can be in e-mail address format or GUID format --> <em:id>[email protected]</em:id> <!-- Indicates that this add-on is an extension --> <em:type>2</em:type> <!-- Extension name displayed in Add-on Manager --> <em:name>Hello, World!</em:name> <!-- Extension version number. There is a version numbering scheme you must follow --> <em:version>0.1</em:version> <!-- Brief description displayed in Add-on Manager --> <em:description>My sessionstore extension.</em:description> <!-- Name of extension's primary developer. Change to your name --> <em:creator>Gomita</em:creator> <!-- Web page address through which extension is distributed --> <em:homepageURL>http://www.xuldev.org/helloworld/</em:homepageURL> <!-- This section gives details of the target application for the extension (in this case: Firefox 2) --> <em:targetApplication> <Description> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <em:minVersion>2.0</em:minVersion> <em:maxVersion>4.0.0.*</em:maxVersion> </Description> </em:targetApplication> <em:optionsURL>chrome://sessionstore/content/prefs.xul</em:optionsURL> </Description> </RDF>chrome.manifest
content sessionstore content/ overlay chrome://browser/content/browser.xul chrome://sessionstore/content/overlay.xuloverlay.xul
<?xml version="1.0"?> <overlay id="sessionstoreOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://sessionstore/content/overlay.js" /> <menupopup id="menu_ToolsPopup"> <menu label="Session Store" insertbefore="sanitizeSeparator"> <menupopup onpopupshowing="gSessionStore.createMenu(event);" oncommand="gSessionStore.restore(event);"> <menuitem label="Save Session" oncommand="gSessionStore.save(event);" /> <menuseparator /> <!-- Dynamically generated menu items go here --> <menuseparator /> <menuitem label="Clear Sessions" oncommand="gSessionStore.clear(event);" /> </menupopup> </menu> </menupopup> </overlay>overlay.js(这个代码看起来比较乱,实际上就是一个函数数组)
var gSessionStore = { // Directory to save sessions (nsILocalFile) _dir: null, // Initialization init: function() { var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties); this._dir = dirSvc.get("ProfD", Components.interfaces.nsILocalFile); this._dir.append("sessionstore"); if (!this._dir.exists()) this._dir.create(this._dir.DIRECTORY_TYPE, 0700); }, // uninitialization uninit: function() { this._dir = null; }, // Save session (event handler) save: function(event) { event.stopPropagation(); var ss = Components.classes["@mozilla.org/browser/sessionstore;1"] .getService(Components.interfaces.nsISessionStore); var state = ss.getBrowserState(); var fileName = "session_" + Date.now() + ".js"; var file = this._dir.clone(); file.append(fileName); this._writeFile(file, state); }, // Restore session (event handler) restore: function(event) { var fileName = event.target.getAttribute("fileName"); var file = this._dir.clone(); file.append(fileName); var state = this._readFile(file); var ss = Components.classes["@mozilla.org/browser/sessionstore;1"] .getService(Components.interfaces.nsISessionStore); ss.setWindowState(window, state, false); }, // Delete session (event handler) clear: function(event) { event.preventBubble(); var fileEnum = this._dir.directoryEntries; while (fileEnum.hasMoreElements()) { var file = fileEnum.getNext().QueryInterface(Components.interfaces.nsIFile); file.remove(false); // debug dump("SessionStore> clear: " + file.leafName + "\n"); } }, // Dynamically generate menu items (event handler) createMenu: function(event) { var menupopup = event.target; for (var i = menupopup.childNodes.length - 1; i >= 0; i--) { var node = menupopup.childNodes[i]; if (node.hasAttribute("fileName")) menupopup.removeChild(node); } var fileEnum = this._dir.directoryEntries; while (fileEnum.hasMoreElements()) { var file = fileEnum.getNext().QueryInterface(Components.interfaces.nsIFile); var re = new RegExp("^session_(\\d+)\.js$"); if (!re.test(file.leafName)) continue; var dateTime = new Date(parseInt(RegExp.$1, 10)).toLocaleString(); var menuitem = document.createElement("menuitem"); menuitem.setAttribute("label", dateTime); menuitem.setAttribute("fileName", file.leafName); menupopup.insertBefore(menuitem, menupopup.firstChild.nextSibling.nextSibling); } }, // Read file _readFile: function(aFile) { try { var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]. createInstance(Components.interfaces.nsIFileInputStream); stream.init(aFile, 0x01, 0, 0); var cvstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. createInstance(Components.interfaces.nsIConverterInputStream); cvstream.init(stream, "UTF-8", 1024, Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); var content = ""; var data = {}; while (cvstream.readString(4096, data)) { content += data.value; } cvstream.close(); return content.replace(/\r\n?/g, "\n"); } catch (ex) { } return null; }, // Write file _writeFile: function(aFile, aData) { // init stream var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Components.interfaces.nsIFileOutputStream); stream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0); // convert to UTF-8 var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. createInstance(Components.interfaces.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; var convertedData = converter.ConvertFromUnicode(aData); convertedData += converter.Finish(); // write and close stream stream.write(convertedData, convertedData.length); if (stream instanceof Components.interfaces.nsISafeOutputStream) { stream.finish(); } else { stream.close(); } }, }; window.addEventListener("load", function(){ gSessionStore.init(); }, false); window.addEventListener("unload", function(){ gSessionStore.uninit(); }, false);prefs.xul
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <prefwindow id="sessionstorePrefs" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="Session Store Preferences"> <prefpane> <preferences> <preference id="extensions.sessionstore.warnOnClear" name="extensions.sessionstore.warnOnClear" type="bool" /> <preference id="extensions.sessionstore.replaceTabs" name="extensions.sessionstore.replaceTabs" type="int" /> </preferences> <checkbox label="Confirm before clearing sessions" preference="extensions.sessionstore.warnOnClear" /> <groupbox> <caption label="When restoring session:" /> <radiogroup preference="extensions.sessionstore.replaceTabs"> <radio value="0" label="Keep current tabs" /> <radio value="1" label="Replace current tabs"/> <radio value="2" label="Ask me every time" /> </radiogroup> </groupbox> </prefpane> </prefwindow>sessionstore-prefs.js
pref("extensions.sessionstore.warnOnClear", true); pref("extensions.sessionstore.replaceTabs", 2);