Bug 1154791 - Remember all ranges for all selections when splitting nodes in the editor transactions; r=ehsan,a=rkent THUNDERBIRD_38_VERBRANCH
authorJorg K <mozilla@jorgk.com>
Wed, 22 Apr 2015 10:50:00 +0200
branchTHUNDERBIRD_38_VERBRANCH
changeset 260325 222192907d835cc7f703dbf6061df23b8c6953cd
parent 260324 2b80e0ec7f3534eff1fee610fd961d65a6efeda3
child 260326 d79fd66bfe580ce655ec91c786aa0163784ba683
push id749
push usermozilla@kewis.ch
push dateWed, 29 Apr 2015 19:45:09 +0000
treeherdermozilla-release@222192907d83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, rkent
bugs1154791
milestone38.0
Bug 1154791 - Remember all ranges for all selections when splitting nodes in the editor transactions; r=ehsan,a=rkent
editor/libeditor/nsEditor.cpp
editor/libeditor/tests/chrome.ini
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -2592,33 +2592,52 @@ nsEditor::CreateTxnForJoinNode(nsINode& 
 }
 
 
 // END nsEditor core implementation
 
 
 // BEGIN nsEditor public helper methods
 
+struct SavedRange {
+  nsRefPtr<Selection> mSelection;
+  nsCOMPtr<nsINode> mStartNode;
+  nsCOMPtr<nsINode> mEndNode;
+  int32_t mStartOffset;
+  int32_t mEndOffset;
+};
+
 nsresult
 nsEditor::SplitNodeImpl(nsIContent& aExistingRightNode,
                         int32_t aOffset,
                         nsIContent& aNewLeftNode)
 {
-  // Get selection
-  nsRefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-
-  // Remember some selection points, if selection is set
-  nsCOMPtr<nsINode> selStartNode, selEndNode;
-  int32_t selStartOffset = 0, selEndOffset = 0;
-  if (selection->GetRangeAt(0)) {
-    selStartNode = selection->GetRangeAt(0)->GetStartParent();
-    selStartOffset = selection->GetRangeAt(0)->StartOffset();
-    selEndNode = selection->GetRangeAt(0)->GetEndParent();
-    selEndOffset = selection->GetRangeAt(0)->EndOffset();
+  // Remember all selection points.
+  nsAutoTArray<SavedRange, 10> savedRanges;
+  for (size_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES - 1; ++i) {
+    SelectionType type(1 << i);
+    SavedRange range;
+    range.mSelection = GetSelection(type);
+    if (type == nsISelectionController::SELECTION_NORMAL) {
+      NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
+    } else if (!range.mSelection) {
+      // For non-normal selections, skip over the non-existing ones.
+      continue;
+    }
+
+    for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
+      nsRefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
+      MOZ_ASSERT(r->IsPositioned());
+      range.mStartNode = r->GetStartParent();
+      range.mStartOffset = r->StartOffset();
+      range.mEndNode = r->GetEndParent();
+      range.mEndOffset = r->EndOffset();
+
+      savedRanges.AppendElement(range);
+    }
   }
 
   nsCOMPtr<nsINode> parent = aExistingRightNode.GetParentNode();
   NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
 
   ErrorResult rv;
   parent->InsertBefore(aNewLeftNode, &aExistingRightNode, rv);
   NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
@@ -2659,51 +2678,74 @@ nsEditor::SplitNodeImpl(nsIContent& aExi
   }
 
   // Handle selection
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   if (ps) {
     ps->FlushPendingNotifications(Flush_Frames);
   }
 
-  if (GetShouldTxnSetSelection()) {
-    // Editor wants us to set selection at split point
-    selection->Collapse(&aNewLeftNode, aOffset);
-  } else if (selStartNode) {
-    // Else adjust the selection if needed.  If selStartNode is null, then
-    // there was no selection.
-    if (selStartNode == &aExistingRightNode) {
-      if (selStartOffset < aOffset) {
-        selStartNode = &aNewLeftNode;
+  bool shouldSetSelection = GetShouldTxnSetSelection();
+
+  nsRefPtr<Selection> previousSelection;
+  for (size_t i = 0; i < savedRanges.Length(); ++i) {
+    // Adjust the selection if needed.
+    SavedRange& range = savedRanges[i];
+
+    // If we have not seen the selection yet, clear all of its ranges.
+    if (range.mSelection != previousSelection) {
+      nsresult rv = range.mSelection->RemoveAllRanges();
+      NS_ENSURE_SUCCESS(rv, rv);
+      previousSelection = range.mSelection;
+    }
+
+    if (shouldSetSelection &&
+        range.mSelection->Type() ==
+          nsISelectionController::SELECTION_NORMAL) {
+      // If the editor should adjust the selection, don't bother restoring
+      // the ranges for the normal selection here.
+      continue;
+    }
+
+    // Split the selection into existing node and new node.
+    if (range.mStartNode == &aExistingRightNode) {
+      if (range.mStartOffset < aOffset) {
+        range.mStartNode = &aNewLeftNode;
       } else {
-        selStartOffset -= aOffset;
+        range.mStartOffset -= aOffset;
       }
     }
-    if (selEndNode == &aExistingRightNode) {
-      if (selEndOffset < aOffset) {
-        selEndNode = &aNewLeftNode;
+
+    if (range.mEndNode == &aExistingRightNode) {
+      if (range.mEndOffset < aOffset) {
+        range.mEndNode = &aNewLeftNode;
       } else {
-        selEndOffset -= aOffset;
+        range.mEndOffset -= aOffset;
       }
     }
-    selection->Collapse(selStartNode, selStartOffset);
-    selection->Extend(selEndNode, selEndOffset);
+
+    nsRefPtr<nsRange> newRange;
+    nsresult rv = nsRange::CreateRange(range.mStartNode, range.mStartOffset,
+                                       range.mEndNode, range.mEndOffset,
+                                       getter_AddRefs(newRange));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = range.mSelection->AddRange(newRange);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (shouldSetSelection) {
+    // Editor wants us to set selection at split point.
+    nsRefPtr<Selection> selection = GetSelection();
+    NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
+    selection->Collapse(&aNewLeftNode, aOffset);
   }
 
   return NS_OK;
 }
 
-struct SavedRange {
-  nsRefPtr<Selection> mSelection;
-  nsCOMPtr<nsINode> mStartNode;
-  nsCOMPtr<nsINode> mEndNode;
-  int32_t mStartOffset;
-  int32_t mEndOffset;
-};
-
 nsresult
 nsEditor::JoinNodesImpl(nsINode* aNodeToKeep,
                         nsINode* aNodeToJoin,
                         nsINode* aParent)
 {
   MOZ_ASSERT(aNodeToKeep);
   MOZ_ASSERT(aNodeToJoin);
   MOZ_ASSERT(aParent);
@@ -2725,19 +2767,17 @@ nsEditor::JoinNodesImpl(nsINode* aNodeTo
       NS_ENSURE_TRUE(range.mSelection, NS_ERROR_NULL_POINTER);
     } else if (!range.mSelection) {
       // For non-normal selections, skip over the non-existing ones.
       continue;
     }
 
     for (uint32_t j = 0; j < range.mSelection->RangeCount(); ++j) {
       nsRefPtr<nsRange> r = range.mSelection->GetRangeAt(j);
-      if (!r->IsPositioned()) {
-        continue;
-      }
+      MOZ_ASSERT(r->IsPositioned());
       range.mStartNode = r->GetStartParent();
       range.mStartOffset = r->StartOffset();
       range.mEndNode = r->GetEndParent();
       range.mEndOffset = r->EndOffset();
 
       // If selection endpoint is between the nodes, remember it as being
       // in the one that is going away instead.  This simplifies later selection
       // adjustment logic at end of this method.
--- a/editor/libeditor/tests/chrome.ini
+++ b/editor/libeditor/tests/chrome.ini
@@ -17,16 +17,17 @@ skip-if = buildapp == 'mulet'
 [test_bug636465.xul]
 [test_bug646194.xul]
 [test_bug780908.xul]
 [test_bug830600.html]
 [test_bug1053048.html]
 [test_bug1100966.html]
 [test_bug1102906.html]
 [test_bug1101392.html]
+[test_bug1154791.html]
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_text_input_handling.html]
 [test_dragdrop.html]
 skip-if = buildapp == 'mulet'
 [test_htmleditor_keyevent_handling.html]
 [test_selection_move_commands.xul]
 [test_texteditor_keyevent_handling.html]
 skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux