Bug 1509767: Support keyboard activation (space/enter) of the Back/Forward buttons when focused. r=Gijs
authorJames Teh <jteh@mozilla.com>
Thu, 20 Dec 2018 05:33:32 +0000
changeset 511853 dbaf671d5e84
parent 511852 fb3f227c4f29
child 511854 49a28ec78bfe
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1509767
milestone66.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 1509767: Support keyboard activation (space/enter) of the Back/Forward buttons when focused. r=Gijs When focused, the Back and Forward buttons previously couldn't be activated by pressing space or enter. Although they do have a command event handler, they have type="menu", which means the command event is not fired for key presses by default. Since these buttons are special (in that they have type="menu" and a command event), this change implements specific keyboard behavior for these buttons. Differential Revision: https://phabricator.services.mozilla.com/D12868
browser/base/content/browser.js
browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -469,43 +469,57 @@ const gClickAndHoldListenersOnElement = 
   },
 
   _cancelHold(aButton) {
     clearTimeout(this._timers.get(aButton));
     aButton.removeEventListener("mouseout", this);
     aButton.removeEventListener("mouseup", this);
   },
 
+  _keypressHandler(aEvent) {
+    if (aEvent.key == " " || aEvent.key == "Enter") {
+      // Normally, command events get fired for keyboard activation. However,
+      // we've set type="menu", so that doesn't happen. Handle this the same
+      // way we handle clicks.
+      aEvent.target.click();
+    }
+  },
+
   handleEvent(e) {
     switch (e.type) {
       case "mouseout":
         this._mouseoutHandler(e);
         break;
       case "mousedown":
         this._mousedownHandler(e);
         break;
       case "click":
         this._clickHandler(e);
         break;
       case "mouseup":
         this._mouseupHandler(e);
         break;
+      case "keypress":
+        this._keypressHandler(e);
+        break;
     }
   },
 
   remove(aButton) {
     aButton.removeEventListener("mousedown", this, true);
     aButton.removeEventListener("click", this, true);
+    aButton.removeEventListener("keypress", this, true);
   },
 
   add(aElm) {
     this._timers.delete(aElm);
 
     aElm.addEventListener("mousedown", this, true);
     aElm.addEventListener("click", this, true);
+    aElm.addEventListener("keypress", this, true);
   },
 };
 
 const gSessionHistoryObserver = {
   observe(subject, topic, data) {
     if (topic != "browser:purge-session-history")
       return;
 
--- a/browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js
+++ b/browser/base/content/test/keyboard/browser_toolbarButtonKeyPress.js
@@ -12,16 +12,29 @@
 // when a user is navigating with the keyboard. This function forces focus as
 // is done during keyboard navigation.
 function forceFocus(aElem) {
   aElem.setAttribute("tabindex", "-1");
   aElem.focus();
   aElem.removeAttribute("tabindex");
 }
 
+function waitForLocationChange() {
+  let promise = new Promise(resolve => {
+    let wpl = {
+      onLocationChange(aWebProgress, aRequest, aLocation) {
+        gBrowser.removeProgressListener(wpl);
+        resolve();
+      },
+    };
+    gBrowser.addProgressListener(wpl);
+  });
+  return promise;
+}
+
 // Test activation of the app menu button from the keyboard.
 // The app menu should appear and focus should move inside it.
 add_task(async function testAppMenuButtonPress() {
   let button = document.getElementById("PanelUI-menu-button");
   forceFocus(button);
   let focused = BrowserTestUtils.waitForEvent(window.PanelUI.mainView, "focus", true);
   EventUtils.synthesizeKey(" ");
   await focused;
@@ -98,16 +111,38 @@ add_task(async function testPageActionsB
     await focused;
     ok(true, "Focus inside Page Actions menu after toolbar button pressed");
     let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
     view.closest("panel").hidePopup();
     await hidden;
   });
 });
 
+// Test activation of the Back and Forward buttons from the keyboard.
+add_task(async function testBackForwardButtonPress() {
+  await BrowserTestUtils.withNewTab("https://example.com/1", async function(aBrowser) {
+    BrowserTestUtils.loadURI(aBrowser, "https://example.com/2");
+
+    await BrowserTestUtils.browserLoaded(aBrowser);
+    let backButton = document.getElementById("back-button");
+    forceFocus(backButton);
+    let onLocationChange = waitForLocationChange();
+    EventUtils.synthesizeKey(" ");
+    await onLocationChange;
+    ok(true, "Location changed after back button pressed");
+
+    let forwardButton = document.getElementById("forward-button");
+    forceFocus(forwardButton);
+    onLocationChange = waitForLocationChange();
+    EventUtils.synthesizeKey(" ");
+    await onLocationChange;
+    ok(true, "Location changed after forward button pressed");
+  });
+});
+
 // Test activation of the Send Tab to Device button from the keyboard.
 // This is a page action button built at runtime by PageActions.
 // The Send Tab to Device menu should appear and focus should move inside it.
 add_task(async function testSendTabToDeviceButtonPress() {
   await BrowserTestUtils.withNewTab("https://example.com", async function() {
     PageActions.actionForID("sendToDevice").pinnedToUrlbar = true;
     let button = document.getElementById("pageAction-urlbar-sendToDevice");
     forceFocus(button);