Bug 1343451 - part 5: Make GeckoEditableSupport dispatch dummy eKeyDown and eKeyUp event during composition always r?jchen draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 27 Feb 2018 17:24:35 +0900
changeset 767186 0817f9fe271a5ab7b40577846feb370f234ec137
parent 766630 7f45e7f41ee946b755930f871195c29796780744
child 767187 b027c344630a7476bc3721f90017f3894000e278
push id102535
push usermasayuki@d-toybox.com
push dateWed, 14 Mar 2018 03:14:39 +0000
reviewersjchen
bugs1343451, 354358
milestone61.0a1
Bug 1343451 - part 5: Make GeckoEditableSupport dispatch dummy eKeyDown and eKeyUp event during composition always r?jchen On Android, GeckoEditableSupport has already dispatched eKeyDown event and eKeyUp event even during composition. I.e., the pref which will be enabled by bug 354358 has already been set to true only on Android. On the other hand, GeckoEditableSupport does not dispatch them if content listens to "input", "compositionstart", "compositionupdate" or "compositionend". So, different from the other platforms, we need additional pref to make the new behavior behind pref. Therefore, this patch adds a new pref, "intl.ime.hack.on_any_apps.fire_key_events_for_composition", to override existing "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition" pref. And sets mKeyCode and mKeyNameIndex of the dummy KeyboardEvents to NS_VK_PROCESSKEY and KEY_NAME_INDEX_Process. MozReview-Commit-ID: Fuy0Ir2xiO5
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
modules/libpref/init/all.js
widget/android/GeckoEditableSupport.cpp
widget/android/GeckoEditableSupport.h
--- 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
@@ -32,17 +32,17 @@ public class testInputConnection extends
         GeckoHelper.blockForReady();
 
         // Spatial navigation interferes with design-mode key event tests.
         mActions.setPref("snav.enabled", false, /* flush */ false);
         // Enable "selectionchange" events for input/textarea.
         mActions.setPref("dom.select_events.enabled", true, /* flush */ false);
         mActions.setPref("dom.select_events.textcontrols.enabled", true, /* flush */ false);
         // Enable dummy key synthesis.
-        mActions.setPref("intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
+        mActions.setPref("intl.ime.hack.on_any_apps.fire_key_events_for_composition",
                          true, /* flush */ false);
 
         final String url = mStringHelper.ROBOCOP_INPUT_URL;
         NavigationHelper.enterAndLoadUrl(url);
         mToolbar.assertTitle(url);
 
         // First run tests inside the normal input field.
         getJS().syncCall("focus_input", INITIAL_TEXT);
@@ -207,28 +207,21 @@ public class testInputConnection extends
             ic.finishComposingText(); // Does not synthesize key.
             assertTextAndSelectionAt("Can finish F key", ic, "f", 1);
             ic.commitText("o", 1); // Synthesizes O key.
             assertTextAndSelectionAt("Can commit O key", ic, "fo", 2);
             ic.commitText("of", 1); // Synthesizes dummy key.
             assertTextAndSelectionAt("Can commit non-key string", ic, "foof", 4);
 
             getJS().syncCall("end_key_log");
-            if (mType.equals("designMode")) {
-                // designMode doesn't support dummy key synthesis.
-                fAssertEquals("Can synthesize keys",
-                              "keydown:o,casm;keypress:o,casm;keyup:o,casm;", // O key
-                              getKeyLog());
-            } else {
-                fAssertEquals("Can synthesize keys",
-                              "keydown:Unidentified,casm;keyup:Unidentified,casm;" + // Dummy
-                              "keydown:o,casm;keypress:o,casm;keyup:o,casm;" +       // O key
-                              "keydown:Unidentified,casm;keyup:Unidentified,casm;",  // Dummy
-                              getKeyLog());
-            }
+            fAssertEquals("Can synthesize keys",
+                          "keydown:Process,casm;keyup:Process,casm;" +     // Dummy
+                          "keydown:o,casm;keypress:o,casm;keyup:o,casm;" + // O key
+                          "keydown:Process,casm;keyup:Process,casm;",      // Dummy
+                          getKeyLog());
 
             ic.deleteSurroundingText(4, 0);
             assertTextAndSelectionAt("Can clear text", ic, "", 0);
 
             // Bug 1133802, duplication when setting the same composing text more than once.
             ic.setComposingText("foo", 1);
             assertTextAndSelectionAt("Can set the composing text", ic, "foo", 3);
             ic.setComposingText("foo", 1);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2387,22 +2387,36 @@ pref("intl.ellipsis",                   
 // OS locale set instead of the app locale set.
 pref("intl.regional_prefs.use_os_locales",  false);
 // fallback charset list for Unicode conversion (converting from Unicode)
 // currently used for mail send only to handle symbol characters (e.g Euro, trademark, smartquotes)
 // for ISO-8859-1
 pref("intl.fallbackCharsetList.ISO-8859-1", "windows-1252");
 pref("font.language.group",                 "chrome://global/locale/intl.properties");
 
-// Android-specific pref to use key-events-only mode for IME-unaware webapps.
+// Android-specific pref to control if keydown and keyup events are fired even
+// in during composition.  Note that those prefs are ignored if
+// "dom.keyboardevent.dispatch_during_composition" is false.
 #ifdef MOZ_WIDGET_ANDROID
+// If true, dispatch the keydown and keyup events on any web apps even during
+// composition.
+#ifdef EARLY_BETA_OR_EARLIER
+pref("intl.ime.hack.on_any_apps.fire_key_events_for_composition", true);
+#else // #ifdef EARLY_BETA_OR_EARLIER
+pref("intl.ime.hack.on_any_apps.fire_key_events_for_composition", false);
+#endif // #ifdef EARLY_BETA_OR_EARLIER #else
+// If true and the above pref is false, dispatch the keydown and keyup events
+// only on IME-unaware web apps.  So, this supports web apps which listen to
+// only keydown or keyup event to get a change to do something at every text
+// input.
 pref("intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", true);
-#else
+#else // #ifdef MOZ_WIDGET_ANDROID
+pref("intl.ime.hack.on_any_apps.fire_key_events_for_composition", false);
 pref("intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition", false);
-#endif
+#endif // #ifdef MOZ_WIDGET_ANDROID #else
 
 // If you use legacy Chinese IME which puts an ideographic space to composition
 // string as placeholder, this pref might be useful.  If this is true and when
 // web contents forcibly commits composition (e.g., moving focus), the
 // ideographic space will be ignored (i.e., commits with empty string).
 pref("intl.ime.remove_placeholder_character_at_commit", false);
 
 pref("intl.uidirection", -1); // -1 to set from locale; 0 for LTR; 1 for RTL
--- a/widget/android/GeckoEditableSupport.cpp
+++ b/widget/android/GeckoEditableSupport.cpp
@@ -8,16 +8,17 @@
 
 #include "AndroidRect.h"
 #include "KeyEvent.h"
 #include "PuppetWidget.h"
 #include "nsIContent.h"
 #include "nsISelection.h"
 
 #include "mozilla/IMEStateManager.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEventDispatcherListener.h"
 #include "mozilla/TextEvents.h"
 
 #include <android/api-level.h>
 #include <android/input.h>
 #include <android/log.h>
 
@@ -553,20 +554,36 @@ ConvertRectArrayToJavaRectFArray(const n
         rects->SetElement(i, rect);
     }
     return rects;
 }
 
 namespace mozilla {
 namespace widget {
 
+bool GeckoEditableSupport::sDispatchKeyEventsInCompositionForAnyApps = false;
+
 NS_IMPL_ISUPPORTS(GeckoEditableSupport,
                   TextEventDispatcherListener,
                   nsISupportsWeakReference)
 
+void
+GeckoEditableSupport::ObservePrefs()
+{
+    static bool sIsObservingPref = false;
+    if (sIsObservingPref) {
+        return;
+    }
+    sIsObservingPref = true;
+    Preferences::AddBoolVarCache(
+        &sDispatchKeyEventsInCompositionForAnyApps,
+        "intl.ime.hack.on_any_apps.fire_key_events_for_composition",
+        false);
+}
+
 RefPtr<TextComposition>
 GeckoEditableSupport::GetComposition() const
 {
     nsCOMPtr<nsIWidget> widget = GetWidget();
     return widget ? IMEStateManager::GetTextCompositionFor(widget) : nullptr;
 }
 
 void
@@ -663,27 +680,31 @@ GeckoEditableSupport::OnKeyEvent(int32_t
     } else {
         dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
     }
 }
 
 /*
  * Send dummy key events for pages that are unaware of input events,
  * to provide web compatibility for pages that depend on key events.
- * Our dummy key events have 0 as the keycode.
  */
 void
 GeckoEditableSupport::SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg)
 {
     nsEventStatus status = nsEventStatus_eIgnore;
     MOZ_ASSERT(mDispatcher);
 
     WidgetKeyboardEvent event(true, msg, aWidget);
     event.mTime = PR_Now() / 1000;
-    MOZ_ASSERT(event.mKeyCode == 0);
+    // TODO: If we can know scan code of the key event which caused replacing
+    //       composition string, we should set mCodeNameIndex here.  Then,
+    //       we should rename this method because it becomes not a "dummy"
+    //       keyboard event.
+    event.mKeyCode = NS_VK_PROCESSKEY;
+    event.mKeyNameIndex = KEY_NAME_INDEX_Process;
     NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
     mDispatcher->DispatchKeyboardEvent(msg, event, status);
 }
 
 void
 GeckoEditableSupport::AddIMETextChange(const IMETextChange& aChange)
 {
     mIMETextChanges.AppendElement(aChange);
@@ -1022,17 +1043,18 @@ GeckoEditableSupport::OnImeReplaceText(i
          * change notification. However, the Java side still expects
          * one, so we manually generate a notification. */
         IMETextChange dummyChange;
         dummyChange.mStart = aStart;
         dummyChange.mOldEnd = dummyChange.mNewEnd = aEnd;
         AddIMETextChange(dummyChange);
     }
 
-    if (mInputContext.mMayBeIMEUnaware) {
+    if (sDispatchKeyEventsInCompositionForAnyApps ||
+        mInputContext.mMayBeIMEUnaware) {
         SendIMEDummyKeyEvent(widget, eKeyDown);
         if (!mDispatcher || widget->Destroyed()) {
             return;
         }
     }
 
     if (composing) {
         mDispatcher->SetPendingComposition(string, mIMERanges);
@@ -1041,17 +1063,18 @@ GeckoEditableSupport::OnImeReplaceText(i
         mIMERanges->Clear();
     } else if (!string.IsEmpty() || mDispatcher->IsComposing()) {
         mDispatcher->CommitComposition(status, &string);
     }
     if (!mDispatcher || widget->Destroyed()) {
         return;
     }
 
-    if (mInputContext.mMayBeIMEUnaware) {
+    if (sDispatchKeyEventsInCompositionForAnyApps ||
+        mInputContext.mMayBeIMEUnaware) {
         SendIMEDummyKeyEvent(widget, eKeyUp);
         // Widget may be destroyed after dispatching the above event.
     }
 }
 
 void
 GeckoEditableSupport::OnImeAddCompositionRange(
         int32_t aStart, int32_t aEnd, int32_t aRangeType, int32_t aRangeStyle,
--- a/widget/android/GeckoEditableSupport.h
+++ b/widget/android/GeckoEditableSupport.h
@@ -101,16 +101,20 @@ class GeckoEditableSupport final
     AutoTArray<IMETextChange, 4> mIMETextChanges;
     RefPtr<TextRangeArray> mIMERanges;
     int32_t mIMEMaskEventsCount; // Mask events when > 0.
     bool mIMEUpdatingContext;
     bool mIMESelectionChanged;
     bool mIMETextChangedDuringFlush;
     bool mIMEMonitorCursor;
 
+    static bool sDispatchKeyEventsInCompositionForAnyApps;
+
+    void ObservePrefs();
+
     nsIWidget* GetWidget() const
     {
         return mDispatcher ? mDispatcher->GetWidget() : mWindow;
     }
 
     nsresult BeginInputTransaction(TextEventDispatcher* aDispatcher)
     {
         if (mIsRemote) {
@@ -175,22 +179,25 @@ public:
         , mEditable(aEditableChild)
         , mEditableAttached(!mIsRemote)
         , mIMERanges(new TextRangeArray())
         , mIMEMaskEventsCount(1) // Mask IME events since there's no focus yet
         , mIMEUpdatingContext(false)
         , mIMESelectionChanged(false)
         , mIMETextChangedDuringFlush(false)
         , mIMEMonitorCursor(false)
-    {}
+    {
+        ObservePrefs();
+    }
 
     // Constructor for content process GeckoEditableChild.
     GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild)
         : GeckoEditableSupport(nullptr, nullptr, aEditableChild)
-    {}
+    {
+    }
 
     NS_DECL_ISUPPORTS
 
     // TextEventDispatcherListener methods
     NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                          const IMENotification& aNotification) override;
 
     NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;