diff --git a/dom/inputmethod/MozKeyboard.js b/dom/inputmethod/MozKeyboard.js index 7bec21534a5..9f4195ad475 100644 --- a/dom/inputmethod/MozKeyboard.js +++ b/dom/inputmethod/MozKeyboard.js @@ -394,6 +394,11 @@ MozInputContext.prototype = { return; } + // Update context first before resolving promise to avoid race condition + if (json.selectioninfo) { + this.updateSelectionContext(json.selectioninfo); + } + switch (msg.name) { case "Keyboard:SendKey:Result:OK": resolver.resolve(); diff --git a/dom/inputmethod/forms.js b/dom/inputmethod/forms.js index b212a1f8cc8..dfe13e26442 100644 --- a/dom/inputmethod/forms.js +++ b/dom/inputmethod/forms.js @@ -224,6 +224,7 @@ let FormAssistant = { _documentEncoder: null, _editor: null, _editing: false, + _selectionPrivate: null, get focusedElement() { if (this._focusedElement && Cu.isDeadWrapper(this._focusedElement)) @@ -244,8 +245,6 @@ let FormAssistant = { return; if (this.focusedElement) { - this.focusedElement.removeEventListener('mousedown', this); - this.focusedElement.removeEventListener('mouseup', this); this.focusedElement.removeEventListener('compositionend', this); if (this._observer) { this._observer.disconnect(); @@ -254,6 +253,10 @@ let FormAssistant = { if (!element) { this.focusedElement.blur(); } + if (this._selectionPrivate) { + this._selectionPrivate.removeSelectionListener(this); + this._selectionPrivate = null; + } } this._documentEncoder = null; @@ -269,8 +272,6 @@ let FormAssistant = { } if (element) { - element.addEventListener('mousedown', this); - element.addEventListener('mouseup', this); element.addEventListener('compositionend', this); if (isContentEditable(element)) { this._documentEncoder = getDocumentEncoder(element); @@ -280,6 +281,12 @@ let FormAssistant = { // Add a nsIEditorObserver to monitor the text content of the focused // element. this._editor.addEditorObserver(this); + + let selection = this._editor.selection; + if (selection) { + this._selectionPrivate = selection.QueryInterface(Ci.nsISelectionPrivate); + this._selectionPrivate.addSelectionListener(this); + } } // If our focusedElement is removed from DOM we want to handle it properly @@ -305,6 +312,10 @@ let FormAssistant = { this.focusedElement = element; }, + notifySelectionChanged: function(aDocument, aSelection, aReason) { + this.updateSelection(); + }, + get documentEncoder() { return this._documentEncoder; }, @@ -376,32 +387,6 @@ let FormAssistant = { } break; - case 'mousedown': - if (!this.focusedElement) { - break; - } - - // We only listen for this event on the currently focused element. - // When the mouse goes down, note the cursor/selection position - this.updateSelection(); - break; - - case 'mouseup': - if (!this.focusedElement) { - break; - } - - // We only listen for this event on the currently focused element. - // When the mouse goes up, see if the cursor has moved (or the - // selection changed) since the mouse went down. If it has, we - // need to tell the keyboard about it - range = getSelectionRange(this.focusedElement); - if (range[0] !== this.selectionStart || - range[1] !== this.selectionEnd) { - this.updateSelection(); - } - break; - case "resize": if (!this.isKeyboardOpened) return; @@ -423,25 +408,12 @@ let FormAssistant = { } break; - case "input": - if (this.focusedElement) { - // When the text content changes, notify the keyboard - this.updateSelection(); - } - break; - case "keydown": if (!this.focusedElement) { break; } CompositionManager.endComposition(''); - - // We use 'setTimeout' to wait until the input element accomplishes the - // change in selection range. - content.setTimeout(function() { - this.updateSelection(); - }.bind(this), 0); break; case "keyup": @@ -450,7 +422,6 @@ let FormAssistant = { } CompositionManager.endComposition(''); - break; case "compositionend": @@ -525,7 +496,8 @@ let FormAssistant = { if (json.requestId && doKeypress) { sendAsyncMessage("Forms:SendKey:Result:OK", { - requestId: json.requestId + requestId: json.requestId, + selectioninfo: this.getSelectionInfo() }); } else if (json.requestId && !doKeypress) { @@ -583,8 +555,6 @@ let FormAssistant = { break; } - this.updateSelection(); - if (json.requestId) { sendAsyncMessage("Forms:SetSelectionRange:Result:OK", { requestId: json.requestId, @@ -651,6 +621,7 @@ let FormAssistant = { json.clauses); sendAsyncMessage("Forms:SetComposition:Result:OK", { requestId: json.requestId, + selectioninfo: this.getSelectionInfo() }); break; } @@ -659,6 +630,7 @@ let FormAssistant = { CompositionManager.endComposition(json.text); sendAsyncMessage("Forms:EndComposition:Result:OK", { requestId: json.requestId, + selectioninfo: this.getSelectionInfo() }); break; } @@ -757,6 +729,8 @@ let FormAssistant = { }; }, + _selectionTimeout: null, + // Notify when the selection range changes updateSelection: function fa_updateSelection() { if (!this.focusedElement) { @@ -764,7 +738,16 @@ let FormAssistant = { } let selectionInfo = this.getSelectionInfo(); if (selectionInfo.changed) { - sendAsyncMessage("Forms:SelectionChange", this.getSelectionInfo()); + // A call to setSelectionRange on input field causes 2 selection changes + // one to [0,0] and one to actual value. Both are sent in same tick. + // Prevent firing two events in that scenario, always only use the last 1 + if (this._selectionTimeout) { + content.clearTimeout(this._selectionTimeout); + } + + this._selectionTimeout = content.setTimeout(function() { + sendAsyncMessage("Forms:SelectionChange", selectionInfo); + }); } } }; diff --git a/dom/inputmethod/mochitest/mochitest.ini b/dom/inputmethod/mochitest/mochitest.ini index 61392de93f4..41bf6ee7156 100644 --- a/dom/inputmethod/mochitest/mochitest.ini +++ b/dom/inputmethod/mochitest/mochitest.ini @@ -13,5 +13,6 @@ support-files = [test_bug949059.html] [test_bug960946.html] [test_bug978918.html] +[test_bug1026997.html] [test_delete_focused_element.html] [test_sendkey_cancel.html] diff --git a/dom/inputmethod/mochitest/test_bug1026997.html b/dom/inputmethod/mochitest/test_bug1026997.html new file mode 100644 index 00000000000..9fa7ef185b8 --- /dev/null +++ b/dom/inputmethod/mochitest/test_bug1026997.html @@ -0,0 +1,101 @@ + + + + + SelectionChange on InputMethod API. + + + + + +Mozilla Bug 1026997 +

+
+
+
+ + +