Bug 1250314 - Let changes flush when committing or canceling composition; r=esawin
authorJim Chen <nchen@mozilla.com>
Fri, 26 Feb 2016 14:38:57 -0500
changeset 324098 e299632214495958b4964759f4920bc2b578ff51
parent 324097 3271fbe16bbb558e3b6022589a84b96ce1bd0ef5
child 324099 304c2876b1a287f0aaf30bb6a145a991e5516847
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersesawin
bugs1250314
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1250314 - Let changes flush when committing or canceling composition; r=esawin Flush IME changes when committing or canceling the composition, before sending a notification, so that the Gecko and Java sides are on the same page. Also, use the GeckoEditableListener constants when calling notifyIME so we don't rely on the Gecko platform constants having the same values as our Java constants.
mobile/android/base/java/org/mozilla/gecko/GeckoEditableListener.java
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoEditableListener.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoEditableListener.java
@@ -12,19 +12,23 @@ import org.mozilla.gecko.annotation.Wrap
  * to the Editable.
  */
 interface GeckoEditableListener {
     // IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko
     @WrapForJNI
     int NOTIFY_IME_OPEN_VKB = -2;
     @WrapForJNI
     int NOTIFY_IME_REPLY_EVENT = -1;
+    @WrapForJNI
     int NOTIFY_IME_OF_FOCUS = 1;
+    @WrapForJNI
     int NOTIFY_IME_OF_BLUR = 2;
+    @WrapForJNI
     int NOTIFY_IME_TO_COMMIT_COMPOSITION = 8;
+    @WrapForJNI
     int NOTIFY_IME_TO_CANCEL_COMPOSITION = 9;
     // IME enabled state for notifyIMEContext()
     int IME_STATE_DISABLED = 0;
     int IME_STATE_ENABLED = 1;
     int IME_STATE_PASSWORD = 2;
     int IME_STATE_PLUGIN = 3;
 
     void notifyIME(int type);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -1757,20 +1757,28 @@ public:
     template<class Impl> class Natives;
 };
 
 class GeckoEditableListener : public mozilla::jni::ObjectBase<GeckoEditableListener, jobject>
 {
 public:
     explicit GeckoEditableListener(const Context& ctx) : ObjectBase<GeckoEditableListener, jobject>(ctx) {}
 
+    static const int32_t NOTIFY_IME_OF_BLUR = 2;
+
+    static const int32_t NOTIFY_IME_OF_FOCUS = 1;
+
     static const int32_t NOTIFY_IME_OPEN_VKB = -2;
 
     static const int32_t NOTIFY_IME_REPLY_EVENT = -1;
 
+    static const int32_t NOTIFY_IME_TO_CANCEL_COMPOSITION = 9;
+
+    static const int32_t NOTIFY_IME_TO_COMMIT_COMPOSITION = 8;
+
     static const bool isMultithreaded = false;
 
 };
 
 class GeckoJavaSampler : public mozilla::jni::ObjectBase<GeckoJavaSampler, jobject>
 {
 public:
     explicit GeckoJavaSampler(const Context& ctx) : ObjectBase<GeckoJavaSampler, jobject>(ctx) {}
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -323,16 +323,17 @@ private:
     void AddIMETextChange(const IMETextChange& aChange);
 
     enum FlushChangesFlag {
         FLUSH_FLAG_NONE,
         FLUSH_FLAG_RETRY
     };
     void PostFlushIMEChanges();
     void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
+    void AsyncNotifyIME(int32_t aNotification);
 
 public:
     bool NotifyIME(const IMENotification& aIMENotification);
     void SetInputContext(const InputContext& aContext,
                          const InputContextAction& aAction);
     InputContext GetInputContext();
 
     // RAII helper class that automatically sends an event reply through
@@ -2756,61 +2757,80 @@ nsWindow::GeckoViewSupport::FlushIMEChan
     }
 
     if (mIMESelectionChanged) {
         mIMESelectionChanged = false;
         mEditable->OnSelectionChange(selStart, selEnd);
     }
 }
 
+void
+nsWindow::GeckoViewSupport::AsyncNotifyIME(int32_t aNotification)
+{
+    // Keep a strong reference to the window to keep 'this' alive.
+    RefPtr<nsWindow> window(&this->window);
+
+    nsAppShell::PostEvent([this, window, aNotification] {
+        if (mIMEMaskEventsCount) {
+            return;
+        }
+
+        mEditable->NotifyIME(aNotification);
+    });
+}
+
 bool
 nsWindow::GeckoViewSupport::NotifyIME(const IMENotification& aIMENotification)
 {
     MOZ_ASSERT(mEditable);
 
     switch (aIMENotification.mMessage) {
         case REQUEST_TO_COMMIT_COMPOSITION: {
             ALOGIME("IME: REQUEST_TO_COMMIT_COMPOSITION");
+
             window.RemoveIMEComposition();
-            mEditable->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
+
+            AsyncNotifyIME(GeckoEditableListener::
+                           NOTIFY_IME_TO_COMMIT_COMPOSITION);
             return true;
         }
 
         case REQUEST_TO_CANCEL_COMPOSITION: {
             ALOGIME("IME: REQUEST_TO_CANCEL_COMPOSITION");
+            RefPtr<nsWindow> kungFuDeathGrip(&window);
 
             // Cancel composition on Gecko side
             if (window.GetIMEComposition()) {
-                RefPtr<nsWindow> kungFuDeathGrip(&window);
                 WidgetCompositionEvent compositionCommitEvent(
                         true, eCompositionCommit, &window);
                 window.InitEvent(compositionCommitEvent, nullptr);
                 // Dispatch it with empty mData value for canceling composition.
                 window.DispatchEvent(&compositionCommitEvent);
             }
 
-            mEditable->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
+            AsyncNotifyIME(GeckoEditableListener::
+                           NOTIFY_IME_TO_CANCEL_COMPOSITION);
             return true;
         }
 
         case NOTIFY_IME_OF_FOCUS: {
             ALOGIME("IME: NOTIFY_IME_OF_FOCUS");
-            mEditable->NotifyIME(NOTIFY_IME_OF_FOCUS);
+            mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_OF_FOCUS);
             return true;
         }
 
         case NOTIFY_IME_OF_BLUR: {
             ALOGIME("IME: NOTIFY_IME_OF_BLUR");
 
             // Mask events because we lost focus. On the next focus event,
             // Gecko will notify Java, and Java will send an acknowledge focus
             // event back to Gecko. That is where we unmask event handling
             mIMEMaskEventsCount++;
 
-            mEditable->NotifyIME(NOTIFY_IME_OF_BLUR);
+            mEditable->NotifyIME(GeckoEditableListener::NOTIFY_IME_OF_BLUR);
             return true;
         }
 
         case NOTIFY_IME_OF_SELECTION_CHANGE: {
             ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE");
 
             PostFlushIMEChanges();
             mIMESelectionChanged = true;
@@ -3089,16 +3109,18 @@ nsWindow::GeckoViewSupport::OnImeUpdateC
 {
     AutoIMESynchronize as(this);
 
     if (mIMEMaskEventsCount > 0) {
         // Not focused.
         return;
     }
 
+    RefPtr<nsWindow> kungFuDeathGrip(&window);
+
     // A composition with no ranges means we want to set the selection.
     if (mIMERanges->IsEmpty()) {
         MOZ_ASSERT(aStart >= 0 && aEnd >= 0);
         window.RemoveIMEComposition();
 
         WidgetSelectionEvent selEvent(true, eSetSelection, &window);
         window.InitEvent(selEvent, nullptr);
 
@@ -3114,17 +3136,16 @@ nsWindow::GeckoViewSupport::OnImeUpdateC
     /*
         Update the composition from aStart to aEnd using
           information from added ranges. This is only used for
           visual indication and does not affect the text content.
           Only the offsets are specified and not the text content
           to eliminate the possibility of this event altering the
           text content unintentionally.
     */
-    RefPtr<nsWindow> kungFuDeathGrip(&window);
     const auto composition(window.GetIMEComposition());
     MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
 
     WidgetCompositionEvent event(true, eCompositionChange, &window);
     window.InitEvent(event, nullptr);
 
     event.mRanges = new TextRangeArray();
     mIMERanges.swap(event.mRanges);