Bug 1066515 - Part 1: Set selection range directly when replacing within current text node. r=yxl
--- 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');