Bug 1327971 - Add support to show list of frames on key-press of "Alt+Down" on the "iframes" button. r=Honza draft
authorabhinav <abhinav.koppula@gmail.com>
Sun, 10 Sep 2017 15:41:35 +0530
changeset 663145 8639a80dcdec5113f8228ebb5dbd9a17dee900b6
parent 662738 bda524beac249b64aa36016800502a34073bf35a
child 731117 01328b72eb397adf73f1d398efa316d2cd4d1ff0
push id79346
push userbmo:abhinav.koppula@gmail.com
push dateTue, 12 Sep 2017 18:38:59 +0000
reviewersHonza
bugs1327971
milestone57.0a1
Bug 1327971 - Add support to show list of frames on key-press of "Alt+Down" on the "iframes" button. r=Honza MozReview-Commit-ID: EZFG4br17mC
devtools/client/framework/components/toolbox-toolbar.js
devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
devtools/client/framework/toolbox.js
devtools/client/locales/en-US/toolbox.properties
--- a/devtools/client/framework/components/toolbox-toolbar.js
+++ b/devtools/client/framework/components/toolbox-toolbar.js
@@ -101,30 +101,40 @@ function renderToolboxButtons({toolboxBu
   });
 
   if (visibleButtons.length === 0) {
     return null;
   }
 
   return div({id: `toolbox-buttons-${isStart ? "start" : "end"}`},
     ...visibleButtons.map(command => {
-      const {id, description, onClick, isChecked, className: buttonClass} = command;
+      const {
+        id,
+        description,
+        onClick,
+        isChecked,
+        className: buttonClass,
+        onKeyDown
+      } = command;
       return button({
         id,
         title: description,
         className: (
           "command-button command-button-invertable devtools-button "
           + buttonClass + (isChecked ? " checked" : "")
         ),
         onClick: (event) => {
           onClick(event);
           focusButton(id);
         },
         onFocus: () => focusButton(id),
-        tabIndex: id === focusedButton ? "0" : "-1"
+        tabIndex: id === focusedButton ? "0" : "-1",
+        onKeyDown: (event) => {
+          onKeyDown(event);
+        }
       });
     }),
     isStart ? div({className: "devtools-separator"}) : null
   );
 }
 
 /**
  * The options button is a ToolboxTab just like in the ToolboxTabs component. However
--- a/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
+++ b/devtools/client/framework/test/browser_toolbox_window_title_frame_select.js
@@ -11,16 +11,18 @@
  * Check that the detached devtools window title is not updated when switching
  * the selected frame. Also check that frames command button has 'open'
  * attribute set when the list of frames is opened.
  */
 
 var {Toolbox} = require("devtools/client/framework/toolbox");
 const URL = URL_ROOT + "browser_toolbox_window_title_frame_select_page.html";
 const IFRAME_URL = URL_ROOT + "browser_toolbox_window_title_changes_page.html";
+const {LocalizationHelper} = require("devtools/shared/l10n");
+const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 add_task(function* () {
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 
   yield addTab(URL);
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   let toolbox = yield gDevTools.showToolbox(target, null,
     Toolbox.HostType.BOTTOM);
@@ -36,19 +38,22 @@ add_task(function* () {
 
   is(getTitle(), `Developer Tools - Page title - ${URL}`,
     "Devtools title correct after switching to detached window host");
 
   // Wait for tick to avoid unexpected 'popuphidden' event, which
   // blocks the frame popup menu opened below. See also bug 1276873
   yield waitForTick();
 
+  let btn = toolbox.doc.getElementById("command-button-frames");
+
+  yield testShortcutToOpenFrames(btn, toolbox);
+
   // Open frame menu and wait till it's available on the screen.
   // Also check 'open' attribute on the command button.
-  let btn = toolbox.doc.getElementById("command-button-frames");
   ok(!btn.classList.contains("checked"), "The checked class must not be present");
   let menu = toolbox.showFramesMenu({target: btn});
   yield once(menu, "open");
 
   ok(btn.classList.contains("checked"), "The checked class must be set");
 
   // Verify that the frame list menu is populated
   let frames = menu.items;
@@ -87,8 +92,30 @@ add_task(function* () {
   Services.prefs.clearUserPref("devtools.toolbox.sideEnabled");
   Services.prefs.clearUserPref("devtools.command-button-frames.enabled");
   finish();
 });
 
 function getTitle() {
   return Services.wm.getMostRecentWindow("devtools:toolbox").document.title;
 }
+
+function* testShortcutToOpenFrames(btn, toolbox) {
+  info("Tests if shortcut Alt+Down opens the frames");
+  // focus the button so that keyPress can be performed
+  btn.focus();
+  // perform keyPress - Alt+Down
+  let shortcut = L10N.getStr("toolbox.showFrames.key");
+  synthesizeKeyShortcut(shortcut, toolbox.win);
+
+  // wait for 200 ms for UI to render
+  yield wait(200);
+
+  // btn should now have the checked class set
+  ok(btn.classList.contains("checked"), "The checked class must be set");
+
+  // pressing Esc should hide the menu again
+  synthesizeKeyShortcut("Esc", toolbox.win);
+  yield wait(200);
+
+  // btn shouldn't have the checked class set
+  ok(!btn.classList.contains("checked"), "The checked class must not be set");
+}
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -108,16 +108,18 @@ function Toolbox(target, selectedTool, h
   this.frameMap = new Map();
   this.selectedFrameId = null;
 
   this._toolRegistered = this._toolRegistered.bind(this);
   this._toolUnregistered = this._toolUnregistered.bind(this);
   this._refreshHostTitle = this._refreshHostTitle.bind(this);
   this._toggleNoAutohide = this._toggleNoAutohide.bind(this);
   this.showFramesMenu = this.showFramesMenu.bind(this);
+  this.handleKeyDownOnFramesButton = this.handleKeyDownOnFramesButton.bind(this);
+  this.showFramesMenuOnKeyDown = this.showFramesMenuOnKeyDown.bind(this);
   this._updateFrames = this._updateFrames.bind(this);
   this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
   this.destroy = this.destroy.bind(this);
   this.highlighterUtils = getHighlighterUtils(this);
   this._highlighterReady = this._highlighterReady.bind(this);
   this._highlighterHidden = this._highlighterHidden.bind(this);
   this._applyCacheSettings = this._applyCacheSettings.bind(this);
   this._applyServiceWorkersTestingSettings =
@@ -682,28 +684,43 @@ Toolbox.prototype = {
    *                      the button based on the target. If the target don't support
    *                      the button feature, this method should return false.
    * @property {Function} isChecked - Optional function called to known if the button
    *                      is toggled or not. The function should return true when
    *                      the button should be displayed as toggled on.
    */
   _createButtonState: function (options) {
     let isCheckedValue = false;
-    const { id, className, description, onClick, isInStartContainer, setup, teardown,
-            isTargetSupported, isChecked } = options;
+    const {
+      id,
+      className,
+      description,
+      onClick,
+      isInStartContainer,
+      setup,
+      teardown,
+      isTargetSupported,
+      isChecked,
+      onKeyDown
+    } = options;
     const toolbox = this;
     const button = {
       id,
       className,
       description,
       onClick(event) {
         if (typeof onClick == "function") {
           onClick(event, toolbox);
         }
       },
+      onKeyDown(event) {
+        if (typeof onKeyDown == "function") {
+          onKeyDown(event, toolbox);
+        }
+      },
       isTargetSupported,
       get isChecked() {
         if (typeof isChecked == "function") {
           return isChecked(toolbox);
         }
         return isCheckedValue;
       },
       set isChecked(value) {
@@ -1200,17 +1217,18 @@ Toolbox.prototype = {
    */
   _buildFrameButton() {
     this.frameButton = this._createButtonState({
       id: "command-button-frames",
       description: L10N.getStr("toolbox.frames.tooltip"),
       onClick: this.showFramesMenu,
       isTargetSupported: target => {
         return target.activeTab && target.activeTab.traits.frames;
-      }
+      },
+      onKeyDown: this.handleKeyDownOnFramesButton
     });
 
     return this.frameButton;
   },
 
   /**
    * Button that disables/enables auto-hiding XUL pop-ups. When enabled, XUL
    * pop-ups will not automatically close when they lose focus.
@@ -2049,16 +2067,33 @@ Toolbox.prototype = {
     let screenX = target.ownerDocument.defaultView.mozInnerScreenX;
     let screenY = target.ownerDocument.defaultView.mozInnerScreenY;
     menu.popup(rect.left + screenX, rect.bottom + screenY, this);
 
     return menu;
   },
 
   /**
+   * Handle keyDown event on 'frames' button to show available frames
+   */
+  handleKeyDownOnFramesButton: function (event) {
+    this.shortcuts.on(L10N.getStr("toolbox.showFrames.key"),
+      this.showFramesMenuOnKeyDown);
+  },
+
+  /**
+   * Show 'frames' menu on key down
+   */
+  showFramesMenuOnKeyDown: function (name, event) {
+    if (event.target.id == "command-button-frames") {
+      this.showFramesMenu(event);
+    }
+  },
+
+  /**
    * Select a frame by sending 'switchToFrame' packet to the backend.
    */
   onSelectFrame: function (frameId) {
     // Send packet to the backend to select specified frame and
     // wait for 'frameUpdate' event packet to update the UI.
     let packet = {
       to: this._target.form.actor,
       type: "switchToFrame",
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -159,16 +159,20 @@ toolbox.minimize.key=CmdOrCtrl+Shift+U
 # Key shortcut used to move the toolbox in bottom or side of the browser window
 toolbox.toggleHost.key=CmdOrCtrl+Shift+D
 
 # LOCALIZATION NOTE (toolbox.frames.tooltip): This is the label for
 # the iframes menu list that appears only when the document has some.
 # It allows you to switch the context of the whole toolbox.
 toolbox.frames.tooltip=Select an iframe as the currently targeted document
 
+# LOCALIZATION NOTE (toolbox.showFrames.key)
+# Key shortcut used to show frames menu when 'frames' button is focused
+toolbox.showFrames.key=Alt+Down
+
 # LOCALIZATION NOTE (toolbox.noautohide.tooltip): This is the label for
 # the button to force the popups/panels to stay visible on blur.
 # This is only visible in the browser toolbox as it is meant for
 # addon developers and Firefox contributors.
 toolbox.noautohide.tooltip=Disable popup auto hide
 
 # LOCALIZATION NOTE (toolbox.closebutton.tooltip): This is the tooltip for
 # the close button the developer tools toolbox.