diff --git a/common/content/buffer.js b/common/content/buffer.js index e7879b8d..3e645678 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -1639,11 +1639,13 @@ const Buffer = Module("buffer", { options: function () { options.add(["nextpattern"], "Patterns to use when guessing the 'next' page in a document sequence", - "stringlist", UTF8("\\bnext\\b,^>$,^(>>|»)$,^(>|»),(>|»)$,\\bmore\\b")); + "stringlist", UTF8("\\bnext\\b,^>$,^(>>|»)$,^(>|»),(>|»)$,\\bmore\\b"), + { validator: Option.validateXPath }); options.add(["previouspattern"], "Patterns to use when guessing the 'previous' page in a document sequence", - "stringlist", UTF8("\\bprev|previous\\b,^<$,^(<<|«)$,^(<|«),(<|«)$")); + "stringlist", UTF8("\\bprev|previous\\b,^<$,^(<<|«)$,^(<|«),(<|«)$"), + { validator: Option.validateXPath }); options.add(["pageinfo", "pa"], "Desired info in the :pageinfo output", diff --git a/common/content/commandline.js b/common/content/commandline.js index d49568e5..54ff49f2 100644 --- a/common/content/commandline.js +++ b/common/content/commandline.js @@ -770,7 +770,7 @@ const CommandLine = Module("commandline", { // FIXME: should trigger "cancel" event if (events.isAcceptKey(key)) { let mode = this._currentExtendedMode; // save it here, as modes.pop() resets it - this._keepCommand = !userContext.hidden_option_command_afterimage; + this._keepCommand = userContext.hidden_option_command_afterimage; this._currentExtendedMode = null; // Don't let modes.pop trigger "cancel" modes.pop(); commandline.triggerCallback("submit", mode, command); diff --git a/common/content/hints.js b/common/content/hints.js index f9780551..6fb546c6 100644 --- a/common/content/hints.js +++ b/common/content/hints.js @@ -1053,25 +1053,15 @@ const Hints = Module("hints", { util.makeXPath(["input[not(@type='hidden')]", "a", "area", "iframe", "textarea", "button", "select", "*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @role='link']"]); - function checkXPath(val) { - try { - util.evaluateXPath(val, document.implementation.createDocument("", "", null)); - return true; - } - catch (e) { - return false; - } - } - options.add(["extendedhinttags", "eht"], "XPath string of hintable elements activated by ';'", "regexmap", "[iI]:" + Option.quote(util.makeXPath(["img"])), - { validator: checkXPath }); + { validator: Option.validateXPath }); options.add(["hinttags", "ht"], "XPath string of hintable elements activated by 'f' and 'F'", "string", DEFAULT_HINTTAGS, - { validator: checkXPath }); + { validator: Option.validateXPath }); options.add(["hintkeys", "hk"], "The keys used to label and select hints", diff --git a/common/content/options.js b/common/content/options.js index 619a9cb0..63613df5 100644 --- a/common/content/options.js +++ b/common/content/options.js @@ -48,6 +48,9 @@ const Option = Class("Option", { if (this.type in Option.joinValues) this.joinValues = Option.joinValues[this.type]; + if (this.type in Option.testValues) + this.testValues = Option.testValues[this.type]; + this._op = Option.ops[this.type]; if (arguments.length > 3) { @@ -325,6 +328,8 @@ const Option = Class("Option", { */ setter: null, + testValues: function (values, validator) validator(values), + /** * @property {function} The function called to validate the option's value * when set. @@ -383,7 +388,7 @@ const Option = Class("Option", { return re; }, unparseRegex: function (re) re.bang + Option.quote(re.source.replace(/\\(.)/g, function (m, n1) n1 == "/" ? n1 : m), /^!|:/) + - (typeof re.result == "string" ? ":" + Option.quote(re.result) : ""), + (typeof re.result === "string" ? ":" + Option.quote(re.result) : ""), getKey: { stringlist: function (k) this.values.indexOf(k) >= 0, @@ -427,6 +432,12 @@ const Option = Class("Option", { }) }, + testValues: { + regexmap: function (vals, validator) vals.every(function (re) validator(re.result)), + stringlist: function (vals, validator) vals.every(validator, this), + stringmap: function (vals, validator) array(values(vals)).every(validator, this) + }, + dequote: function (value) { let arg; [, arg, Option._quote] = Commands.parseArg(String(value), ""); @@ -556,6 +567,12 @@ const Option = Class("Option", { if (this.type == "regexmap") return Array.concat(values).every(function (re) res.some(function (item) item[0] == re.result)); return Array.concat(values).every(function (value) res.some(function (item) item[0] == value)); + }, + + validateXPath: function (values) { + let evaluator = XPathEvaluator(); + return this.testValues(values, + function (value) evaluator.createExpression(value, util.evaluateXPath.resolver)); } }); diff --git a/common/modules/util.jsm b/common/modules/util.jsm index e7f3f1cf..c73d30e6 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -275,34 +275,36 @@ const Util = Module("Util", { * @param {boolean} asIterator Whether to return the results as an * XPath iterator. */ - evaluateXPath: function (expression, doc, elem, asIterator) { - if (!doc) - doc = util.activeWindow.content.document; - if (!elem) - elem = doc; - if (isArray(expression)) - expression = util.makeXPath(expression); + evaluateXPath: (function () { + function evaluateXPath(expression, doc, elem, asIterator) { + if (!doc) + doc = util.activeWindow.content.document; + if (!elem) + elem = doc; + if (isArray(expression)) + expression = util.makeXPath(expression); - let result = doc.evaluate(expression, elem, - function lookupNamespaceURI(prefix) { - return { - xul: XUL.uri, - xhtml: XHTML.uri, - xhtml2: "http://www.w3.org/2002/06/xhtml2", - dactyl: NS.uri - }[prefix] || null; - }, - asIterator ? Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE : Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE, - null - ); + let result = doc.evaluate(expression, elem, + evaluateXPath.resolver, + asIterator ? Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE : Ci.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE, + null + ); - return Object.create(result, { - __iterator__: { - value: asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; } - : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); } - } - }); - }, + return Object.create(result, { + __iterator__: { + value: asIterator ? function () { let elem; while ((elem = this.iterateNext())) yield elem; } + : function () { for (let i = 0; i < this.snapshotLength; i++) yield this.snapshotItem(i); } + } + }); + } + evaluateXPath.resolver = function lookupNamespaceURI(prefix) ({ + xul: XUL.uri, + xhtml: XHTML.uri, + xhtml2: "http://www.w3.org/2002/06/xhtml2", + dactyl: NS.uri + }[prefix] || null); + return evaluateXPath; + })(), extend: function extend(dest) { Array.slice(arguments, 1).filter(util.identity).forEach(function (src) {