diff --git a/browser/devtools/framework/test/Makefile.in b/browser/devtools/framework/test/Makefile.in
index 9befc3ccea0d..9d9ab0d9b2bb 100644
--- a/browser/devtools/framework/test/Makefile.in
+++ b/browser/devtools/framework/test/Makefile.in
@@ -22,6 +22,7 @@ MOCHITEST_BROWSER_FILES = \
browser_toolbox_tool_ready.js \
browser_toolbox_sidebar.js \
browser_toolbox_window_shortcuts.js \
+ browser_toolbox_tabsswitch_shortcuts.js \
browser_toolbox_window_title_changes.js \
browser_toolbox_options.js \
browser_toolbox_options_disablejs.js \
diff --git a/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js b/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
new file mode 100644
index 000000000000..e8b4c9463d38
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let Toolbox = devtools.Toolbox;
+
+let toolbox, toolIDs, idIndex, secondTime = false,
+ reverse = false, nextKey = null, prevKey = null;
+
+function test() {
+ waitForExplicitFinish();
+
+ addTab("about:blank", function() {
+ toolIDs = [tool.id for (tool of gDevTools.getToolDefinitionArray())];
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ idIndex = 0;
+ gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
+ .then(testShortcuts);
+ });
+}
+
+function testShortcuts(aToolbox, aIndex) {
+ if (aIndex == toolIDs.length) {
+ aIndex = 0;
+ if (secondTime) {
+ secondTime = false;
+ reverse = true;
+ aIndex = toolIDs.length - 2;
+ }
+ else {
+ secondTime = true;
+ }
+ }
+ else if (aIndex == -1) {
+ aIndex = toolIDs.length - 1;
+ if (secondTime) {
+ tidyUp();
+ return;
+ }
+ secondTime = true;
+ }
+
+ toolbox = aToolbox;
+ if (!nextKey) {
+ nextKey = toolbox.doc.getElementById("toolbox-next-tool-key")
+ .getAttribute("key");
+ prevKey = toolbox.doc.getElementById("toolbox-previous-tool-key")
+ .getAttribute("key");
+ }
+ info("Toolbox fired a `ready` event");
+
+ toolbox.once("select", onSelect);
+
+ if (aIndex != null) {
+ // This if block is to allow the call of onSelect without shortcut press for
+ // the first time. That happens because on opening of toolbox, one tool gets
+ // selected atleast.
+
+ let key = (reverse ? prevKey: nextKey);
+ let modifiers = {
+ accelKey: true
+ };
+ idIndex = aIndex;
+ info("Testing shortcut to switch to tool " + aIndex + ":" + toolIDs[aIndex] +
+ " using key " + key);
+ EventUtils.synthesizeKey(key, modifiers, toolbox.doc.defaultView);
+ }
+}
+
+function onSelect(event, id) {
+ info("toolbox-select event from " + id);
+
+ is(toolIDs.indexOf(id), idIndex,
+ "Correct tool is selected on pressing the shortcut for " + id);
+ // Execute soon to reset the stack trace.
+ executeSoon(() => {
+ testShortcuts(toolbox, idIndex + (reverse ? -1: 1));
+ });
+}
+
+function tidyUp() {
+ toolbox.destroy().then(function() {
+ gBrowser.removeCurrentTab();
+
+ toolbox = toolIDs = idIndex = Toolbox = secondTime = reverse = nextKey =
+ prevKey = null;
+ finish();
+ });
+}
diff --git a/browser/devtools/framework/toolbox.js b/browser/devtools/framework/toolbox.js
index d1a776aaa4e8..efece2c12257 100644
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -207,6 +207,7 @@ Toolbox.prototype = {
this._buildTabs();
this._buildButtons();
this._addKeysToWindow();
+ this._addToolSwitchingKeys();
this._telemetry.toolOpened("toolbox");
@@ -230,6 +231,13 @@ Toolbox.prototype = {
}.bind(this, "options"), true);
},
+ _addToolSwitchingKeys: function TBOX__addToolSwitchingKeys() {
+ 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");
+ prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
+ },
+
/**
* Adds the keys and commands to the Toolbox Window in window mode.
*/
@@ -539,6 +547,26 @@ Toolbox.prototype = {
});
},
+ /**
+ * Loads the tool next to the currently selected tool.
+ */
+ selectNextTool: function TBOX_selectNextTool() {
+ let selected = this.doc.querySelector(".devtools-tab[selected]");
+ let next = selected.nextSibling || selected.parentNode.firstChild;
+ let tool = next.getAttribute("toolid");
+ return this.selectTool(tool);
+ },
+
+ /**
+ * Loads the tool just left to the currently selected tool.
+ */
+ selectPreviousTool: function TBOX_selectPreviousTool() {
+ let selected = this.doc.querySelector(".devtools-tab[selected]");
+ let previous = selected.previousSibling || selected.parentNode.lastChild;
+ let tool = previous.getAttribute("toolid");
+ return this.selectTool(tool);
+ },
+
/**
* Highlights the tool's tab if it is not the currently selected tool.
*
diff --git a/browser/devtools/framework/toolbox.xul b/browser/devtools/framework/toolbox.xul
index e1cd2f7f8d97..aed728ee51b3 100644
--- a/browser/devtools/framework/toolbox.xul
+++ b/browser/devtools/framework/toolbox.xul
@@ -23,6 +23,14 @@
key="&toolboxOptionsButton.key;"
oncommand="void(0);"
modifiers="shift, accel"/>
+
+
diff --git a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
index 89b982d089a8..13568c9c69fa 100644
--- a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
@@ -9,6 +9,8 @@
+
+