Bug 465284: Open link in new tab [r=mark.finkle]
authorBen Combee <combee@mozilla.com>
Fri, 23 Oct 2009 12:02:40 -0400
changeset 65703 4dd89bfaf8f0b5bbce57ef37bbb9068edbab0802
parent 65702 1dbd7b0bed258eaeacc486ca3162fcb7013a5a82
child 65704 8191e6a60722eec940c8aa0df206072ad612b781
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmark
bugs465284
Bug 465284: Open link in new tab [r=mark.finkle]
mobile/chrome/content/InputHandler.js
mobile/chrome/content/Util.js
mobile/chrome/content/browser.js
--- a/mobile/chrome/content/InputHandler.js
+++ b/mobile/chrome/content/InputHandler.js
@@ -401,19 +401,20 @@ InputHandler.EventInfo.prototype = {
  *
  * Between mousedown and mouseup, MouseModule incrementally drags and updates
  * the dragger accordingly, and also determines whether a [double-]click occured
  * (based on whether the input moves have moved outside of a certain drag disk
  * centered at the mousedown position).  If a [double-]click happened, any
  * customClicker will be notified.  The customClicker must support the following
  * interface:
  *
- *   singleClick(cx, cy)
+ *   singleClick(cx, cy, modifiers)
  *     Signals a single (as opposed to double) click occured at client
- *     coordinates cx, cy
+ *     coordinates cx, cy.  Specify optional modifiers to include
+ *     shift-keys with click.
  *
  *   doubleClick(cx1, cy1, cx2, cy2)
  *     Signals a doubleclick occured, with the first click at client coordinates
  *     cx1, cy1, and second click at client coordinates cx2, cy2.
  *
  * There is a default dragger in case a scrollable element is dragged --- see
  * the defaultDragger prototype property.  There is no default clicker.
  */
@@ -684,17 +685,24 @@ MouseModule.prototype = {
     //dump('doing single click with ' + this._downUpEvents.length + '\n');
     //for (let i = 0; i < this._downUpEvents.length; ++i)
     //  dump('      ' + this._downUpEvents[i].event.type
     //       + " :: " + this._downUpEvents[i].event.button
     //       + " :: " + this._downUpEvents[i].event.detail + '\n');/**/
 
     let ev = this._downUpEvents[1].event;
     this._cleanClickBuffer(2);
-    this._clicker.singleClick(ev.clientX, ev.clientY);
+
+    // borrowed from nsIDOMNSEvent.idl
+    let modifiers =
+      (ev.altKey   ? Ci.nsIDOMNSEvent.ALT_MASK     : 0) |
+      (ev.ctrlKey  ? Ci.nsIDOMNSEvent.CONTROL_MASK : 0) |
+      (ev.shiftKey ? Ci.nsIDOMNSEvent.SHIFT_MASK   : 0) |
+      (ev.metaKey  ? Ci.nsIDOMNSEvent.META_MASK    : 0);
+    this._clicker.singleClick(ev.clientX, ev.clientY, modifiers);
   },
 
   /**
    * Endpoint of _commitAnotherClick().  Finalize a double click and tell the clicker.
    */
   _doDoubleClick: function _doDoubleClick() {
     //dump('doing double click with ' + this._downUpEvents.length + '\n');
     //for (let i = 0; i < this._downUpEvents.length; ++i)
--- a/mobile/chrome/content/Util.js
+++ b/mobile/chrome/content/Util.js
@@ -80,16 +80,38 @@ let Util = {
   /** Executes aFunc after other events have been processed. */
   executeSoon: function executeSoon(aFunc) {
     let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
     tm.mainThread.dispatch({
       run: function() {
         aFunc();
       }
     }, Ci.nsIThread.DISPATCH_NORMAL);
+  },
+
+  getHrefForElement: function getHrefForElement(target) {
+    // XXX: This is kind of a hack to work around a Gecko bug (see bug 266932)
+    // We're going to walk up the DOM looking for a parent link node.
+    // This shouldn't be necessary, but we're matching the existing behaviour for left click
+
+    let link = null;
+    while (target) {
+      if (target instanceof HTMLAnchorElement || 
+          target instanceof HTMLAreaElement ||
+          target instanceof HTMLLinkElement) {
+          if (target.hasAttribute("href"))
+            link = target;
+      }
+      target = target.parentNode;
+    }
+
+    if (link && link.hasAttribute("href"))
+      return link.href;
+    else
+      return null;
   }
 
 };
 
 
 /**
  * Simple Point class.
  *
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1519,19 +1519,28 @@ ContentCustomClicker.prototype = {
     },
 
     mouseDown: function mouseDown(cX, cY) {
     },
 
     mouseUp: function mouseUp(cX, cY) {
     },
 
-    singleClick: function singleClick(cX, cY) {
-      this._dispatchMouseEvent("mousedown", cX, cY);
-      this._dispatchMouseEvent("mouseup", cX, cY);
+    singleClick: function singleClick(cX, cY, modifiers) {
+      if (modifiers == 0) {
+        this._dispatchMouseEvent("mousedown", cX, cY);
+        this._dispatchMouseEvent("mouseup", cX, cY);
+      }
+      else if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
+        let [elementX, elementY] = Browser.transformClientToBrowser(cX, cY);
+        let element = Browser.elementFromPoint(elementX, elementY);
+        let uri = Util.getHrefForElement(element);
+        if (uri)
+          Browser.addTab(uri, false);
+      }
     },
 
     doubleClick: function doubleClick(cX1, cY1, cX2, cY2) {
       if (!Browser.zoomToPoint(cX2, cY2))
         Browser.zoomFromPoint(cX2, cY2);
     },
 
     toString: function toString() {