Bug 892244 - Ctrl+[ / Ctrl+] to switch devtools tabs, r=harth
authorGirish Sharma <scrapmachines@gmail.com>
Sat, 20 Jul 2013 19:06:43 +0530
changeset 151608 ed6e47fb4bfedbd974cd5a7fd895fea788259e01
parent 151607 7c3d5bdc569c47fe3e61a2cdcb09331b344225c2
child 151609 de691ff8c67791447de2e1d1cea6de941fa0544c
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersharth
bugs892244
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 892244 - Ctrl+[ / Ctrl+] to switch devtools tabs, r=harth
browser/devtools/framework/test/Makefile.in
browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
browser/devtools/framework/toolbox.js
browser/devtools/framework/toolbox.xul
browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
--- a/browser/devtools/framework/test/Makefile.in
+++ b/browser/devtools/framework/test/Makefile.in
@@ -17,16 +17,17 @@ MOCHITEST_BROWSER_FILES = \
 		browser_toolbox_dynamic_registration.js \
 		browser_toolbox_hosts.js \
 		browser_toolbox_ready.js \
 		browser_toolbox_select_event.js \
 		browser_target_events.js \
 		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 \
 		browser_toolbox_options_disablejs.html \
 		browser_toolbox_options_disablejs_iframe.html \
 		browser_toolbox_highlight.js \
 		browser_toolbox_raise.js \
 		$(NULL)
new file mode 100644
--- /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();
+  });
+}
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -202,16 +202,17 @@ Toolbox.prototype = {
         let closeButton = this.doc.getElementById("toolbox-close");
         closeButton.addEventListener("command", this.destroy, true);
 
         this._buildDockButtons();
         this._buildOptions();
         this._buildTabs();
         this._buildButtons();
         this._addKeysToWindow();
+        this._addToolSwitchingKeys();
 
         this._telemetry.toolOpened("toolbox");
 
         this.selectTool(this._defaultToolId).then(function(panel) {
           this.emit("ready");
           deferred.resolve();
         }.bind(this));
       };
@@ -225,16 +226,23 @@ Toolbox.prototype = {
 
   _buildOptions: function TBOX__buildOptions() {
     let key = this.doc.getElementById("toolbox-options-key");
     key.addEventListener("command", function(toolId) {
       this.selectTool(toolId);
     }.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.
    */
   _addKeysToWindow: function TBOX__addKeysToWindow() {
     if (this.hostType != Toolbox.HostType.WINDOW) {
       return;
     }
     let doc = this.doc.defaultView.parent.document;
@@ -535,16 +543,36 @@ Toolbox.prototype = {
     return this.loadTool(id).then((panel) => {
       this.emit("select", id);
       this.emit(id + "-selected", panel);
       return panel;
     });
   },
 
   /**
+   * 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.
    *
    * @param {string} id
    *        The id of the tool to highlight
    */
   highlightTool: function TBOX_highlightTool(id) {
     let tab = this.doc.getElementById("toolbox-tab-" + id);
     tab && tab.classList.add("highlighted");
--- a/browser/devtools/framework/toolbox.xul
+++ b/browser/devtools/framework/toolbox.xul
@@ -18,16 +18,24 @@
 
   <commandset id="editMenuCommands"/>
   <keyset id="editMenuKeys"/>
   <keyset id="toolbox-keyset">
     <key id="toolbox-options-key"
          key="&toolboxOptionsButton.key;"
          oncommand="void(0);"
          modifiers="shift, accel"/>
+    <key id="toolbox-next-tool-key"
+         key="&toolboxNextTool.key;"
+         oncommand="void(0);"
+         modifiers="accel"/>
+    <key id="toolbox-previous-tool-key"
+         key="&toolboxPreviousTool.key;"
+         oncommand="void(0);"
+         modifiers="accel"/>
   </keyset>
 
   <notificationbox id="toolbox-notificationbox" flex="1">
     <toolbar class="devtools-tabbar">
 #ifdef XP_MACOSX
       <hbox id="toolbox-controls">
         <toolbarbutton id="toolbox-close"
                        class="devtools-closebutton"
--- a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
@@ -4,16 +4,18 @@
 
 <!-- LOCALIZATION NOTE : FILE This file contains the Toolbox strings -->
 <!-- LOCALIZATION NOTE : FILE Do not translate key -->
 
 <!ENTITY closeCmd.key  "W">
 
 <!ENTITY toolboxCloseButton.tooltip    "Close Developer Tools">
 <!ENTITY toolboxOptionsButton.key      "O">
+<!ENTITY toolboxNextTool.key           "]">
+<!ENTITY toolboxPreviousTool.key       "[">
 
 <!-- LOCALIZATION NOTE (options.context.advancedSettings): This is the label for
   -  the heading of the advanced settings group in the options panel. -->
 <!ENTITY options.context.advancedSettings "Advanced settings">
 
 <!-- LOCALIZATION NOTE (options.context.requiresRestart2): This is the requires
   -  restart label at right of settings that require a browser restart to be
   -  effective. -->