Bug 674791 - dragging the left text selection marker down selects to the bottom of the page [r=wjohnston, a=johnath]
authorMark Finkle <mfinkle@mozilla.com>
Thu, 04 Aug 2011 15:26:42 -0400
changeset 72765 9d4a445ca2354d68cb5c1d95f53bf2a2f71cb014
parent 72764 d05ba21b434bdabb08f4e632eb2eae61f2754e85
child 72766 ddc2fd44b7fa91c692d308b7de817ac79092448e
push id298
push usermfinkle@mozilla.com
push dateWed, 10 Aug 2011 01:44:01 +0000
treeherdermozilla-aurora@9d4a445ca235 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswjohnston, johnath
bugs674791
milestone7.0a2
Bug 674791 - dragging the left text selection marker down selects to the bottom of the page [r=wjohnston, a=johnath]
mobile/chrome/content/content.js
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -1346,18 +1346,18 @@ var SelectionHandler = {
     let json = aMessage.json;
 
     switch (aMessage.name) {
       case "Browser:SelectionStart": {
         // Clear out the text cache
         this.selectedText = "";
 
         // if this is an iframe, dig down to find the document that was clicked
-        let x = json.x;
-        let y = json.y;
+        let x = json.x - scrollOffset.x;
+        let y = json.y - scrollOffset.y;
         let offset = scrollOffset;
         let elem = utils.elementFromPoint(x, y, true, false);
         while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) {
           // adjust client coordinates' origin to be top left of iframe viewport
           let rect = elem.getBoundingClientRect();
           scrollOffset = ContentScroll.getScrollOffset(elem.ownerDocument.defaultView);
           offset.x += rect.left;
           x -= rect.left;
@@ -1374,18 +1374,18 @@ var SelectionHandler = {
         let currentDocShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
 
         // Remove any previous selected or created ranges. Tapping anywhere on a
         // page will create an empty range.
         let selection = contentWindow.getSelection();
         selection.removeAllRanges();
 
         // Position the caret using a fake mouse click
-        utils.sendMouseEventToWindow("mousedown", x - scrollOffset.x, y - scrollOffset.y, 0, 1, 0, true);
-        utils.sendMouseEventToWindow("mouseup", x - scrollOffset.x, y - scrollOffset.y, 0, 1, 0, true);
+        utils.sendMouseEventToWindow("mousedown", x, y, 0, 1, 0, true);
+        utils.sendMouseEventToWindow("mouseup", x, y, 0, 1, 0, true);
 
         // Select the word nearest the caret
         try {
           let selcon = currentDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay).QueryInterface(Ci.nsISelectionController);
           selcon.wordMove(false, false);
           selcon.wordMove(true, true);
         } catch(e) {
           // If we couldn't select the word at the given point, bail
@@ -1418,17 +1418,17 @@ var SelectionHandler = {
 
       case "Browser:SelectionEnd": {
         let tap = { x: json.x - this.cache.offset.x, y: json.y - this.cache.offset.y };
         pointInSelection = (tap.x > this.cache.rect.left && tap.x < this.cache.rect.right) && (tap.y > this.cache.rect.top && tap.y < this.cache.rect.bottom);
 
         try {
           // The selection might already be gone
           if (this.contentWindow)
-            this.contentWindow.getSelection().collapseToStart();
+            this.contentWindow.getSelection().removeAllRanges();
           this.contentWindow = null;
         } catch(e) {}
 
         if (pointInSelection && this.selectedText.length) {
           let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
           clipboard.copyString(this.selectedText);
           sendAsyncMessage("Browser:SelectionCopied", { succeeded: true });
         } else {
@@ -1445,29 +1445,34 @@ var SelectionHandler = {
         let elemUnder = elementFromPoint(json.x - scrollOffset.x, json.y - scrollOffset.y);
         if (elemUnder && elemUnder instanceof Ci.nsIDOMHTMLInputElement || elemUnder instanceof Ci.nsIDOMHTMLTextAreaElement)
           return;
 
         // Limit the selection to the initial content window (don't leave or enter iframes)
         if (elemUnder && elemUnder.ownerDocument.defaultView != this.contentWindow)
           return;
 
+        // Use fake mouse events to update the selection
         if (json.type == "end") {
-          this.cache.end.x = json.x - scrollOffset.x;
-          this.cache.end.y = json.y - scrollOffset.y;
-          utils.sendMouseEventToWindow("mousedown", this.cache.end.x, this.cache.end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
-          utils.sendMouseEventToWindow("mouseup", this.cache.end.x, this.cache.end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+          // Keep the cache in "client" coordinates, but translate for the mouse event
+          this.cache.end = { x: json.x, y: json.y };
+          let end = { x: this.cache.end.x - scrollOffset.x, y: this.cache.end.y - scrollOffset.y };
+          utils.sendMouseEventToWindow("mousedown", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+          utils.sendMouseEventToWindow("mouseup", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
         } else {
-          this.cache.start.x = json.x - scrollOffset.x;
-          this.cache.start.y = json.y - scrollOffset.y;
-          utils.sendMouseEventToWindow("mousedown", this.cache.start.x, this.cache.start.y, 0, 1, 0, true);
-          // Don't cause a click. A mousedown is enough to move the caret
-          //utils.sendMouseEventToWindow("mouseup", this.cache.start.x, this.cache.start.y, 0, 1, 0, true);
-          utils.sendMouseEventToWindow("mousedown", this.cache.end.x, this.cache.end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
-          utils.sendMouseEventToWindow("mouseup", this.cache.end.x, this.cache.end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+          // Keep the cache in "client" coordinates, but translate for the mouse event
+          this.cache.start = { x: json.x, y: json.y };
+          let start = { x: this.cache.start.x - scrollOffset.x, y: this.cache.start.y - scrollOffset.y };
+          let end = { x: this.cache.end.x - scrollOffset.x, y: this.cache.end.y - scrollOffset.y };
+
+          utils.sendMouseEventToWindow("mousedown", start.x, start.y, 0, 0, 0, true);
+          utils.sendMouseEventToWindow("mouseup", start.x, start.y, 0, 0, 0, true);
+
+          utils.sendMouseEventToWindow("mousedown", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+          utils.sendMouseEventToWindow("mouseup", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
         }
 
         // Cache the selected text since the selection might be gone by the time we get the "end" message
         let selection = this.contentWindow.getSelection()
         this.selectedText = selection.toString().trim();
 
         // Update the rect we use to test when finishing the clipboard operation
         let range = selection.getRangeAt(0).QueryInterface(Ci.nsIDOMNSRange);
@@ -1482,16 +1487,22 @@ var SelectionHandler = {
     for (let i=0; i<rects.length; i++) {
       if (i == 0) {
         cache.start.x = rects[i].left + aOffset.x;
         cache.start.y = rects[i].bottom + aOffset.y;
       }
       cache.end.x = rects[i].right + aOffset.x;
       cache.end.y = rects[i].bottom + aOffset.y;
     }
+
+    // Keep the handles from being positioned completely out of the selection range
+    const HANDLE_VERTICAL_MARGIN = 4;
+    cache.start.y -= HANDLE_VERTICAL_MARGIN;
+    cache.end.y -= HANDLE_VERTICAL_MARGIN;
+
     cache.rect = aRange.getBoundingClientRect();
     cache.rect.left += aOffset.x;
     cache.rect.top += aOffset.y;
     cache.rect.right += aOffset.x;
     cache.rect.bottom += aOffset.y;
     cache.offset = aOffset;
 
     return cache;