Bug 674791 - dragging the left text selection marker down selects to the bottom of the page [r=wjohnston]
authorMark Finkle <mfinkle@mozilla.com>
Thu, 04 Aug 2011 15:26:42 -0400
changeset 73841 5684f06138f39e6c6b95cb076cdbe449875a1c2d
parent 73840 c7ea065539d2f84a2d92c98aae474123a6c704f8
child 73901 c7931e07dd4dcda4916d58753bfdb933372e8148
child 73949 8d226c3994b022d1312e214360069caf4c126aa8
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerswjohnston
bugs674791
milestone8.0a1
Bug 674791 - dragging the left text selection marker down selects to the bottom of the page [r=wjohnston]
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;