diff options
-rw-r--r-- | common/content/buffer.js | 9 | ||||
-rw-r--r-- | common/content/dactyl.js | 2 | ||||
-rw-r--r-- | common/content/mappings.js | 2 | ||||
-rw-r--r-- | common/content/tabs.js | 13 | ||||
-rw-r--r-- | common/modules/addons.jsm | 84 | ||||
-rw-r--r-- | common/tests/functional/dactyl.js | 42 | ||||
-rw-r--r-- | common/tests/functional/testCommands.js | 256 |
7 files changed, 319 insertions, 89 deletions
diff --git a/common/content/buffer.js b/common/content/buffer.js index 020d9bdd..1e6e8d95 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -314,13 +314,14 @@ var Buffer = Module("buffer", { statusline.updateUrl(); - if (webProgress.DOMWindow && uri) { - statusline.updateProgress(webProgress.DOMWindow); + let win = webProgress.DOMWindow; + if (win && uri) { + statusline.updateProgress(win); let oldURI = webProgress.document.dactylURI; if (webProgress.document.dactylLoadIdx === webProgress.loadedTransIndex || !oldURI || uri.spec.replace(/#.*/, "") !== oldURI.replace(/#.*/, "")) - for (let frame in values(buffer.allFrames(webProgress.DOMWindow))) + for (let frame in values(buffer.allFrames(win))) frame.document.dactylFocusAllowed = false; webProgress.document.dactylURI = uri.spec; webProgress.document.dactylLoadIdx = webProgress.loadedTransIndex; @@ -334,7 +335,7 @@ var Buffer = Module("buffer", { util.timeout(function () { buffer._triggerLoadAutocmd("LocationChange", - (webProgress.DOMWindow || content).document, + (win || content).document, uri); }); diff --git a/common/content/dactyl.js b/common/content/dactyl.js index 4263e64b..879c745a 100644 --- a/common/content/dactyl.js +++ b/common/content/dactyl.js @@ -588,7 +588,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), { * Initialize the help system. */ initHelp: function (force) { - if (!force && !this.helpInitialized) { + if (force || !this.helpInitialized) { if ("noscriptOverlay" in window) { noscriptOverlay.safeAllow("chrome-data:", true, false); noscriptOverlay.safeAllow("dactyl:", true, false); diff --git a/common/content/mappings.js b/common/content/mappings.js index 26edda35..7958dd4b 100644 --- a/common/content/mappings.js +++ b/common/content/mappings.js @@ -703,7 +703,7 @@ var Mappings = Module("mappings", { keepQuotes: true, options: [ { - names: ["-description", "-d"], + names: ["-description", "-desc", "-d"], description: "A description of this mapping group", type: CommandOption.STRING }, diff --git a/common/content/tabs.js b/common/content/tabs.js index 6766e388..49add59a 100644 --- a/common/content/tabs.js +++ b/common/content/tabs.js @@ -348,12 +348,15 @@ var Tabs = Module("tabs", { * reloading. */ reload: function (tab, bypassCache) { - if (bypassCache) { - const flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; - config.tabbrowser.getBrowserForTab(tab).reloadWithFlags(flags); + try { + if (bypassCache) { + const flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; + config.tabbrowser.getBrowserForTab(tab).reloadWithFlags(flags); + } + else + config.tabbrowser.reloadTab(tab); } - else - config.tabbrowser.reloadTab(tab); + catch (e if !(e instanceof Error)) {} }, /** diff --git a/common/modules/addons.jsm b/common/modules/addons.jsm index 368f27ec..826c11a8 100644 --- a/common/modules/addons.jsm +++ b/common/modules/addons.jsm @@ -436,18 +436,24 @@ var Addons = Module("addons", { } }); -if (!Ci.nsIExtensionManager || !services.extensionManager) +if (!("nsIExtensionManager" in Ci) || !services.extensionManager) Components.utils.import("resource://gre/modules/AddonManager.jsm"); else var AddonManager = { + PERM_CAN_UNINSTALL: 1, + PERM_CAN_ENABLE: 2, + PERM_CAN_DISABLE: 4, + PERM_CAN_UPGRADE: 8, + getAddonByID: function (id, callback) { callback = callback || util.identity; - let addon = id; - if (!isObject(addon)) - addon = services.extensionManager.getItemForID(id); - if (!addon) - return callback(null); - addon = Object.create(addon); + addon = services.extensionManager.getItemForID(id); + if (addon) + addon = this.wrapAddon(addon); + return callback(addon); + }, + wrapAddon: function wrapAddon(addon) { + addon = Object.create(addon.QueryInterface(Ci.nsIUpdateItem)); function getRdfProperty(item, property) { let resource = services.rdf.GetResource("urn:mozilla:item:" + item.id); @@ -471,6 +477,8 @@ else update(addon, { + get permissions() 1 | (this.userDisabled ? 2 : 4), + appDisabled: false, installLocation: Class.memoize(function () services.extensionManager.getInstallLocation(this.id)), @@ -491,15 +499,18 @@ else } }); - return callback(addon); + return addon; }, getAddonsByTypes: function (types, callback) { let res = []; for (let [, type] in Iterator(types)) for (let [, item] in Iterator(services.extensionManager .getItemList(Ci.nsIUpdateItem["TYPE_" + type.toUpperCase()], {}))) - res.push(this.getAddonByID(item)); - return (callback || util.identity)(res); + res.push(this.wrapAddon(item)); + + if (callback) + util.timeout(function () { callback(res); }); + return res; }, getInstallForFile: function (file, callback, mimetype) { callback({ @@ -512,6 +523,34 @@ else getInstallForURL: function (url, callback, mimetype) { dactyl.assert(false, "Install by URL not implemented"); }, + observers: [], + addAddonListener: function (listener) { + observer.listener = listener; + function observer(subject, topic, data) { + if (subject instanceof Ci.nsIUpdateItem) + subject = AddonManager.wrapAddon(subject); + + if (data === "item-installed") + listener.onInstalling(subject, true); + else if (data === "item-uninstalled") + listener.onUnistalling(subject, true); + else if (data === "item-upgraded") + listener.onInstalling(subject, true); + else if (data === "item-enabled") + listener.onEnabling(subject, true); + else if (data === "item-disabled") + listener.onDisabling(subject, true); + } + services.observer.addObserver(observer, "em-action-requested", false); + this.observers.push(observer); + }, + removeAddonListener: function (listener) { + this.observers = this.observers.filter(function (observer) { + if (observer.listener !== listener) + return true; + services.observer.removeObserver(observer, "em-action-requested"); + }); + } }; var addonErrors = array.toObject([ @@ -522,15 +561,22 @@ var addonErrors = array.toObject([ endModule(); -iter.forEach(properties(config.addon), function (prop) { - let desc = Object.getOwnPropertyDescriptor(config.addon, prop); - if (callable(desc.value)) - Addon.prototype[prop] = function proxy() this.addon[prop].apply(this.addon, arguments); - else - Object.defineProperty(Addon.prototype, prop, { - get: function get_proxy() this.addon[prop], - set: function set_proxy(val) this.addon[prop] = val - }); +let iterator = properties(config.addon); +if ("nsIUpdateItem" in Ci) + iterator = iter(iterator, properties(config.addon.__proto__)); + +iter.forEach(iterator, function (prop) { + let desc = Object.getOwnPropertyDescriptor(config.addon, prop) || + Object.getOwnPropertyDescriptor(config.addon.__proto__, prop); + + if (!set.has(Addon.prototype, prop)) + if (callable(desc.value)) + Addon.prototype[prop] = function proxy() this.addon[prop].apply(this.addon, arguments); + else + Object.defineProperty(Addon.prototype, prop, { + get: function get_proxy() this.addon[prop], + set: function set_proxy(val) this.addon[prop] = val + }); }); } catch(e){ if (isString(e)) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); } diff --git a/common/tests/functional/dactyl.js b/common/tests/functional/dactyl.js index 9bd4f1ed..d1b38344 100644 --- a/common/tests/functional/dactyl.js +++ b/common/tests/functional/dactyl.js @@ -50,7 +50,7 @@ function Controller(controller) { } this._countError = function countError(message, highlight) { if (/\bErrorMsg\b/.test(highlight)) - self.errorCount++; + self.errorMessageCount++; } this.dactyl.dactyl.registerObserver("beep", this._countBeep); this.dactyl.dactyl.registerObserver("echoLine", this._countError); @@ -69,6 +69,7 @@ Controller.prototype = { beepCount: 0, errorCount: 0, + errorMessageCount: 0, /** * Asserts that an error message is displayed during the execution @@ -82,10 +83,10 @@ Controller.prototype = { * @param {string} message The message to display upon assertion failure. @optional */ assertMessageError: function (func, self, args, message) { - let errorCount = this.errorCount; + let errorCount = this.errorMessageCount; this.assertNoErrors(func, self, args, message); - // dump("assertMessageError " + errorCount + " " + this.errorCount + "\n"); - return utils.assert('dactyl.assertMessageError', this.errorCount > errorCount, + // dump("assertMessageError " + errorCount + " " + this.errorMessageCount + "\n"); + return utils.assert('dactyl.assertMessageError', this.errorMessageCount > errorCount, "Expected error but got none" + (message ? ": " + message : "")); }, @@ -164,13 +165,12 @@ Controller.prototype = { * of *func*. @optional * @param {Array} args Arguments to be passed to *func*. @optional * @param {string} message The message to display upon assertion failure. @optional - * @param {string} message The message to display upon assertion failure. @optional */ assertNoErrors: function (func, self, args, message) { let msg = message ? ": " + message : ""; + let beepCount = this.beepCount; let errorCount = this.errorCount; - if (func) { errorCount = this.dactyl.util.errorCount; @@ -199,6 +199,34 @@ Controller.prototype = { }, /** + * Asserts that the no error messages are reported during the call + * of *func*. + * + * @param {function} func A function to call during before the + * assertion takes place. When present, the current error count + * is reset before execution. + * @optional + * @param {object} self The 'this' object to be used during the call + * of *func*. @optional + * @param {Array} args Arguments to be passed to *func*. @optional + * @param {string} message The message to display upon assertion failure. @optional + */ + assertNoErrorMessages: function (func, self, args, message) { + let msg = message ? ": " + message : ""; + let count = this.errorMessageCount; + + try { + func.apply(self || this, args || []); + } + catch (e) { + this.dactyl.util.reportError(e); + } + + return utils.assertEqual('dactyl.assertNoErrorMessages', count, this.errorMessageCount, + "Error messsages were reported" + msg); + }, + + /** * Resets the error count used to determine whether new errors were * reported during the execution of a test. */ @@ -352,7 +380,7 @@ Controller.prototype = { runExCompletion: wrapAssertNoErrors(function (cmd) { this.setExMode(); - utils.assertEqual("dactyl.runExCompletion", + utils.assertEqual("dactyl.assertCommandLineFocused", this.elements.commandInput, this.elements.focused, "Running Ex Completion: The command line is not focused"); diff --git a/common/tests/functional/testCommands.js b/common/tests/functional/testCommands.js index bee10881..e0266a93 100644 --- a/common/tests/functional/testCommands.js +++ b/common/tests/functional/testCommands.js @@ -42,7 +42,7 @@ var tests = { anyOutput: ["about:pentadactyl"] }, bmark: { - anyOutput: ["bmark", "bmark -tags=foo -titlt=bar -keyword=baz -charset=UTF-8 -post=quux about:pentadactyl"], + someOutput: ["bmark", "bmark -tags=foo -titlt=bar -keyword=baz -charset=UTF-8 -post=quux about:pentadactyl"], error: ["bmark -tags=foo -titlt=bar -keyword=baz -charset=nonExistentCharset -post=quux about:pentadactyl"], completions: [ "-max=1 -keyword=", @@ -71,14 +71,16 @@ var tests = { completions: ["", "1"] }, cd: { - anyOutput: ["", "~/"], + lineOutput: ["", "~/"], completions: ["", "~/"] }, colorscheme: { error: ["", "some-non-existent-scheme"] }, command: { - anyOutput: ["", "foo", "foo bar", "-js bar baz"], + multiOutput: [""], + someOutput: ["foo"], + noOutput: ["foo bar", "-js bar baz"], error: ["foo bar", "-js bar baz"] }, comclear: { @@ -103,9 +105,7 @@ var tests = { get delmarks() this.delmacros, get delqmarks() this.delmacros, delstyle: { - completions: [ - "", "-name=", "-name=foo ", "-index=", "-index=" - ] + completions: ["", "-name=", "-name=foo ", "-index=", "-index="] }, dialog: { // Skip implementation for now @@ -114,7 +114,7 @@ var tests = { doautoall: {}, // Skip for now doautocmd: {}, // Skip for now downloads: { - multiOutput: ["", "dactyl", "-type=extension", "-type=extension dactyl"] + multiOutput: ["", "dactyl", "dactyl"] }, echo: { singleOutput: ["' - '"], @@ -127,7 +127,10 @@ var tests = { "commands.get('" ] }, - get echoerr() this.echo, + get echoerr() ({ + errorsOk: true, + __proto__: this.echo, + }), get echomsg() this.echo, else: {}, // Skip for now elseif: {}, // Skip for now @@ -218,50 +221,180 @@ var tests = { get listoptions() this.listcommands, loadplugins: {}, macros: { - multiOutput: [""] + multiOutput: [""], + complete: [""] }, map: { multiOutput: ["", "i"], - noOutput: ["i j", "-b i j", "-js i j()", "-ex i :j"] + noOutput: [ + "i j", + "-builtin i j", + "-group=user -b i j", + "-js i j()", + "-ex i :j", + "-silent i :j", + "-mode=ex -b <C-a> <C-a>" + ], + error: [ + "-mode=some-nonexistent-mode <C-a> <C-a>", + "-gtroup=some-nonexistent-group <C-a> <C-a>" + ], + complete: [ + "", + "-", + "-mode=ex ", + "-mode=", + "-group=", + "-builtin i ", + "-ex i ", + "-javascript i ", + ] }, mapclear: { + noOutput: [""], + complete: [""] + }, + mapgroup: { + multiOutput: [""], + noOutput: [ + "foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'", + "! foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'", + "foo", + "user" + ], + error: [ + "some-nonexistent-group", + "foo -d='foo group' -nopersist 'bar.com,http://bar/*,http://bar,^http:'" + ], + complete: [ + "", + "foo " + ], + cleanup: ["delmapgroup foo"] + }, + mark: { + error: ["", "#", "xy"], + noOutput: ["y"], + complete: [""] + }, + marks: { + init: ["delmarks q"], + multiOutput: ["", "y"], + error: ["q", "#"], + complete: [""] + }, + messages: { + anyOutput: ["messages"] + }, + messclear: { + error: ["q"], noOutput: [""] }, - mapgroup: {}, - mark: {}, - marks: {}, - messages: {}, - messclear: {}, - mkpentadactylrc: {}, - mksyntax: {}, - mlistkeys: {}, - mmap: {}, - mmapclear: {}, - mnoremap: {}, - munmap: {}, - nlistkeys: {}, - nmap: {}, - nmapclear: {}, - nnoremap: {}, - nohlfind: {}, - noremap: {}, - normal: {}, - nunmap: {}, - open: {}, - pageinfo: {}, - pagestyle: {}, - preferences: {}, - pwd: {}, - qmark: {}, - qmarks: {}, - quit: {}, - quitall: {}, - redraw: {}, - rehash: {}, - reload: {}, - reloadall: {}, - restart: {}, - runtime: {}, + mkpentadactylrc: { + noOutput: [ + "some-nonexistent-rc.penta", + "! some-nonexistent-rc.penta" + ], + error: ["some-nonexistent-rc.penta"], + complete: [""], + cleanup: ["silent !rm some-nonexistent-rc.penta"] + }, + mksyntax: { + noOutput: [ + "some-nonexistent-pentadactyl-dir/", + "! some-nonexistent-pentadactyl-dir/", + "some-nonexistent-pentadactyl-dir/foo.vim", + "! some-nonexistent-pentadactyl-dir/foo.vim", + ], + error: [ + "some-nonexistent-pentadactyl-dir/", + "some-nonexistent-pentadactyl-dir/foo.vim" + ], + complete: [""], + cleanup: ["silent !rm -r some-nonexistent-pentadactyl-dir/"] + }, + normal: { + noOutput: ["<Nop>"], + lineOutput: ["<C-g>"], + multiOutput: ["g<C-g>"] + }, + open: { + noOutput: ["about:blank | about:home"], + complete: [ + "", + "./", + "./ | ", + "chrome://", + "chrome://browser/", + "chrome://browser/content/", + "about:", + "resource://", + "resource://dactyl/" + ] + }, + pageinfo: { + multiOutput: ["", "fgm"], + complete: [""], + error: ["abcdefghijklmnopqrstuvwxyz", "f g m"] + }, + pagestyle: { + complete: [""] + }, + preferences: {}, // Skip for now + pwd: { + singleOutput: [""] + }, + qmark: { + lineOutput: [ + "m", + "m foo bar" + ], + error: ["", "#"], + complete: ["", "m "] + }, + qmarks: { + init: ["delqmarks x"], + multiOutput: ["", "m", "x"], + complete: [""] + }, + quit: {}, // Skip for now + quitall: {}, // Skip for now + redraw: { + noOutput: [""] + }, + rehash: {}, // Skip for now + reload: { + noOutput: [""] + }, + reloadall: { + noOutput: [""] + }, + restart: {}, // Skip + runtime: { + init: [ + "js File('~/.pentadactyl/some-nonexistent/good.css').write('')", + "js File('~/.pentadactyl/some-nonexistent/good.js').write('')", + "js File('~/.pentadactyl/some-nonexistent/bad.js').write('dactyl.echoerr(\"error\")')", + "js File('~/.pentadactyl/some-nonexistent/good.penta').write('')", + "js File('~/.pentadactyl/some-nonexistent/bad.penta').write('echoerr \"error\"')", + ], + cleanup: ["js File('~/.pentadactyl/some-nonexistent').remove(true)"], + noOutput: [ + "some-nonexistent/good.css", + "some-nonexistent/good.js", + "some-nonexistent/good.penta" + ], + errors: [ + "some-nonexistent/bad.js", + "some-nonexistent/bad.penta" + ], + singleOutput: ["some-nonexistent-file.js"], + complete: [ + "", + "plugins/", + "info/" + ] + }, sanitize: {}, saveas: {}, sbclose: {}, @@ -326,22 +459,37 @@ function addTest(cmdName, testName, func) { global["testCommand_" + cmdName + "_" + testName] = func; } -function runCommands(cmdName, testName, commands, test) { +function runCommands(cmdName, testName, commands, test, forbidErrors) { addTest(cmdName, testName, function () { commands.forEach(function (cmd) { // dump("CMD: " + cmdName + " " + cmd + "\n"); dactyl.clearMessage(); dactyl.closeMessageWindow(); + cmd = cmdName + cmd.replace(/^(!?) ?/, "$1 "); - dactyl.runExCommand(cmd); + if (forbidErrors) + dactyl.assertNoErrorMessages(function () { dactyl.runExCommand(cmd) }, + null, [], cmd); + else + dactyl.runExCommand(cmd); + controller.waitForPageLoad(controller.tabs.activeTab); + test(cmd); }); }); } +function _runCommands(cmdName, testName, commands) { + addTest(cmdName, testName, function () { + commands.forEach(function (cmd) { + dactyl.runExCommand(cmd); + controller.waitForPageLoad(controller.tabs.activeTab); + }); + }); +} for (var val in Iterator(tests)) (function ([command, params]) { if (params.init) - runCommands(command, "init", params.init, function () {}); + _runCommands(command, "init", params.init, function () {}); // Goddamn stupid fucking MozMill and its stupid fucking sandboxes with their ancient fucking JS versions. for (var val in Iterator(params)) (function ([testName, commands]) { @@ -362,12 +510,12 @@ for (var val in Iterator(tests)) (function ([command, params]) { case "singleOutput": runCommands(command, testName, commands, function (cmd) { dactyl.assertMessageLine(/./, "Expected command output: " + cmd); - }); + }, true); break; case "multiOutput": runCommands(command, testName, commands, function (cmd) { dactyl.assertMessageWindowOpen(true, "Expected command output: " + cmd); - }); + }, true && !params.errorsOk); break; case "error": addTest(command, testName, function () { @@ -375,6 +523,7 @@ for (var val in Iterator(tests)) (function ([command, params]) { cmd = command + cmd.replace(/^(!?) ?/, "$1 "); dactyl.assertMessageError(function () { dactyl.runExCommand(cmd); + controller.waitForPageLoad(controller.tabs.activeTab); }, null, [], cmd); }); }); @@ -382,7 +531,10 @@ for (var val in Iterator(tests)) (function ([command, params]) { case "completions": addTest(command, testName, function () { commands.forEach(function (cmd) { - dactyl.runExCompletion(command + cmd.replace(/^(!?) ?/, "$1 ")); + dactyl.assertNoErrorMessages(function () { + dactyl.runExCompletion(command + cmd.replace(/^(!?) ?/, "$1 ")); + controller.waitForPageLoad(controller.tabs.activeTab); + }); }); }); break; @@ -390,7 +542,7 @@ for (var val in Iterator(tests)) (function ([command, params]) { })(val); if (params.cleanup) - runCommands(command, "cleanup", params.cleanup, function () {}); + _runCommands(command, "cleanup", params.cleanup, function () {}); })(val); // vim: sw=4 ts=8 et: |