From a537346a6522014c47e5d8f062ef59aa939b1092 Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Fri, 15 Aug 2014 07:50:43 -0500 Subject: [PATCH] Bug 1050439 - Focus should be restored to previously active element after split console is closed;r=jwalker --- browser/devtools/framework/toolbox.js | 31 +++++++- browser/devtools/webconsole/test/browser.ini | 1 + .../test/browser_webconsole_split_focus.js | 74 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 browser/devtools/webconsole/test/browser_webconsole_split_focus.js diff --git a/browser/devtools/framework/toolbox.js b/browser/devtools/framework/toolbox.js index 3afa78dd2b2..d8fb23518b9 100644 --- a/browser/devtools/framework/toolbox.js +++ b/browser/devtools/framework/toolbox.js @@ -76,6 +76,7 @@ function Toolbox(target, selectedTool, hostType, hostOptions) { this._highlighterHidden = this._highlighterHidden.bind(this); this._prefChanged = this._prefChanged.bind(this); this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this); + this._onFocus = this._onFocus.bind(this); this._target.on("close", this.destroy); @@ -253,7 +254,7 @@ Toolbox.prototype = { this._applyCacheSettings(); this._addKeysToWindow(); this._addReloadKeys(); - this._addToolSwitchingKeys(); + this._addHostListeners(); this._addZoomKeys(); this._loadInitialZoom(); @@ -345,7 +346,7 @@ Toolbox.prototype = { }); }, - _addToolSwitchingKeys: function() { + _addHostListeners: function() { let nextKey = this.doc.getElementById("toolbox-next-tool-key"); nextKey.addEventListener("command", this.selectNextTool.bind(this), true); let prevKey = this.doc.getElementById("toolbox-previous-tool-key"); @@ -354,6 +355,8 @@ Toolbox.prototype = { // Split console uses keypress instead of command so the event can be // cancelled with stopPropagation on the keypress, and not preventDefault. this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false); + + this.doc.addEventListener("focus", this._onFocus, true); }, _saveSplitConsoleHeight: function() { @@ -982,6 +985,23 @@ Toolbox.prototype = { } }, + /** + * If the console is split and we are focusing an element outside + * of the console, then store the newly focused element, so that + * it can be restored once the split console closes. + */ + _onFocus: function({originalTarget}) { + // Ignore any non element nodes, or any elements contained + // within the webconsole frame. + let webconsoleURL = gDevTools.getToolDefinition("webconsole").url; + if (originalTarget.nodeType !== 1 || + originalTarget.baseURI === webconsoleURL) { + return; + } + + this._lastFocusedElement = originalTarget; + }, + /** * Opens the split console. * @@ -993,6 +1013,7 @@ Toolbox.prototype = { Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, true); this._refreshConsoleDisplay(); this.emit("split-console"); + return this.loadTool("webconsole").then(() => { this.focusConsoleInput(); }); @@ -1009,6 +1030,10 @@ Toolbox.prototype = { Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, false); this._refreshConsoleDisplay(); this.emit("split-console"); + + if (this._lastFocusedElement) { + this._lastFocusedElement.focus(); + } return promise.resolve(); }, @@ -1312,6 +1337,7 @@ Toolbox.prototype = { destroyHost: function() { this.doc.removeEventListener("keypress", this._splitConsoleOnKeypress, false); + this.doc.removeEventListener("focus", this._onFocus, true); return this._host.destroy(); }, @@ -1334,6 +1360,7 @@ Toolbox.prototype = { gDevTools.off("pref-changed", this._prefChanged); + this._lastFocusedElement = null; this._saveSplitConsoleHeight(); this.webconsolePanel.removeEventListener("resize", this._saveSplitConsoleHeight); diff --git a/browser/devtools/webconsole/test/browser.ini b/browser/devtools/webconsole/test/browser.ini index 32ddf719fd6..1627ccc34b6 100644 --- a/browser/devtools/webconsole/test/browser.ini +++ b/browser/devtools/webconsole/test/browser.ini @@ -281,6 +281,7 @@ skip-if = buildapp == 'mulet' [browser_webconsole_scratchpad_panel_link.js] [browser_webconsole_split.js] [browser_webconsole_split_escape_key.js] +[browser_webconsole_split_focus.js] [browser_webconsole_split_persist.js] [browser_webconsole_view_source.js] [browser_webconsole_reflow.js] diff --git a/browser/devtools/webconsole/test/browser_webconsole_split_focus.js b/browser/devtools/webconsole/test/browser_webconsole_split_focus.js new file mode 100644 index 00000000000..3246311074c --- /dev/null +++ b/browser/devtools/webconsole/test/browser_webconsole_split_focus.js @@ -0,0 +1,74 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function test() { + info("Test that the split console state is persisted"); + + let toolbox; + let TEST_URI = "data:text/html;charset=utf-8,

Web Console test for splitting

"; + + Task.spawn(runner).then(finish); + + function* runner() { + info("Opening a tab while there is no user setting on split console pref"); + let {tab} = yield loadTab(TEST_URI); + let target = TargetFactory.forTab(tab); + toolbox = yield gDevTools.showToolbox(target, "inspector"); + + ok(!toolbox.splitConsole, "Split console is hidden by default"); + + info ("Focusing the search box before opening the split console"); + let inspector = toolbox.getPanel("inspector"); + inspector.searchBox.focus(); + + // Use the binding element since inspector.searchBox is a XUL element. + let activeElement = getActiveElement(inspector.panelDoc); + activeElement = activeElement.ownerDocument.getBindingParent(activeElement); + is (activeElement, inspector.searchBox, "Search box is focused"); + + yield toolbox.openSplitConsole(); + + ok(toolbox.splitConsole, "Split console is now visible"); + + // Use the binding element since jsterm.inputNode is a XUL textarea element. + let activeElement = getActiveElement(toolbox.doc); + activeElement = activeElement.ownerDocument.getBindingParent(activeElement); + let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode; + is(activeElement, inputNode, "Split console input is focused by default"); + + yield toolbox.closeSplitConsole(); + + info ("Making sure that the search box is refocused after closing the split console"); + // Use the binding element since inspector.searchBox is a XUL element. + let activeElement = getActiveElement(inspector.panelDoc); + activeElement = activeElement.ownerDocument.getBindingParent(activeElement); + is (activeElement, inspector.searchBox, "Search box is focused"); + + yield toolbox.destroy(); + } + + function getActiveElement(doc) { + let activeElement = doc.activeElement; + while (activeElement && activeElement.contentDocument) { + activeElement = activeElement.contentDocument.activeElement; + } + return activeElement; + } + + function toggleSplitConsoleWithEscape() { + let onceSplitConsole = toolbox.once("split-console"); + let contentWindow = toolbox.frame.contentWindow; + contentWindow.focus(); + EventUtils.sendKey("ESCAPE", contentWindow); + return onceSplitConsole; + } + + function finish() { + toolbox = TEST_URI = null; + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight"); + finishTest(); + } +}