Bug 1129078 - part 4, Add AutoPrepareFocusRange stack objects on paths to TakeFocus that sets up mAnchorFocusRange (and possibly removes mIsGenerated ranges) based on what operation the user is performing. r=smaug
☠☠ backed out by 7afbf9ebc89b ☠ ☠
authorMats Palmgren <mats@mozilla.com>
Fri, 20 Feb 2015 17:34:21 +0000
changeset 258779 ce5cb876af9ab955878f61367262c33cc9971ef4
parent 258778 c199ff86c77f2b0562fb525ce32d78566008a6d1
child 258780 9830ab1a2028aab1c1a1ff865704908b56a37722
push id721
push userjlund@mozilla.com
push dateTue, 21 Apr 2015 23:03:33 +0000
treeherdermozilla-release@d27c9211ebb3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1129078
milestone38.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 1129078 - part 4, Add AutoPrepareFocusRange stack objects on paths to TakeFocus that sets up mAnchorFocusRange (and possibly removes mIsGenerated ranges) based on what operation the user is performing. r=smaug
layout/generic/Selection.h
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -22,16 +22,17 @@ struct CachedOffsetForFrame;
 class nsAutoScrollTimer;
 class nsIContentIterator;
 class nsIFrame;
 class nsFrameSelection;
 struct SelectionDetails;
 
 namespace mozilla {
 class ErrorResult;
+struct AutoPrepareFocusRange;
 }
 
 struct RangeData
 {
   explicit RangeData(nsRange* aRange)
     : mRange(aRange)
   {}
 
@@ -221,17 +222,17 @@ public:
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       aSelection->mApplyUserSelectStyle = true;
     }
     AutoRestore<bool> mSavedValue;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
 private:
-
+  friend struct mozilla::AutoPrepareFocusRange;
   class ScrollSelectionIntoViewEvent;
   friend class ScrollSelectionIntoViewEvent;
 
   class ScrollSelectionIntoViewEvent : public nsRunnable {
   public:
     NS_DECL_NSIRUNNABLE
     ScrollSelectionIntoViewEvent(Selection* aSelection,
                                  SelectionRegion aRegion,
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -637,16 +637,17 @@ private:
             (nsISelectionListener::DRAG_REASON |
              nsISelectionListener::MOUSEDOWN_REASON |
              nsISelectionListener::MOUSEUP_REASON |
              nsISelectionListener::KEYPRESS_REASON)) !=
            nsISelectionListener::NO_REASON;
   }
 
   friend class mozilla::dom::Selection;
+  friend struct mozilla::AutoPrepareFocusRange;
 #ifdef DEBUG
   void printSelection();       // for debugging
 #endif /* DEBUG */
 
   void ResizeBuffer(uint32_t aNewBufSize);
 
 /*HELPER METHODS*/
   // Whether MoveCaret should use logical or visual movement,
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -321,16 +321,111 @@ IsValidSelectionPoint(nsFrameSelection *
     //if newfocus == the limiter. that's ok. but if not there and not parent bad
     return false; //not in the right content. tLimiter said so
   }
 
   limiter = aFrameSel->GetAncestorLimiter();
   return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
 }
 
+namespace mozilla {
+struct MOZ_STACK_CLASS AutoPrepareFocusRange
+{
+  AutoPrepareFocusRange(Selection* aSelection,
+                        bool aContinueSelection,
+                        bool aMultipleSelection
+                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+    if (aSelection->mRanges.Length() <= 1) {
+      return;
+    }
+
+    if (aSelection->mFrameSelection->IsUserSelectionReason()) {
+      mUserSelect.emplace(aSelection);
+    }
+    bool userSelection = aSelection->mApplyUserSelectStyle;
+
+    nsTArray<RangeData>& ranges = aSelection->mRanges;
+    if (!userSelection ||
+        (!aContinueSelection && aMultipleSelection)) {
+      // Scripted command or the user is starting a new explicit multi-range
+      // selection.
+      for (RangeData& entry : ranges) {
+        entry.mRange->SetIsGenerated(false);
+      }
+      return;
+    }
+
+    int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
+    bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
+                                         nsISelectionListener::MOUSEDOWN_REASON |
+                                         nsISelectionListener::MOUSEUP_REASON |
+                                         nsISelectionListener::COLLAPSETOSTART_REASON));
+    if (!isAnchorRelativeOp) {
+      return;
+    }
+
+    // This operation is against the anchor but our current mAnchorFocusRange
+    // represents the focus in a multi-range selection.  The anchor from a user
+    // perspective is the most distant generated range on the opposite side.
+    // Find that range and make it the mAnchorFocusRange.
+    const size_t len = ranges.Length();
+    size_t newAnchorFocusIndex = size_t(-1);
+    if (aSelection->GetDirection() == eDirNext) {
+      for (size_t i = 0; i < len; ++i) {
+        if (ranges[i].mRange->IsGenerated()) {
+          newAnchorFocusIndex = i;
+          break;
+        }
+      }
+    } else {
+      size_t i = len;
+      while (i--) {
+        if (ranges[i].mRange->IsGenerated()) {
+          newAnchorFocusIndex = i;
+          break;
+        }
+      }
+    }
+
+    if (newAnchorFocusIndex == size_t(-1)) {
+      // There are no generated ranges - that's fine.
+      return;
+    }
+
+    // Setup the new mAnchorFocusRange and mark the old one as generated.
+    if (aSelection->mAnchorFocusRange) {
+      aSelection->mAnchorFocusRange->SetIsGenerated(true);
+    }
+    nsRange* range = ranges[newAnchorFocusIndex].mRange;
+    range->SetIsGenerated(false);
+    aSelection->mAnchorFocusRange = range;
+
+    // Remove all generated ranges (including the old mAnchorFocusRange).
+    nsRefPtr<nsPresContext> presContext = aSelection->GetPresContext();
+    size_t i = len;
+    while (i--) {
+      range = aSelection->mRanges[i].mRange;
+      if (range->IsGenerated()) {
+        range->SetInSelection(false);
+        aSelection->selectFrames(presContext, range, false);
+        aSelection->mRanges.RemoveElementAt(i);
+      }
+    }
+    if (aSelection->mFrameSelection) {
+      aSelection->mFrameSelection->InvalidateDesiredPos();
+    }
+  }
+
+  Maybe<Selection::AutoApplyUserSelectStyle> mUserSelect;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+}
 
 ////////////BEGIN nsFrameSelection methods
 
 nsFrameSelection::nsFrameSelection()
 {
   int32_t i;
   for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
     mDomSelections[i] = new Selection(this);
@@ -794,16 +889,18 @@ nsFrameSelection::MoveCaret(nsDirection 
     } else {
       PostReason(nsISelectionListener::COLLAPSETOEND_REASON);
       mHint = CARET_ASSOCIATE_BEFORE;
     }
   } else {
     PostReason(nsISelectionListener::KEYPRESS_REASON);
   }
 
+  AutoPrepareFocusRange prep(sel, aContinueSelection, false);
+
   if (aAmount == eSelectLine) {
     result = FetchDesiredPos(desiredPos);
     if (NS_FAILED(result)) {
       return result;
     }
     SetDesiredPos(desiredPos);
   }
 
@@ -1371,16 +1468,18 @@ nsFrameSelection::HandleClick(nsIContent
   if (!mDragSelectingCells)
   {
     BidiLevelFromClick(aNewFocus, aContentOffset);
     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
     if (aContinueSelection &&
         AdjustForMaintainedSelection(aNewFocus, aContentOffset))
       return NS_OK; //shift clicked to maintained selection. rejected.
 
+    int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    AutoPrepareFocusRange prep(mDomSelections[index], aContinueSelection, aMultipleSelection);
     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aHint,
                      aContinueSelection, aMultipleSelection);
   }
   
   return NS_OK;
 }
 
 void
@@ -2045,16 +2144,18 @@ nsFrameSelection::SelectAll()
     if (!doc)
       return NS_ERROR_FAILURE;
     rootContent = doc->GetRootElement();
     if (!rootContent)
       return NS_ERROR_FAILURE;
   }
   int32_t numChildren = rootContent->GetChildCount();
   PostReason(nsISelectionListener::NO_REASON);
+  int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+  AutoPrepareFocusRange prep(mDomSelections[index], false, false);
   return TakeFocus(rootContent, 0, numChildren, CARET_ASSOCIATE_BEFORE, false, false);
 }
 
 //////////END FRAMESELECTION
 
 void
 nsFrameSelection::StartBatchChanges()
 {