diff --git a/common/content/buffer.js b/common/content/buffer.js index c31b92be..b905e2c2 100644 --- a/common/content/buffer.js +++ b/common/content/buffer.js @@ -352,8 +352,16 @@ var Buffer = Module("buffer", { focusAllowed: function focusAllowed(elem) { if (elem instanceof Window && !Editor.getEditor(elem)) return true; + let doc = elem.ownerDocument || elem.document || elem; - return !options["strictfocus"] || doc.dactylFocusAllowed; + switch (options.get("strictfocus").getKey(doc.documentURI, "moderate")) { + case "despotic": + return elem.dactylFocusAllowed; + case "moderate": + return doc.dactylFocusAllowed; + default: + return true; + } }, /** @@ -365,6 +373,7 @@ var Buffer = Module("buffer", { */ focusElement: function focusElement(elem) { let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem; + elem.dactylFocusAllowed = true; win.document.dactylFocusAllowed = true; if (isinstance(elem, [HTMLFrameElement, HTMLIFrameElement])) @@ -1655,7 +1664,7 @@ var Buffer = Module("buffer", { let elem = buffer.lastInputField; if (args.count >= 1 || !elem || !events.isContentNode(elem)) { - let xpath = ["frame", "iframe", "input", "textarea[not(@disabled) and not(@readonly)]"]; + let xpath = ["frame", "iframe", "input", "xul:textbox", "textarea[not(@disabled) and not(@readonly)]"]; let frames = buffer.allFrames(null, true); @@ -1670,7 +1679,8 @@ var Buffer = Module("buffer", { let computedStyle = util.computedStyle(elem); let rect = elem.getBoundingClientRect(); return computedStyle.visibility != "hidden" && computedStyle.display != "none" && - computedStyle.MozUserFocus != "ignore" && rect.width && rect.height; + (elem instanceof Ci.nsIDOMXULTextBoxElement || computedStyle.MozUserFocus != "ignore") && + rect.width && rect.height; }); dactyl.assert(elements.length > 0); diff --git a/common/content/editor.js b/common/content/editor.js index 5f6cf74e..f92f3e0e 100644 --- a/common/content/editor.js +++ b/common/content/editor.js @@ -654,7 +654,10 @@ var Editor = Module("editor", { }); mappings.add([modes.INPUT], - ["", "", "", "", "", ""], + ["", "", "", "", "", "", + "", "", "", "", + "", "", "", "", "", "", + "", "", "", ""], "Handled by " + config.host, function () Events.PASS); diff --git a/common/content/events.js b/common/content/events.js index 2052e6b4..f2f44374 100644 --- a/common/content/events.js +++ b/common/content/events.js @@ -1135,14 +1135,19 @@ var Events = Module("events", { let win = (elem.ownerDocument || elem).defaultView || elem; - if (events.isContentNode(elem) && !buffer.focusAllowed(elem) - && !(services.focus.getLastFocusMethod(win) & 0x7000) + if (!(services.focus.getLastFocusMethod(win) & 0x7000) + && events.isContentNode(elem) + && !buffer.focusAllowed(elem) && isinstance(elem, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, Window])) { + if (elem.frameElement) dactyl.focusContent(true); else if (!(elem instanceof Window) || Editor.getEditor(elem)) dactyl.focus(window); } + + if (elem instanceof Element) + elem.dactylFocusAllowed = undefined; }, /* @@ -1329,8 +1334,11 @@ var Events = Module("events", { let elem = event.target; let win = elem.ownerDocument && elem.ownerDocument.defaultView || elem; - for (; win; win = win != win.parent && win.parent) + for (; win; win = win != win.parent && win.parent) { + for (; elem instanceof Element; elem = elem.parentNode) + elem.dactylFocusAllowed = true; win.document.dactylFocusAllowed = true; + } }, popupshown: function onPopupShown(event) { @@ -1650,7 +1658,14 @@ var Events = Module("events", { options.add(["strictfocus", "sf"], "Prevent scripts from focusing input elements without user intervention", - "boolean", true); + "sitemap", "*:moderate", + { + values: { + despotic: "Only allow focus changes when explicitly requested by the user", + moderate: "Allow focus changes after user-initiated focus change", + "laisses-faire": "Always allow focus changes" + } + }); options.add(["timeout", "tmo"], "Whether to execute a shorter key command after a timeout when a longer command exists", diff --git a/common/modules/options.jsm b/common/modules/options.jsm index 6676e736..319c83e9 100644 --- a/common/modules/options.jsm +++ b/common/modules/options.jsm @@ -311,7 +311,10 @@ var Option = Class("Option", { * @property {function(CompletionContext, Args)} This option's completer. * @see CompletionContext */ - completer: function completer(context) { + completer: function completer(context, extra) { + if (/map$/.test(this.type) && extra.value == null) + return; + if (this.values) context.completions = this.values; }, diff --git a/common/modules/util.jsm b/common/modules/util.jsm index df4dca63..a8ffcc26 100644 --- a/common/modules/util.jsm +++ b/common/modules/util.jsm @@ -950,7 +950,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]), */ makeXPath: function makeXPath(nodes) { return array(nodes).map(util.debrace).flatten() - .map(function (node) [node, "xhtml:" + node]).flatten() + .map(function (node) /^[a-z]+:/.test(node) ? node : [node, "xhtml:" + node]).flatten() .map(function (node) "//" + node).join(" | "); },