Bug 864589 - Show/hide text selection handles if a selection is programatically added/removed, r=margaret, ehsan
☠☠ backed out by 2c6cf76338b5 ☠ ☠
authorMark Capella <markcapella@twcny.rr.com>
Fri, 16 Aug 2013 16:45:42 -0400
changeset 143019 2404f5888de3b217188b2f5019a9ea265c841be8
parent 143018 ae177d0849f5fdd3da826f0fa275bc35fd9cb0ea
child 143020 38dbe545e3d7c8095236281f047dd4f3bb27289f
push id32605
push userphilringnalda@gmail.com
push dateMon, 19 Aug 2013 00:51:46 +0000
treeherdermozilla-inbound@7f882e063eaf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret, ehsan
bugs864589
milestone26.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 864589 - Show/hide text selection handles if a selection is programatically added/removed, r=margaret, ehsan
content/base/public/nsISelectionListener.idl
layout/generic/nsSelection.cpp
mobile/android/chrome/content/SelectionHandler.js
--- a/content/base/public/nsISelectionListener.idl
+++ b/content/base/public/nsISelectionListener.idl
@@ -12,13 +12,15 @@ interface nsISelection;
 interface nsISelectionListener : nsISupports
 {
   const short NO_REASON=0;
   const short DRAG_REASON=1;
   const short MOUSEDOWN_REASON=2;/*bitflags*/
   const short MOUSEUP_REASON=4;/*bitflags*/
   const short KEYPRESS_REASON=8;/*bitflags*/
   const short SELECTALL_REASON=16;
+  const short COLLAPSETOSTART_REASON=32;
+  const short COLLAPSETOEND_REASON=64;
 
 	void			notifySelectionChanged(in nsIDOMDocument doc, in nsISelection sel, in short reason);
 };
 
 
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -4439,16 +4439,20 @@ Selection::CollapseToStart()
   if (NS_FAILED(rv) || cnt <= 0)
     return NS_ERROR_DOM_INVALID_STATE_ERR;
 
   // Get the first range
   nsRange* firstRange = mRanges[0].mRange;
   if (!firstRange)
     return NS_ERROR_FAILURE;
 
+  if (mFrameSelection) {
+    int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
+    mFrameSelection->PostReason(reason);
+  }
   return Collapse(firstRange->GetStartParent(), firstRange->StartOffset());
 }
 
 /*
  * Sets the whole selection to be one point
  * at the end of the current selection
  */
 NS_IMETHODIMP
@@ -4459,16 +4463,20 @@ Selection::CollapseToEnd()
   if (NS_FAILED(rv) || cnt <= 0)
     return NS_ERROR_DOM_INVALID_STATE_ERR;
 
   // Get the last range
   nsRange* lastRange = mRanges[cnt - 1].mRange;
   if (!lastRange)
     return NS_ERROR_FAILURE;
 
+  if (mFrameSelection) {
+    int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
+    mFrameSelection->PostReason(reason);
+  }
   return Collapse(lastRange->GetEndParent(), lastRange->EndOffset());
 }
 
 /*
  * IsCollapsed -- is the whole selection just one point, or unset?
  */
 bool
 Selection::IsCollapsed()
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -169,16 +169,24 @@ var SelectionHandler = {
     this._contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor).
                             getInterface(Ci.nsIDOMWindowUtils).getScrollXY(false, scrollX, scrollY);
     return {
       X: scrollX.value,
       Y: scrollY.value
     };
   },
 
+  notifySelectionChanged: function sh_notifySelectionChanged(aDocument, aSelection, aReason) {
+    // If the selection was collapsed to Start or to End, always close it
+    if ((aReason & Ci.nsISelectionListener.COLLAPSETOSTART_REASON) ||
+        (aReason & Ci.nsISelectionListener.COLLAPSETOEND_REASON)) {
+      this._closeSelection();
+    }
+  },
+
   /*
    * 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.
    */
   startSelection: function sh_startSelection(aElement, aX, aY) {
@@ -198,16 +206,19 @@ var SelectionHandler = {
 
     let selection = this._getSelection();
     // If the range didn't have any text, let's bail
     if (!selection || selection.rangeCount == 0) {
       this._closeSelection();
       return;
     }
 
+    // Add a listener to end the selection if it's removed programatically
+    selection.QueryInterface(Ci.nsISelectionPrivate).addSelectionListener(this);
+
     // 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;
@@ -458,16 +469,18 @@ var SelectionHandler = {
   _closeSelection: function sh_closeSelection() {
     // Bail if there's no active selection
     if (this._activeType == this.TYPE_NONE)
       return;
 
     if (this._activeType == this.TYPE_SELECTION) {
       let selection = this._getSelection();
       if (selection) {
+        // Remove our listener before we removeAllRanges()
+        selection.QueryInterface(Ci.nsISelectionPrivate).removeSelectionListener(this);
         selection.removeAllRanges();
       }
     }
 
     this._activeType = this.TYPE_NONE;
 
     sendMessageToJava({ type: "TextSelection:HideHandles" });