Bug 1379466 - Use auxclick event to trigger new tab on middle click. r=smaug
authorIan Moody <moz-ian@perix.co.uk>
Thu, 18 Apr 2019 12:57:36 +0000
changeset 470160 4e152cfac89d9f368599cadf8b9dc1e87fd06d2b
parent 470159 8ff0d835d90bb268325fd16fa70c8d4a31662127
child 470161 273553e141f1188c807488a12e45ab12c9ccb8a8
push id112843
push useraiakab@mozilla.com
push dateFri, 19 Apr 2019 09:50:22 +0000
treeherdermozilla-inbound@c06f27cbfe40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1379466
milestone68.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 1379466 - Use auxclick event to trigger new tab on middle click. r=smaug So it is still preventDefault()able once non-primary clicks aren't web visible. Don't let browser.js' contentAreaClick handle any non-primary clicks. ClickHandlerChild.jsm handles them first anyway. Can probably rip it out entirely in another bug. Differential Revision: https://phabricator.services.mozilla.com/D26791
browser/actors/ClickHandlerChild.jsm
browser/base/content/browser.js
browser/base/content/test/general/browser_contentAreaClick.js
browser/components/BrowserGlue.jsm
--- a/browser/actors/ClickHandlerChild.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -14,17 +14,18 @@ ChromeUtils.defineModuleGetter(this, "Pr
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
                                "resource://gre/modules/WebNavigationFrames.jsm");
 ChromeUtils.defineModuleGetter(this, "E10SUtils",
                                "resource://gre/modules/E10SUtils.jsm");
 
 class ClickHandlerChild extends ActorChild {
   handleEvent(event) {
-    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
+    if (!event.isTrusted || event.defaultPrevented || event.button == 2 ||
+        (event.type == "click" && event.button == 1)) {
       return;
     }
 
     let originalTarget = event.originalTarget;
     let ownerDoc = originalTarget.ownerDocument;
     if (!ownerDoc) {
       return;
     }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6276,17 +6276,17 @@ function hrefAndLinkNodeForClickEvent(ev
  *
  * @param event
  *        The click event.
  * @param isPanelClick
  *        Whether the event comes from an extension panel.
  * @note default event is prevented if the click is handled.
  */
 function contentAreaClick(event, isPanelClick) {
-  if (!event.isTrusted || event.defaultPrevented || event.button == 2)
+  if (!event.isTrusted || event.defaultPrevented || event.button != 0)
     return;
 
   let [href, linkNode] = hrefAndLinkNodeForClickEvent(event);
   if (!href) {
     // Not a link, handle middle mouse navigation.
     if (event.button == 1 &&
         Services.prefs.getBoolPref("middlemouse.contentLoadURL") &&
         !Services.prefs.getBoolPref("general.autoScroll")) {
--- a/browser/base/content/test/general/browser_contentAreaClick.js
+++ b/browser/base/content/test/general/browser_contentAreaClick.js
@@ -118,46 +118,49 @@ var gTests = [
     preventDefault: true,
   },
 
   {
     desc: "Simple middle click opentab",
     setup() {},
     clean() {},
     event: { button: 1 },
+    wantedEvent: "auxclick",
     targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ],
     expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ],
     preventDefault: true,
   },
 
   {
     desc: "Simple middle click openwin",
     setup() {
       Services.prefs.setBoolPref("browser.tabs.opentabfor.middleclick", false);
     },
     clean() {
       Services.prefs.clearUserPref("browser.tabs.opentabfor.middleclick");
     },
     event: { button: 1 },
+    wantedEvent: "auxclick",
     targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ],
     expectedInvokedMethods: [ "urlSecurityCheck", "openLinkIn" ],
     preventDefault: true,
   },
 
   {
     desc: "Middle mouse paste",
     setup() {
       Services.prefs.setBoolPref("middlemouse.contentLoadURL", true);
       Services.prefs.setBoolPref("general.autoScroll", false);
     },
     clean() {
       Services.prefs.clearUserPref("middlemouse.contentLoadURL");
       Services.prefs.clearUserPref("general.autoScroll");
     },
     event: { button: 1 },
+    wantedEvent: "auxclick",
     targets: [ "emptylink" ],
     expectedInvokedMethods: [ "middleMousePaste" ],
     preventDefault: true,
   },
 
 ];
 
 // Array of method names that will be replaced in the new window.
@@ -203,19 +206,23 @@ function test() {
       }, gTestWin.content, true);
     }, gTestWin);
   });
 }
 
 // Click handler used to steal click events.
 var gClickHandler = {
   handleEvent(event) {
+    if (event.type == "click" && event.button != 0) {
+      return;
+    }
     let linkId = event.target.id || event.target.localName;
-    is(event.type, "click",
-       gCurrentTest.desc + ":Handler received a click event on " + linkId);
+    let wantedEvent = gCurrentTest.wantedEvent || "click";
+    is(event.type, wantedEvent,
+       `${gCurrentTest.desc}:Handler received a ${wantedEvent} event on ${linkId}`);
 
     let isPanelClick = linkId == "panellink";
     gTestWin.contentAreaClick(event, isPanelClick);
     let prevent = event.defaultPrevented;
     is(prevent, gCurrentTest.preventDefault,
        gCurrentTest.desc + ": event.defaultPrevented is correct (" + prevent + ")");
 
     // Check that all required methods have been called.
@@ -236,16 +243,17 @@ var gClickHandler = {
 
     executeSoon(runNextTest);
   },
 };
 
 function setupTestBrowserWindow() {
   // Steal click events and don't propagate them.
   gTestWin.addEventListener("click", gClickHandler, true);
+  gTestWin.addEventListener("auxclick", gClickHandler, true);
 
   // Replace methods.
   gReplacedMethods.forEach(function(methodName) {
     let targetObj = methodName == "getShortcutOrURIAndPostData" ? UrlbarUtils : gTestWin;
     sinon.stub(targetObj, methodName).returnsArg(0);
   });
 
   // Inject links in content.
@@ -281,20 +289,21 @@ function runNextTest() {
   }
 
   // Move to next target.
   sinon.resetHistory();
   let target = gCurrentTest.targets.shift();
 
   info(gCurrentTest.desc + ": testing " + target);
 
-  // Fire click event.
+  // Fire (aux)click event.
   let targetElt = gTestWin.content.document.getElementById(target);
   ok(targetElt, gCurrentTest.desc + ": target is valid (" + targetElt.id + ")");
   EventUtils.synthesizeMouseAtCenter(targetElt, gCurrentTest.event, gTestWin.content);
 }
 
 function finishTest() {
   info("Restoring browser...");
   gTestWin.removeEventListener("click", gClickHandler, true);
+  gTestWin.removeEventListener("auxclick", gClickHandler, true);
   gTestWin.close();
   finish();
 }
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -72,16 +72,17 @@ let ACTORS = {
     },
   },
 
   ClickHandler: {
     child: {
       module: "resource:///actors/ClickHandlerChild.jsm",
       events: {
         "click": {capture: true, mozSystemGroup: true},
+        "auxclick": {capture: true, mozSystemGroup: true},
       },
     },
   },
 
   ContextMenu: {
     child: {
       module: "resource:///actors/ContextMenuChild.jsm",
       events: {