Back out 4 changesets (bug 1051556) for java.lang.IllegalArgumentExceptions
authorPhil Ringnalda <philringnalda@gmail.com>
Wed, 09 Dec 2015 18:20:51 -0800
changeset 297702 8d22eea111d91930bd2d26fa007208ec72233611
parent 297701 87e625f2b84747a861677d2e7f14481cb1201c7a
child 297703 4230fdf612273aeb844fd1a709b8988d6efad459
push id8824
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:18:56 +0000
treeherdermozilla-aurora@e2031358e2a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1051556
milestone45.0a1
backs outc89683497d233d6f8853e53bed9da20c58723d5c
c3f4c2d01bad5a114f0e7d47308948af6a465e5b
4a83a8594ddc07b26e2afdadf90d3da07cb82c76
fe3d880efce840e6aed79ac66e83c8de4cd3daa6
Back out 4 changesets (bug 1051556) for java.lang.IllegalArgumentExceptions CLOSED TREE Backed out changeset c89683497d23 (bug 1051556) Backed out changeset c3f4c2d01bad (bug 1051556) Backed out changeset 4a83a8594ddc (bug 1051556) Backed out changeset fe3d880efce8 (bug 1051556)
mobile/android/base/java/org/mozilla/gecko/GeckoEditable.java
mobile/android/tests/browser/robocop/robocop_head.js
mobile/android/tests/browser/robocop/robocop_input.html
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
widget/android/nsWindow.cpp
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoEditable.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoEditable.java
@@ -963,24 +963,20 @@ final class GeckoEditable extends JNIObj
             @Override
             public void run() {
                 mListener.onSelectionChange(newStart, newEnd);
             }
         });
     }
 
     private void geckoReplaceText(int start, int oldEnd, CharSequence newText) {
-        if (AppConstants.Versions.preHC) {
-            // Don't use replace() because Gingerbread has a bug where if the replaced text
-            // has the same spans as the original text, the spans will end up being deleted
-            mText.delete(start, oldEnd);
-            mText.insert(start, newText);
-        } else {
-            mText.replace(start, oldEnd, newText);
-        }
+        // Don't use replace() because Gingerbread has a bug where if the replaced text
+        // has the same spans as the original text, the spans will end up being deleted
+        mText.delete(start, oldEnd);
+        mText.insert(start, newText);
     }
 
     private boolean isSameText(int start, int oldEnd, CharSequence newText) {
         return oldEnd - start == newText.length() &&
                TextUtils.regionMatches(mText, start, newText, 0, oldEnd - start);
     }
 
     @WrapForJNI @Override
@@ -1000,65 +996,84 @@ final class GeckoEditable extends JNIObj
             Log.e(LOGTAG, "invalid text notification range: " +
                   start + " to " + unboundedOldEnd);
             throw new IllegalArgumentException("invalid text notification range");
         }
         /* For the "end" parameters, Gecko can pass in a large
            number to denote "end of the text". Fix that here */
         final int oldEnd = unboundedOldEnd > mText.length() ? mText.length() : unboundedOldEnd;
         // new end should always match text
-        if (unboundedOldEnd < Integer.MAX_VALUE / 2 &&
-                unboundedNewEnd != (start + text.length())) {
+        if (start != 0 && unboundedNewEnd != (start + text.length())) {
             Log.e(LOGTAG, "newEnd does not match text: " + unboundedNewEnd + " vs " +
                   (start + text.length()));
             throw new IllegalArgumentException("newEnd does not match text");
         }
         final int newEnd = start + text.length();
         final Action action = mActionQueue.peek();
 
         if (action != null && action.mType == Action.TYPE_ACKNOWLEDGE_FOCUS) {
             // Simply replace the text for newly-focused editors.
             mText.replace(0, mText.length(), text);
 
-        } else if (action != null &&
-                action.mType == Action.TYPE_REPLACE_TEXT &&
-                start <= action.mStart &&
-                oldEnd >= action.mEnd &&
-                newEnd >= action.mStart + action.mSequence.length()) {
+        } else {
+            mChangedText.clearSpans();
+            mChangedText.replace(0, mChangedText.length(), text);
+            // Preserve as many spans as possible
+            TextUtils.copySpansFrom(mText, start, Math.min(oldEnd, newEnd),
+                                    Object.class, mChangedText, 0);
 
-            // actionNewEnd is the new end of the original replacement action
-            final int actionNewEnd = action.mStart + action.mSequence.length();
+            if (action != null &&
+                    action.mType == Action.TYPE_REPLACE_TEXT &&
+                    start <= action.mStart &&
+                    action.mStart + action.mSequence.length() <= newEnd) {
+
+                // actionNewEnd is the new end of the original replacement action
+                final int actionNewEnd = action.mStart + action.mSequence.length();
+                int selStart = Selection.getSelectionStart(mText);
+                int selEnd = Selection.getSelectionEnd(mText);
+
+                // Replace old spans with new spans
+                mChangedText.replace(action.mStart - start, actionNewEnd - start,
+                                     action.mSequence);
+                geckoReplaceText(start, oldEnd, mChangedText);
 
-            // Replace old spans with new spans
-            if (start == action.mStart && oldEnd == action.mEnd && newEnd == actionNewEnd) {
-                // The new text exactly matches our sequence, so do a direct replace.
-                geckoReplaceText(start, oldEnd, action.mSequence);
+                // delete/insert above might have moved our selection to somewhere else
+                // this happens when the Gecko text change covers a larger range than
+                // the original replacement action. Fix selection here
+                if (selStart >= start && selStart <= oldEnd) {
+                    selStart = selStart < action.mStart ? selStart :
+                               selStart < action.mEnd   ? actionNewEnd :
+                                                          selStart + actionNewEnd - action.mEnd;
+                    mText.setSpan(Selection.SELECTION_START, selStart, selStart,
+                                  Spanned.SPAN_POINT_POINT);
+                }
+                if (selEnd >= start && selEnd <= oldEnd) {
+                    selEnd = selEnd < action.mStart ? selEnd :
+                             selEnd < action.mEnd   ? actionNewEnd :
+                                                      selEnd + actionNewEnd - action.mEnd;
+                    mText.setSpan(Selection.SELECTION_END, selEnd, selEnd,
+                                  Spanned.SPAN_POINT_POINT);
+                }
+
+                // Ignore the next selection change because the selection change is a
+                // side-effect of the replace-text event we sent.
+                mIgnoreSelectionChange = true;
+
             } else {
-                mChangedText.clearSpans();
-                mChangedText.replace(0, mChangedText.length(), mText, start, oldEnd);
-                mChangedText.replace(action.mStart - start, action.mEnd - start, action.mSequence);
+                // Gecko side initiated the text change.
+                if (isSameText(start, oldEnd, mChangedText)) {
+                    // Nothing to do because the text is the same. This could happen when
+                    // the composition is updated for example. Ignore the next selection
+                    // change because the selection change is a side-effect of the
+                    // update-composition event we sent.
+                    mIgnoreSelectionChange = true;
+                    return;
+                }
                 geckoReplaceText(start, oldEnd, mChangedText);
             }
-
-            // Ignore the next selection change because the selection change is a
-            // side-effect of the replace-text event we sent.
-            mIgnoreSelectionChange = true;
-
-        } else {
-            // Gecko side initiated the text change.
-            if (isSameText(start, oldEnd, text)) {
-                // Nothing to do because the text is the same. This could happen when
-                // the composition is updated for example. Ignore the next selection
-                // change because the selection change is a side-effect of the
-                // update-composition event we sent.
-                mIgnoreSelectionChange = true;
-                return;
-            }
-
-            geckoReplaceText(start, oldEnd, text);
         }
 
         geckoPostToIc(new Runnable() {
             @Override
             public void run() {
                 mListener.onTextChange(text, start, oldEnd, newEnd);
             }
         });
--- a/mobile/android/tests/browser/robocop/robocop_head.js
+++ b/mobile/android/tests/browser/robocop/robocop_head.js
@@ -1,28 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// The test js is shared between sandboxed (which has no SpecialPowers object)
-// and content mochitests (where the |Components| object is accessible only as
-// SpecialPowers.Components). Expose Components if necessary here to make things
-// work everywhere.
-//
-// Even if the real |Components| doesn't exist, we might shim in a simple JS
-// placebo for compat. An easy way to differentiate this from the real thing
-// is whether the property is read-only or not.
-{
-  let c = Object.getOwnPropertyDescriptor(this, 'Components');
-  if ((!c.value || c.writable) && typeof SpecialPowers === 'object')
-    Components = SpecialPowers.wrap(SpecialPowers.Components);
-}
-
 /*
  * This file contains common code that is loaded before each test file(s).
  * See http://developer.mozilla.org/en/docs/Writing_xpcshell-based_unit_tests
  * for more information.
  */
 
 var _quit = false;
 var _tests_pending = 0;
--- a/mobile/android/tests/browser/robocop/robocop_input.html
+++ b/mobile/android/tests/browser/robocop/robocop_input.html
@@ -1,49 +1,25 @@
 <!DOCTYPE html>
 <html>
   <head>
     <title>Robocop Input</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
   </head>
-  <body>
+  <body onload="fillInput()">
     <p><input id="input" type="text"></p>
     <p><input id="resetting-input" type="text"></p>
-    <script type="application/javascript;version=1.8" src="robocop_head.js"></script>
-    <script type="application/javascript;version=1.8">
-      let input = document.getElementById("input");
-
-      // An input that resets the editor on every input by resetting the value property.
-      let resetting_input = document.getElementById("resetting-input");
-      resetting_input.addEventListener('input', function () {
-          this.value = this.value;
-      });
-
-      let test = {
-        focus_input: function(val) {
-          input.value = val;
-          input.focus();
-        },
+    <script>
+      function fillInput() {
+        // fill the input with the URL hash if provided
+        var input = document.getElementById("input");
+        input.value = window.location.hash.slice(1); // remove leading #
+        input.focus();
 
-        test_reflush_changes: function() {
-          let inputEditable = SpecialPowers.wrap(input).QueryInterface(SpecialPowers.Ci.nsIDOMNSEditableElement);
-          let inputIme = inputEditable.editor.QueryInterface(SpecialPowers.Ci.nsIEditorIMESupport);
-          do_check_true(inputIme.composing);
-
-          // Ending the composition then setting the input value triggers the bug.
-          inputIme.forceCompositionEnd();
-          input.value = "good";
-        },
-
-        focus_resetting_input: function(val) {
-          resetting_input.value = val;
-          resetting_input.focus();
-        },
-
-        finish_test: function() {
-          java.disconnect();
-        },
-      };
-
-      var java = new JavaBridge(test);
+        // An input that resets the editor on every input by resetting the value property.
+        var resetting_input = document.getElementById("resetting-input");
+        resetting_input.addEventListener('input', function () {
+            this.value = this.value;
+        });
+      }
     </script>
   </body>
 </html>
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
@@ -10,66 +10,45 @@ import static org.mozilla.gecko.tests.he
 import static org.mozilla.gecko.tests.helpers.TextInputHelper.assertText;
 import static org.mozilla.gecko.tests.helpers.TextInputHelper.assertTextAndSelection;
 import static org.mozilla.gecko.tests.helpers.TextInputHelper.assertTextAndSelectionAt;
 import static org.mozilla.gecko.tests.helpers.TextInputHelper.getText;
 import static org.mozilla.gecko.tests.helpers.WaitHelper.waitFor;
 
 import org.mozilla.gecko.tests.components.GeckoViewComponent.InputConnectionTest;
 import org.mozilla.gecko.tests.helpers.GeckoHelper;
-import org.mozilla.gecko.tests.helpers.JavascriptBridge;
 import org.mozilla.gecko.tests.helpers.NavigationHelper;
 
 import com.jayway.android.robotium.solo.Condition;
 
 import android.view.KeyEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 
 /**
  * Tests the proper operation of GeckoInputConnection
  */
 public class testInputConnection extends UITest {
 
     private static final String INITIAL_TEXT = "foo";
 
-    private JavascriptBridge js;
-
-    @Override // UITest
-    public void setUp() throws Exception {
-        super.setUp();
-        js = new JavascriptBridge(this);
-    }
-
-    @Override // UITest
-    public void tearDown() throws Exception {
-        js.disconnect();
-        super.tearDown();
-    }
-
     public void testInputConnection() throws InterruptedException {
         GeckoHelper.blockForReady();
 
-        final String url = mStringHelper.ROBOCOP_INPUT_URL;
+        final String url = mStringHelper.ROBOCOP_INPUT_URL + "#" + INITIAL_TEXT;
         NavigationHelper.enterAndLoadUrl(url);
         mToolbar.assertTitle(url);
 
-        // First run tests inside the normal input field.
-        js.syncCall("focus_input", INITIAL_TEXT);
         mGeckoView.mTextInput
             .waitForInputConnection()
-            .testInputConnection(new BasicInputConnectionTest());
-
-        // Then switch focus to the resetting input field, and run tests there.
-        js.syncCall("focus_resetting_input", "");
-        mGeckoView.mTextInput
-            .waitForInputConnection()
+            // First run tests inside the normal input field.
+            .testInputConnection(new BasicInputConnectionTest())
+            // Then switch focus to the resetting input field, and run tests there.
+            .testInputConnection(new FocusNextInputFieldTest())
             .testInputConnection(new ResettingInputConnectionTest());
-
-        js.syncCall("finish_test");
     }
 
     private class BasicInputConnectionTest extends InputConnectionTest {
         @Override
         public void test(InputConnection ic, EditorInfo info) {
             // Test initial text provided by the hash in the test page URL
             assertText("Initial text matches URL hash", ic, INITIAL_TEXT);
 
@@ -173,35 +152,45 @@ public class testInputConnection extends
             assertTextAndSelectionAt("Can clear text", ic, "", 0);
 
             // Bug 1209465, cannot enter ideographic space character by itself (U+3000).
             ic.commitText("\u3000", 1);
             assertTextAndSelectionAt("Can commit ideographic space", ic, "\u3000", 1);
 
             ic.deleteSurroundingText(1, 0);
             assertTextAndSelectionAt("Can clear text", ic, "", 0);
+        }
+    }
 
-            // Bug 1051556, exception due to committing text changes during flushing.
-            ic.setComposingText("bad", 1);
-            assertTextAndSelectionAt("Can set the composing text", ic, "bad", 3);
-            js.asyncCall("test_reflush_changes");
-            // Wait for text change notifications to come in.
-            processGeckoEvents(ic);
-            assertTextAndSelectionAt("Can re-flush text changes", ic, "good", 4);
-            ic.setComposingText("done", 1);
-            assertTextAndSelectionAt("Can update composition after re-flushing", ic, "done", 4);
-            ic.finishComposingText();
-            assertTextAndSelectionAt("Can finish composing text", ic, "done", 4);
+    /**
+     * FocusNextInputFieldTest is used to switch focus from one input field to
+     * another on the test page by sending a tab key.
+     */
+    private class FocusNextInputFieldTest extends InputConnectionTest {
+        @Override
+        public void test(final InputConnection ic, EditorInfo info) {
+            // First clear all text.
+            ic.setSelection(0, 0);
+            assertSelectionAt("Can set selection to start", ic, 0);
 
-            ic.deleteSurroundingText(4, 0);
-            assertTextAndSelectionAt("Can clear text", ic, "", 0);
+            ic.deleteSurroundingText(0, Integer.MAX_VALUE);
+            assertTextAndSelectionAt("Can clear all text", ic, "", 0);
 
-            // Make sure we don't leave behind stale events for the following test.
+            // Set dummy text in currently focused input so we can check when we have switched focus.
+            final String dummyText = "dummy switch input text";
+            ic.commitText(dummyText, 1);
+            assertTextAndSelectionAt("Can commit text", ic, dummyText, dummyText.length());
+
+            // Finish processing events from the old input field.
             processGeckoEvents(ic);
             processInputConnectionEvents();
+
+            final KeyEvent tabKey = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB);
+            ic.sendKeyEvent(tabKey);
+            ic.sendKeyEvent(KeyEvent.changeAction(tabKey, KeyEvent.ACTION_UP));
         }
     }
 
     /**
      * ResettingInputConnectionTest performs tests on the resetting input in
      * robocop_input.html. Any test that uses the normal input should be put in
      * BasicInputConnectionTest.
      */
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -236,17 +236,16 @@ public:
     }
 
     Natives(nsWindow* aWindow)
         : window(*aWindow)
         , mIMERanges(new TextRangeArray())
         , mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet
         , mIMEUpdatingContext(false)
         , mIMESelectionChanged(false)
-        , mIMETextChangedDuringFlush(false)
     {}
 
     ~Natives();
 
     using Base::DisposeNative;
     using EditableBase::DisposeNative;
 
     /**
@@ -312,27 +311,21 @@ private:
     mozilla::widget::GeckoEditable::GlobalRef mEditable;
     nsAutoTArray<mozilla::UniquePtr<mozilla::WidgetEvent>, 8> mIMEKeyEvents;
     nsAutoTArray<IMETextChange, 4> mIMETextChanges;
     InputContext mInputContext;
     RefPtr<mozilla::TextRangeArray> mIMERanges;
     int32_t mIMEMaskEventsCount; // Mask events when > 0
     bool mIMEUpdatingContext;
     bool mIMESelectionChanged;
-    bool mIMETextChangedDuringFlush;
 
     void SendIMEDummyKeyEvents();
     void AddIMETextChange(const IMETextChange& aChange);
-
-    enum FlushChangesFlag {
-        FLUSH_FLAG_NONE,
-        FLUSH_FLAG_RETRY
-    };
-    void PostFlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
-    void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
+    void PostFlushIMEChanges();
+    void FlushIMEChanges();
 
 public:
     bool NotifyIME(const IMENotification& aIMENotification);
     void SetInputContext(const InputContext& aContext,
                          const InputContextAction& aAction);
     InputContext GetInputContext();
 
     // Handle an Android KeyEvent.
@@ -1978,20 +1971,16 @@ nsWindow::Natives::SendIMEDummyKeyEvents
     window.DispatchEvent(&upEvent);
 }
 
 void
 nsWindow::Natives::AddIMETextChange(const IMETextChange& aChange)
 {
     mIMETextChanges.AppendElement(aChange);
 
-    // We may not be in the middle of flushing,
-    // in which case this flag is meaningless.
-    mIMETextChangedDuringFlush = true;
-
     // Now that we added a new range we need to go back and
     // update all the ranges before that.
     // Ranges that have offsets which follow this new range
     // need to be updated to reflect new offsets
     const int32_t delta = aChange.mNewEnd - aChange.mOldEnd;
     for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
         IMETextChange& previousChange = mIMETextChanges[i];
         if (previousChange.mStart > aChange.mOldEnd) {
@@ -2040,116 +2029,85 @@ nsWindow::Natives::AddIMETextChange(cons
         mIMETextChanges.RemoveElementAt(srcIndex);
         // Any ranges that we skip over between src and dst are not mergeable
         // so we can safely continue the merge starting at dst
         srcIndex = dstIndex;
     }
 }
 
 void
-nsWindow::Natives::PostFlushIMEChanges(FlushChangesFlag aFlags)
+nsWindow::Natives::PostFlushIMEChanges()
 {
-    if (aFlags != FLUSH_FLAG_RETRY &&
-            (!mIMETextChanges.IsEmpty() || mIMESelectionChanged)) {
+    if (!mIMETextChanges.IsEmpty() || mIMESelectionChanged) {
         // Already posted
         return;
     }
 
     // Keep a strong reference to the window to keep 'this' alive.
     RefPtr<nsWindow> window(&this->window);
 
-    nsAppShell::gAppShell->PostEvent([this, window, aFlags] {
+    nsAppShell::gAppShell->PostEvent([this, window] {
         if (!window->Destroyed()) {
-            FlushIMEChanges(aFlags);
+            FlushIMEChanges();
         }
     });
 }
 
 void
-nsWindow::Natives::FlushIMEChanges(FlushChangesFlag aFlags)
+nsWindow::Natives::FlushIMEChanges()
 {
     // Only send change notifications if we are *not* masking events,
     // i.e. if we have a focused editor,
     NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount);
 
     nsCOMPtr<nsISelection> imeSelection;
     nsCOMPtr<nsIContent> imeRoot;
 
     // If we are receiving notifications, we must have selection/root content.
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(IMEStateManager::GetFocusSelectionAndRoot(
             getter_AddRefs(imeSelection), getter_AddRefs(imeRoot))));
 
     RefPtr<nsWindow> kungFuDeathGrip(&window);
     window.UserActivity();
 
-    struct TextRecord {
-        nsString text;
-        int32_t start;
-        int32_t oldEnd;
-        int32_t newEnd;
-    };
-    nsAutoTArray<TextRecord, 4> textTransaction;
-    if (mIMETextChanges.Length() > textTransaction.Capacity()) {
-        textTransaction.SetCapacity(mIMETextChanges.Length());
-    }
-
-    mIMETextChangedDuringFlush = false;
-
-    for (const IMETextChange &change : mIMETextChanges) {
+    for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) {
+        IMETextChange &change = mIMETextChanges[i];
+
         if (change.mStart == change.mOldEnd &&
                 change.mStart == change.mNewEnd) {
             continue;
         }
 
         WidgetQueryContentEvent event(true, eQueryTextContent, &window);
 
         if (change.mNewEnd != change.mStart) {
             window.InitEvent(event, nullptr);
             event.InitForQueryTextContent(change.mStart,
                                           change.mNewEnd - change.mStart);
             window.DispatchEvent(&event);
             NS_ENSURE_TRUE_VOID(event.mSucceeded);
             NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get());
         }
 
-        if (mIMETextChangedDuringFlush) {
-            // The query event above could have triggered more text changes to
-            // come in, as indicated by our flag. If that happens, try flushing
-            // IME changes again later.
-            if (!NS_WARN_IF(aFlags == FLUSH_FLAG_RETRY)) {
-                // Don't retry if already retrying, to avoid infinite loops.
-                PostFlushIMEChanges(FLUSH_FLAG_RETRY);
-            }
-            return;
-        }
-
-        textTransaction.AppendElement(
-                TextRecord{event.mReply.mString, change.mStart,
-                           change.mOldEnd, change.mNewEnd});
+        mEditable->OnTextChange(event.mReply.mString, change.mStart,
+                                change.mOldEnd, change.mNewEnd);
     }
-
     mIMETextChanges.Clear();
 
-    for (const TextRecord& record : textTransaction) {
-        mEditable->OnTextChange(record.text, record.start,
-                                record.oldEnd, record.newEnd);
-    }
-
     if (mIMESelectionChanged) {
-        mIMESelectionChanged = false;
-
         WidgetQueryContentEvent event(true, eQuerySelectedText, &window);
         window.InitEvent(event, nullptr);
         window.DispatchEvent(&event);
 
         NS_ENSURE_TRUE_VOID(event.mSucceeded);
         NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get());
 
         mEditable->OnSelectionChange(int32_t(event.GetSelectionStart()),
                                      int32_t(event.GetSelectionEnd()));
+        mIMESelectionChanged = false;
     }
 }
 
 bool
 nsWindow::Natives::NotifyIME(const IMENotification& aIMENotification)
 {
     MOZ_ASSERT(mEditable);