Bug 950464 - Default to selecting all text in SelectionHandler. r=margaret
authorWes Johnston <wjohnston@mozilla.com>
Tue, 17 Dec 2013 18:18:35 -0800
changeset 160918 336da7d0b51aab4f6374d404a10fb43979266732
parent 160917 fc79fa2fd2e368403ec3bd2a299063324acdfb92
child 160919 b329b704e89a34f13c9ca7b80b6115de896afb80
push id4028
push userwjohnston@mozilla.com
push dateWed, 18 Dec 2013 02:22:56 +0000
treeherderfx-team@b329b704e89a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs950464
milestone29.0a1
Bug 950464 - Default to selecting all text in SelectionHandler. r=margaret
mobile/android/chrome/content/SelectionHandler.js
mobile/android/chrome/content/browser.js
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -7,16 +7,19 @@ var SelectionHandler = {
   HANDLE_TYPE_START: "START",
   HANDLE_TYPE_MIDDLE: "MIDDLE",
   HANDLE_TYPE_END: "END",
 
   TYPE_NONE: 0,
   TYPE_CURSOR: 1,
   TYPE_SELECTION: 2,
 
+  SELECT_ALL: 0,
+  SELECT_AT_POINT: 1,
+
   // Keeps track of data about the dimensions of the selection. Coordinates
   // stored here are relative to the _contentWindow window.
   _cache: null,
   _activeType: 0, // TYPE_NONE
   _ignoreSelectionChanges: false, // True while user drags text selection handles
   _ignoreCompositionChanges: false, // Persist caret during IME composition updates
 
   // The window that holds the selection (can be a sub-frame)
@@ -215,36 +218,41 @@ var SelectionHandler = {
     }
   },
 
   /*
    * Called from browser.js when the user long taps on text or chooses
    * the "Select Word" context menu item. Initializes SelectionHandler,
    * starts a selection, and positions the text selection handles.
    *
-   * @param aX, aY tap location in client coordinates.
+   * @param aOptions list of options describing how to start selection
+   *                 Options include:
+   *                   mode - SELECT_ALL to select everything in the target
+   *                          element, or SELECT_AT_POINT to select a word.
+   *                   x    - The x-coordinate for SELECT_AT_POINT.
+   *                   y    - The y-coordinate for SELECT_AT_POINT.
    */
-  startSelection: function sh_startSelection(aElement, aX, aY) {
+  startSelection: function sh_startSelection(aElement, aOptions = { mode: SelectionHandler.SELECT_ALL }) {
     // Clear out any existing active selection
     this._closeSelection();
 
     this._initTargetInfo(aElement);
 
     // Clear any existing selection from the document
     this._contentWindow.getSelection().removeAllRanges();
 
-    // If we didn't have any coordinates to associate with this event (for instance, selectAll is chosen from
-    // the actionMode), set them to a point inside the top left corner of the target
-    if (aX == undefined || aY == undefined) {
-      let rect = this._targetElement.getBoundingClientRect();
-      aX = rect.left + 1;
-      aY = rect.top  + 1;
-    }
-
-    if (!this._domWinUtils.selectAtPoint(aX, aY, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
+    if (aOptions.mode == this.SELECT_ALL) {
+      this._getSelectionController().selectAll();
+    } else if (aOptions.mode == this.SELECT_AT_POINT) {
+      if (!this._domWinUtils.selectAtPoint(aOptions.x, aOptions.y, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
+        this._deactivate();
+        return false;
+      }
+    } else {
+      Services.console.logStringMessage("Invalid selection mode " + aOptions.mode);
       this._deactivate();
       return false;
     }
 
     let selection = this._getSelection();
     // If the range didn't have any text, let's bail
     if (!selection || selection.rangeCount == 0 || selection.getRangeAt(0).collapsed) {
       this._deactivate();
@@ -257,46 +265,50 @@ var SelectionHandler = {
 
     // Initialize the cache
     this._cache = { start: {}, end: {}};
     this._updateCacheForSelection();
 
     let scroll = this._getScrollPos();
     // Figure out the distance between the selection and the click
     let positions = this._getHandlePositions(scroll);
-    let clickX = scroll.X + aX;
-    let clickY = scroll.Y + aY;
-    let distance = 0;
 
-    // Check if the click was in the bounding box of the selection handles
-    if (positions[0].left < clickX && clickX < positions[1].left
-        && positions[0].top < clickY && clickY < positions[1].top) {
-      distance = 0;
-    } else {
-      // If it was outside, check the distance to the center of the selection
-      let selectposX = (positions[0].left + positions[1].left) / 2;
-      let selectposY = (positions[0].top + positions[1].top) / 2;
-
-      let dx = Math.abs(selectposX - clickX);
-      let dy = Math.abs(selectposY - clickY);
-      distance = dx + dy;
-    }
-
-    let maxSelectionDistance = Services.prefs.getIntPref("browser.ui.selection.distance");
-    // Do not select text far away from where the user clicked
-    if (distance > maxSelectionDistance) {
-      this._closeSelection();
-      return false;
+    if (aOptions.mode == this.SELECT_AT_POINT && !this.selectionNearClick(scroll.X + aOptions.x,
+                                                                      scroll.Y + aOptions.y,
+                                                                      positions)) {
+        this._closeSelection();
+        return false;
     }
 
     this._positionHandles(positions);
-    this._sendMessage("TextSelection:ShowHandles", [this.HANDLE_TYPE_START, this.HANDLE_TYPE_END], aX, aY);
+    this._sendMessage("TextSelection:ShowHandles", [this.HANDLE_TYPE_START, this.HANDLE_TYPE_END], aOptions.x, aOptions.y);
     return true;
   },
 
+  /* Return true if the current selection (given by aPositions) is near to where the coordinates passed in */
+  _selectionNearClick: function(aX, aY, aPositions) {
+      let distance = 0;
+
+      // Check if the click was in the bounding box of the selection handles
+      if (aPositions[0].left < aX && aX < aPositions[1].left
+          && aPositions[0].top < aY && aY < aPositions[1].top) {
+        distance = 0;
+      } else {
+        // If it was outside, check the distance to the center of the selection
+        let selectposX = (aPositions[0].left + aPositions[1].left) / 2;
+        let selectposY = (aPositions[0].top + aPositions[1].top) / 2;
+
+        let dx = Math.abs(selectposX - aX);
+        let dy = Math.abs(selectposY - aY);
+        distance = dx + dy;
+      }
+
+      let maxSelectionDistance = Services.prefs.getIntPref("browser.ui.selection.distance");
+      return (distance < maxSelectionDistance);
+  },
 
   /* Reads a value from an action. If the action defines the value as a function, will return the result of calling
      the function. Otherwise, will return the value itself. If the value isn't defined for this action, will return a default */
   _getValue: function(obj, name, defaultValue) {
     if (!(name in obj))
       return defaultValue;
 
     if (typeof obj[name] == "function")
@@ -484,24 +496,25 @@ var SelectionHandler = {
                                  QueryInterface(Ci.nsISelectionController);
   },
 
   // Used by the contextmenu "matches" functions in ClipboardHelper
   isSelectionActive: function sh_isSelectionActive() {
     return (this._activeType == this.TYPE_SELECTION);
   },
 
-  selectAll: function sh_selectAll(aElement, aX, aY) {
-    if (this._activeType != this.TYPE_SELECTION)
-      this.startSelection(aElement, aX, aY);
-
-    let selectionController = this._getSelectionController();
-    selectionController.selectAll();
-    this._updateCacheForSelection();
-    this._positionHandles();
+  selectAll: function sh_selectAll(aElement) {
+    if (this._activeType != this.TYPE_SELECTION) {
+      this.startSelection(aElement, { mode : this.SELECT_ALL });
+    } else {
+      let selectionController = this._getSelectionController();
+      selectionController.selectAll();
+      this._updateCacheForSelection();
+      this._positionHandles();
+    }
   },
 
   /*
    * Moves the selection as the user drags a selection handle.
    *
    * @param aIsStartHandle whether the user is moving the start handle (as opposed to the end handle)
    * @param aX, aY selection point in client coordinates
    */
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2121,17 +2121,21 @@ var NativeWindow = {
                              0, null);
         target.ownerDocument.defaultView.addEventListener("contextmenu", this, false);
         target.dispatchEvent(event);
       } else {
         this._target = null;
         BrowserEventHandler._cancelTapHighlight();
 
         if (SelectionHandler.canSelect(target)) {
-          if (!SelectionHandler.startSelection(target, aX, aY)) {
+          if (!SelectionHandler.startSelection(target, {
+              mode: SelectionHandler.SELECT_AT_POINT,
+              x: aX,
+              y: aY
+            })) {
             SelectionHandler.attachCaret(target);
           }
         }
       }
     },
 
     // Actually shows the native context menu by passing a list of context menu items to
     // show to the Java.
@@ -6222,21 +6226,21 @@ var ClipboardHelper = {
       string = aElement.value.slice(selectionStart, selectionEnd);
       this.clipboardHelper.copyString(string, aElement.ownerDocument);
     } else {
       this.clipboardHelper.copyString(aElement.value, aElement.ownerDocument);
     }
   },
 
   selectWord: function(aElement, aX, aY) {
-    SelectionHandler.startSelection(aElement, aX, aY);
-  },
-
-  selectAll: function(aElement, aX, aY) {
-    SelectionHandler.selectAll(aElement, aX, aY);
+    SelectionHandler.startSelection(aElement, {
+      mode: SelectionHandler.SELECT_AT_POINT,
+      x: aX,
+      y: aY
+    });
   },
 
   searchWith: function(aElement) {
     SelectionHandler.searchSelection(aElement);
   },
 
   share: function() {
     SelectionHandler.shareSelection();