Bug 1066515 - Part 1: Set selection range directly when replacing within current text node. r=yxl
authorTing-Yu Chou <janus926@gmail.com>
Fri, 03 Oct 2014 14:09:53 +0800
changeset 209577 f589902d878343e52b2acfed6b07128aba4138be
parent 209576 c5a41dac572942ed2c1ed14d218529852477c305
child 209578 986b480432804bee64cb9c8bd75bb01f22ade9c8
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersyxl
bugs1066515
milestone35.0a1
Bug 1066515 - Part 1: Set selection range directly when replacing within current text node. r=yxl
dom/inputmethod/forms.js
--- a/dom/inputmethod/forms.js
+++ b/dom/inputmethod/forms.js
@@ -580,21 +580,18 @@ let FormAssistant = {
           });
         }
         break;
       }
 
       case "Forms:ReplaceSurroundingText": {
         CompositionManager.endComposition('');
 
-        let selectionRange = getSelectionRange(target);
         if (!replaceSurroundingText(target,
                                     json.text,
-                                    selectionRange[0],
-                                    selectionRange[1],
                                     json.offset,
                                     json.length)) {
           if (json.requestId) {
             sendAsyncMessage("Forms:ReplaceSurroundingText:Result:Error", {
               requestId: json.requestId,
               error: "failed"
             });
           }
@@ -1134,41 +1131,60 @@ function getPlaintextEditor(element) {
     }
   }
   if (editor) {
     editor.QueryInterface(Ci.nsIPlaintextEditor);
   }
   return editor;
 }
 
-function replaceSurroundingText(element, text, selectionStart, selectionEnd,
-                                offset, length) {
+function replaceSurroundingText(element, text, offset, length) {
   let editor = FormAssistant.editor;
   if (!editor) {
     return false;
   }
 
   // Check the parameters.
-  let start = selectionStart + offset;
-  if (start < 0) {
-    start = 0;
-  }
   if (length < 0) {
     length = 0;
   }
-  let end = start + length;
 
-  if (selectionStart != start || selectionEnd != end) {
-    // Change selection range before replacing.
-    if (!setSelectionRange(element, start, end)) {
-      return false;
+  // Change selection range before replacing. For content editable element,
+  // searching the node for setting selection range is not needed when the
+  // selection is collapsed within a text node.
+  let fastPathHit = false;
+  if (!isPlainTextField(element)) {
+    let sel = element.ownerDocument.defaultView.getSelection();
+    let node = sel.anchorNode;
+    if (sel.isCollapsed && node && node.nodeType == 3 /* TEXT_NODE */) {
+      let start = sel.anchorOffset + offset;
+      let end = start + length;
+      // Fallback to setSelectionRange() if the replacement span multiple nodes.
+      if (start >= 0 && end <= node.textContent.length) {
+        fastPathHit = true;
+        sel.collapse(node, start);
+        sel.extend(node, end);
+      }
+    }
+  }
+  if (!fastPathHit) {
+    let range = getSelectionRange(element);
+    let start = range[0] + offset;
+    if (start < 0) {
+      start = 0;
+    }
+    let end = start + length;
+    if (start != range[0] || end != range[1]) {
+      if (!setSelectionRange(element, start, end)) {
+        return false;
+      }
     }
   }
 
-  if (start != end) {
+  if (length) {
     // Delete the selected text.
     editor.deleteSelection(Ci.nsIEditor.ePrevious, Ci.nsIEditor.eStrip);
   }
 
   if (text) {
     // We don't use CR but LF
     // see https://bugzilla.mozilla.org/show_bug.cgi?id=902847
     text = text.replace(/\r/g, '\n');