From 5f42595f651cd12ae34bf66fab5dc3f8577c27a1 Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Sun, 21 Aug 2011 10:41:46 -0400 Subject: [PATCH] Fix some cleanup issues. Better error pages for bad dactyl: URLs. Remove some dead code. Other cleanup. --- common/bootstrap.js | 22 +++--- common/components/commandline-handler.js | 2 +- common/content/dactyl.js | 4 ++ common/content/statusline.js | 1 - common/modules/base.jsm | 15 ++++- common/modules/bootstrap.jsm | 7 +- common/modules/config.jsm | 9 +-- common/modules/prefs.jsm | 4 +- common/modules/protocol.jsm | 74 +++++++++++---------- common/modules/styles.jsm | 14 ++-- common/modules/util.jsm | 6 +- common/tests/functional/testHelpCommands.js | 17 +++-- pentadactyl/content/config.js | 10 +-- 13 files changed, 99 insertions(+), 86 deletions(-) diff --git a/common/bootstrap.js b/common/bootstrap.js index bf3f0d24..28cc607d 100755 --- a/common/bootstrap.js +++ b/common/bootstrap.js @@ -32,16 +32,8 @@ const manager = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); const BOOTSTRAP_JSM = "resource://dactyl/bootstrap.jsm"; const BOOTSTRAP_CONTRACT = "@dactyl.googlecode.com/base/bootstrap"; -JSMLoader = JSMLoader || BOOTSTRAP_CONTRACT in Cc && Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader; -var JSMLoader = BOOTSTRAP_CONTRACT in Components.classes && - Components.classes[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader; - -// Temporary migration code. -if (!JSMLoader && "@mozilla.org/fuel/application;1" in Components.classes) - JSMLoader = Components.classes["@mozilla.org/fuel/application;1"] - .getService(Components.interfaces.extIApplication) - .storage.get("dactyl.JSMLoader", null); +var JSMLoader = BOOTSTRAP_CONTRACT in Cc && Cc[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader; function reportError(e) { dump("\ndactyl: bootstrap: " + e + "\n" + (e.stack || Error().stack) + "\n"); @@ -119,7 +111,8 @@ function startup(data, reason) { }; else getURI = function getURI(path) - Services.io.newURI("jar:" + Services.io.newFileURI(basePath).spec + "!/" + path, null, null); + Services.io.newURI("jar:" + Services.io.newFileURI(basePath).spec.replace(/!/g, "%21") + "!" + + "/" + path, null, null); try { init(); @@ -243,7 +236,7 @@ function init() { if (!(BOOTSTRAP_CONTRACT in Cc)) manager.registerFactory(Components.ID("{f541c8b0-fe26-4621-a30b-e77d21721fb5}"), - String("{f541c8b0-fe26-4621-a30b-e77d21721fb5}"), + "{f541c8b0-fe26-4621-a30b-e77d21721fb5}", BOOTSTRAP_CONTRACT, { QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]), instance: { @@ -292,6 +285,12 @@ function shutdown(data, reason) { } } +function uninstall(data, reason) { + dump("dactyl: bootstrap: uninstall " + reasonToString(reason) + "\n"); + if (reason == ADDON_UNINSTALL) + Services.prefs.deleteBranch("extensions.dactyl."); +} + function reasonToString(reason) { for each (let name in ["disable", "downgrade", "enable", "install", "shutdown", "startup", @@ -302,6 +301,5 @@ function reasonToString(reason) { } function install(data, reason) { dump("dactyl: bootstrap: install " + reasonToString(reason) + "\n"); } -function uninstall(data, reason) { dump("dactyl: bootstrap: uninstall " + reasonToString(reason) + "\n"); } // vim: set fdm=marker sw=4 ts=4 et: diff --git a/common/components/commandline-handler.js b/common/components/commandline-handler.js index 2bd10f9b..ee02b04a 100644 --- a/common/components/commandline-handler.js +++ b/common/components/commandline-handler.js @@ -19,7 +19,7 @@ function init() { Cu.import("resource://dactyl/bootstrap.jsm"); if (!JSMLoader.initialized) JSMLoader.init(); - Cu.import("resource://dactyl/base.jsm"); + JSMLoader.load("base.jsm", global); require(global, "config"); require(global, "util"); } diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 1e453fea..168173ae 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -56,7 +56,11 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { delete window.dactyl; delete window.liberator; + styles.system.add("cleanup-sheet", config.styleableChrome, ); styles.unregisterSheet("resource://dactyl-skin/dactyl.css"); + DOM('#TabsToolbar tab', document).style.display; }, destroy: function () { diff --git a/common/content/statusline.js b/common/content/statusline.js index ba290b72..a4932ac6 100644 --- a/common/content/statusline.js +++ b/common/content/statusline.js @@ -12,7 +12,6 @@ var StatusLine = Module("statusline", { init: function init() { this._statusLine = document.getElementById("status-bar"); this.statusBar = document.getElementById("addon-bar") || this._statusLine; - this.statusBar.collapsed = true; this.baseGroup = this.statusBar == this._statusLine ? "StatusLine " : ""; if (this.statusBar.localName == "toolbar") { diff --git a/common/modules/base.jsm b/common/modules/base.jsm index e5b2d4fc..adcde383 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -851,7 +851,7 @@ Class.extend = function extend(subclass, superclass, overrides) { * property's value. * @returns {Class.Property} */ -Class.memoize = function memoize(getter, wait) +Class.Memoize = Class.memoize = function Memoize(getter, wait) Class.Property({ configurable: true, enumerable: true, @@ -893,6 +893,19 @@ Class.memoize = function memoize(getter, wait) } }); +/** + * Updates the given object with the object in the target class's + * prototype. + */ +Class.Update = function Update(obj) + Class.Property({ + configurable: true, + enumerable: true, + init: function (key, target) { + this.value = update({}, target[key], obj); + } + }); + Class.replaceProperty = function replaceProperty(obj, prop, value) { Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true }); return value; diff --git a/common/modules/bootstrap.jsm b/common/modules/bootstrap.jsm index fc4eac60..ca5eaa15 100644 --- a/common/modules/bootstrap.jsm +++ b/common/modules/bootstrap.jsm @@ -14,11 +14,6 @@ var BOOTSTRAP_CONTRACT = "@dactyl.googlecode.com/base/bootstrap"; var JSMLoader = BOOTSTRAP_CONTRACT in Components.classes && Components.classes[BOOTSTRAP_CONTRACT].getService().wrappedJSObject.loader; -if (!JSMLoader && "@mozilla.org/fuel/application;1" in Components.classes) - JSMLoader = Components.classes["@mozilla.org/fuel/application;1"] - .getService(Components.interfaces.extIApplication) - .storage.get("dactyl.JSMLoader", null); - if (JSMLoader && JSMLoader.bump === 6) JSMLoader.global = this; else @@ -133,6 +128,8 @@ else purge: function purge() { dump("dactyl: JSMLoader: purge\n"); + this.bootstrap = null; + if (Cu.unload) { Object.keys(this.modules).reverse().forEach(function (url) { try { diff --git a/common/modules/config.jsm b/common/modules/config.jsm index 6b7273ac..048cafc9 100644 --- a/common/modules/config.jsm +++ b/common/modules/config.jsm @@ -309,7 +309,7 @@ var ConfigBase = Class("ConfigBase", { version: Class.memoize(function () { if (this.VCSPath) return io.system(["hg", "-R", this.VCSPath, "log", "-r.", - "--template=hg{rev}." + this.branch]).output; + "--template=hg{rev}-{branch}"]).output; return this.addon.version; }), @@ -504,13 +504,6 @@ var ConfigBase = Class("ConfigBase", { */ host: null, - /** - * @property {[[]]} An array of application specific mode specifications. - * The values of each mode are passed to modes.addMode during - * dactyl startup. - */ - modes: [], - /** * @property {string} The name of the extension. * Required. diff --git a/common/modules/prefs.jsm b/common/modules/prefs.jsm index 13500685..af3ea15e 100644 --- a/common/modules/prefs.jsm +++ b/common/modules/prefs.jsm @@ -44,7 +44,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) cleanup: function cleanup(reason) { if (this.defaults != this) - this.defaults.cleanup(); + this.defaults.cleanup(reason); this._observers = {}; if (this.observe) { @@ -62,7 +62,7 @@ var Prefs = Module("prefs", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]) this.branches.saved.resetBranch(); } - if (reason == "uninstall" && this == prefs) + if (reason == "uninstall") localPrefs.resetBranch(); } }, diff --git a/common/modules/protocol.jsm b/common/modules/protocol.jsm index 8634d9f9..e8ddea05 100644 --- a/common/modules/protocol.jsm +++ b/common/modules/protocol.jsm @@ -12,12 +12,10 @@ defineModule("protocol", { var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal); -var DNE = "resource://gre/does/not/exist"; - -function Channel(url, orig, noFake) { +function Channel(url, orig, noErrorChannel) { try { if (url == null) - return noFake ? null : FakeChannel(orig); + return noErrorChannel ? null : NetError(orig); if (url instanceof Ci.nsIChannel) return url; @@ -29,17 +27,28 @@ function Channel(url, orig, noFake) { return let ([type, data] = url) StringChannel(data, type, orig); let uri = services.io.newURI(url, null, null); - return (new XMLChannel(uri, null, noFake)).channel; + return (new XMLChannel(uri, null, noErrorChannel)).channel; } catch (e) { util.reportError(e); + util.dump(url); throw e; } } -function FakeChannel(orig) { - let channel = services.io.newChannel(DNE, null, null); - channel.originalURI = orig; - return channel; +function NetError(orig, error) { + return services.InterfacePointer({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel]), + + name: orig.spec, + + URI: orig, + + originalURI: orig, + + asyncOpen: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND }, + + open: function () { throw error || Cr.NS_ERROR_FILE_NOT_FOUND } + }).data.QueryInterface(Ci.nsIChannel); } function RedirectChannel(to, orig, time) { let html = .toXMLString(); @@ -68,7 +77,19 @@ function ProtocolBase() { this.wrappedJSObject = this; this.pages = {}; - this.providers = {}; + this.providers = { + "content": function (uri, path) this.pages[path] || this.contentBase + path, + + "data": function (uri) { + var channel = services.io.newChannel(uri.path.replace(/^\/(.*)(?:#.*)?/, "data:$1"), + null, null); + + channel.contentCharset = "UTF-8"; + channel.owner = systemPrincipal; + channel.originalURI = uri; + return channel; + } + }; } ProtocolBase.prototype = { get contractID() "@mozilla.org/network/protocol;1?name=" + this.scheme, @@ -92,34 +113,17 @@ ProtocolBase.prototype = { try { uri.QueryInterface(Ci.nsIURL); + let path = decodeURIComponent(uri.filePath.substr(1)); if (uri.host in this.providers) - return Channel(this.providers[uri.host](uri, uri.filePath.substr(1)), uri); + return Channel(this.providers[uri.host].call(this, uri, path), + uri); - let path = decodeURIComponent(uri.path.replace(/^\/|#.*/g, "")); - switch(uri.host) { - case "content": - return Channel(this.pages[path] || this.contentBase + path, uri); - case "data": - try { - var channel = services.io.newChannel(uri.path.replace(/^\/(.*)(?:#.*)?/, "data:$1"), - null, null); - } - catch (e) { - var error = e; - break; - } - channel.contentCharset = "UTF-8"; - channel.owner = systemPrincipal; - channel.originalURI = uri; - return channel; - } + return NetError(uri); } catch (e) { util.reportError(e); + throw e; } - if (error) - throw error; - return FakeChannel(uri); } }; @@ -131,7 +135,7 @@ function LocaleChannel(pkg, locale, path, orig) { return channel; } - return FakeChannel(orig); + return NetError(orig); } function StringChannel(data, contentType, uri) { @@ -146,13 +150,13 @@ function StringChannel(data, contentType, uri) { return channel; } -function XMLChannel(uri, contentType, noFake) { +function XMLChannel(uri, contentType, noErrorChannel) { try { var channel = services.io.newChannelFromURI(uri); var channelStream = channel.open(); } catch (e) { - this.channel = noFake ? null : FakeChannel(uri); + this.channel = noErrorChannel ? null : NetError(uri); return; } diff --git a/common/modules/styles.jsm b/common/modules/styles.jsm index 3f5b28a8..4ebce7d2 100644 --- a/common/modules/styles.jsm +++ b/common/modules/styles.jsm @@ -254,12 +254,14 @@ var Styles = Module("Styles", { this.cleanup(); this.allSheets = {}; - services["dactyl:"].providers["style"] = function styleProvider(uri) { - let id = /^\/(\d*)/.exec(uri.path)[1]; - if (Set.has(styles.allSheets, id)) - return ["text/css", styles.allSheets[id].fullCSS]; - return null; - }; + update(services["dactyl:"].providers, { + "style": function styleProvider(uri, path) { + let id = parseInt(path); + if (Set.has(styles.allSheets, id)) + return ["text/css", styles.allSheets[id].fullCSS]; + return null; + } + }); }, cleanup: function cleanup() { diff --git a/common/modules/util.jsm b/common/modules/util.jsm index 70c4b1a9..7f49a39a 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -1589,8 +1589,8 @@ var DOM = Class("DOM", { attrHooks: array.toObject([ ["", { - href: { get: function (elem) elem.href }, - src: { get: function (elem) elem.src } + href: { get: function (elem) elem.href || elem.getAttribute("href") }, + src: { get: function (elem) elem.src || elem.getAttribute("src") } }] ]), @@ -1843,7 +1843,7 @@ var DOM = Class("DOM", { */ get style() { let node = this[0]; - while (!(node instanceof Ci.nsIDOMElement) && node.parentNode) + while (node && !(node instanceof Ci.nsIDOMElement) && node.parentNode) node = node.parentNode; try { diff --git a/common/tests/functional/testHelpCommands.js b/common/tests/functional/testHelpCommands.js index dd0afe96..912545ba 100644 --- a/common/tests/functional/testHelpCommands.js +++ b/common/tests/functional/testHelpCommands.js @@ -21,10 +21,17 @@ var setupTest = function (test) { function urlTarget(url) Services.io.newChannel(url, null, null).name; -__defineGetter__("doesNotExist", function () { - delete this.doesNotExist; - return this.doesNotExist = urlTarget("dactyl://help-tag/non-existent-help-tag-url-thingy"); -}); +function urlExists(url) { + try { + let chan = Services.io.newChannel(url); + chan.open(); + try { chan.cancel(Cr.NS_BINDING_ABORTED) } catch (e) {} + return true; + } + catch (e) { + return false; + } +} const HELP_FILES = ["all", "tutorial", "intro", "starting", "browsing", "buffer", "cmdline", "editing", "options", "pattern", "tabs", "hints", @@ -70,7 +77,7 @@ var testExHelpCommand_PageTagArg_OpensHelpPageContainingTag = function () { let links = controller.tabs.activeTab.querySelectorAll("a[href^='dactyl:']"); - let missing = Array.filter(links, function (link) urlTarget(link.href) === doesNotExist) + let missing = Array.filter(links, function (link) urlExists(link.href)) .map(function (link) link.textContent + " -> " + link.href); utils.assertEqual("testHelpCommands.assertNoDeadLinks", 0, missing.length, diff --git a/pentadactyl/content/config.js b/pentadactyl/content/config.js index b7307678..e7c032b4 100644 --- a/pentadactyl/content/config.js +++ b/pentadactyl/content/config.js @@ -18,7 +18,7 @@ var Config = Module("config", ConfigBase, { Local: function Local(dactyl, modules, window) let ({ config } = modules) ({ - completers: Class.memoize(function () update({ sidebar: "sidebar", window: "window" }, this.__proto__.completers)), + completers: Class.Update({ sidebar: "sidebar", window: "window" }), dialogs: { about: ["About Firefox", @@ -278,7 +278,7 @@ var Config = Module("config", ConfigBase, { const { CompletionContext, bookmarkcache, completion } = modules; const { document } = window; - var searchRunning = null; // only until Firefox fixes https://bugzilla.mozilla.org/show_bug.cgi?id=510589 + var searchRunning = null; completion.location = function location(context) { if (!services.autoCompleteSearch) return; @@ -349,13 +349,9 @@ var Config = Module("config", ConfigBase, { "Handled by " + config.host, function () Events.PASS_THROUGH); }, - modes: function (dactyl, modules, window) { - const { modes } = modules; - config.modes.forEach(function (mode) { modes.addMode.apply(this, mode); }); - }, options: function (dactyl, modules, window) { modules.options.add(["online"], - "Set the 'work offline' option", + "Enables or disables offline mode", "boolean", true, { setter: function (value) {