Bug 1137557 - Part 0: TextEventDispatcher shouldn't forward keyboard events coming from TextInputProcessor to the parent process. r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 04 Aug 2015 05:52:00 -0400
changeset 293392 692a0e1e46a6805a266c86dd1de5cec2ede1b930
parent 293391 9d67edd311f51efea70b387cd85b28c9603cc6b0
child 293393 1d47c8de65633109bd821654e873ad8abd418899
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1137557
milestone43.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 1137557 - Part 0: TextEventDispatcher shouldn't forward keyboard events coming from TextInputProcessor to the parent process. r=smaug
dom/base/TextInputProcessor.cpp
dom/base/TextInputProcessor.h
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.h
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
+#include "gfxPrefs.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextInputProcessor.h"
 #include "nsIDocShell.h"
 #include "nsIWidget.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
@@ -767,16 +768,33 @@ TextInputProcessor::Keydown(nsIDOMKeyEve
   WidgetKeyboardEvent* originalKeyEvent =
     aDOMKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   if (NS_WARN_IF(!originalKeyEvent)) {
     return NS_ERROR_INVALID_ARG;
   }
   return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aDoDefault);
 }
 
+TextEventDispatcher::DispatchTo
+TextInputProcessor::GetDispatchTo() const
+{
+  // Support asynchronous tests.
+  if (mForTests) {
+    return gfxPrefs::TestEventsAsyncEnabled() ?
+             TextEventDispatcher::eDispatchToParentProcess :
+             TextEventDispatcher::eDispatchToCurrentProcess;
+  }
+
+  // Otherwise, TextInputProcessor supports only keyboard apps on B2G.
+  // Keyboard apps on B2G doesn't want to dispatch keyboard events to
+  // chrome process. Therefore, this should dispatch key events only in
+  // the current process.
+  return TextEventDispatcher::eDispatchToCurrentProcess;
+}
+
 nsresult
 TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
                                     uint32_t aKeyFlags,
                                     bool aAllowToDispatchKeypress,
                                     bool& aDoDefault)
 {
   aDoDefault = false;
 
@@ -811,24 +829,25 @@ TextInputProcessor::KeydownInternal(cons
   nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
                                       nsEventStatus_eConsumeNoDefault;
-  if (!mDispatcher->DispatchKeyboardEvent(NS_KEY_DOWN, keyEvent, status)) {
+  if (!mDispatcher->DispatchKeyboardEvent(NS_KEY_DOWN, keyEvent, status,
+                                          GetDispatchTo())) {
     // If keydown event isn't dispatched, we don't need to dispatch keypress
     // events.
     return NS_OK;
   }
 
   if (aAllowToDispatchKeypress) {
-    mDispatcher->MaybeDispatchKeypressEvents(keyEvent, status);
+    mDispatcher->MaybeDispatchKeypressEvents(keyEvent, status, GetDispatchTo());
   }
 
   aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::Keyup(nsIDOMKeyEvent* aDOMKeyEvent,
@@ -885,17 +904,18 @@ TextInputProcessor::KeyupInternal(const 
   nsRefPtr<TextEventDispatcher> kungfuDeathGrip(mDispatcher);
   rv = IsValidStateForComposition();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
                                       nsEventStatus_eConsumeNoDefault;
-  mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status);
+  mDispatcher->DispatchKeyboardEvent(NS_KEY_UP, keyEvent, status,
+                                     GetDispatchTo());
   aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
                                      bool* aActive)
 {
--- a/dom/base/TextInputProcessor.h
+++ b/dom/base/TextInputProcessor.h
@@ -4,28 +4,25 @@
  * 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/. */
 
 #ifndef mozilla_dom_textinputprocessor_h_
 #define mozilla_dom_textinputprocessor_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEventDispatcherListener.h"
 #include "nsAutoPtr.h"
 #include "nsITextInputProcessor.h"
 #include "nsITextInputProcessorCallback.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
-namespace widget{
-class TextEventDispatcher;
-} // namespace widget
-
 class TextInputProcessor final : public nsITextInputProcessor
                                , public widget::TextEventDispatcherListener
 {
   typedef mozilla::widget::IMENotification IMENotification;
   typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
 
 public:
   TextInputProcessor();
@@ -59,16 +56,17 @@ private:
              uint32_t aKeyFlags = 0);
   nsresult KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
                            uint32_t aKeyFlags,
                            bool aAllowToDispatchKeypress,
                            bool& aDoDefault);
   nsresult KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
                          uint32_t aKeyFlags,
                          bool& aDoDefault);
+  TextEventDispatcher::DispatchTo GetDispatchTo() const;
   nsresult IsValidStateForComposition();
   void UnlinkFromTextEventDispatcher();
   nsresult PrepareKeyboardEventToDispatch(WidgetKeyboardEvent& aKeyboardEvent,
                                           uint32_t aKeyFlags);
   bool IsValidEventTypeForComposition(
          const WidgetKeyboardEvent& aKeyboardEvent) const;
   nsresult PrepareKeyboardEventForComposition(
              nsIDOMKeyEvent* aDOMKeyEvent,
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -136,25 +136,43 @@ TextEventDispatcher::InitEvent(WidgetGUI
   aEvent.mFlags.mIsSynthesizedForTests = mForTests;
 }
 
 nsresult
 TextEventDispatcher::DispatchEvent(nsIWidget* aWidget,
                                    WidgetGUIEvent& aEvent,
                                    nsEventStatus& aStatus)
 {
+  MOZ_ASSERT(!aEvent.AsInputEvent(), "Use DispatchInputEvent()");
+
+  nsRefPtr<TextEventDispatcher> kungFuDeathGrip(this);
+  nsCOMPtr<nsIWidget> widget(aWidget);
+  mDispatchingEvent++;
+  nsresult rv = widget->DispatchEvent(&aEvent, aStatus);
+  mDispatchingEvent--;
+  return rv;
+}
+
+nsresult
+TextEventDispatcher::DispatchInputEvent(nsIWidget* aWidget,
+                                        WidgetInputEvent& aEvent,
+                                        nsEventStatus& aStatus,
+                                        DispatchTo aDispatchTo)
+{
   nsRefPtr<TextEventDispatcher> kungFuDeathGrip(this);
   nsCOMPtr<nsIWidget> widget(aWidget);
   mDispatchingEvent++;
 
+  // If the event is dispatched via nsIWidget::DispatchInputEvent(), it
+  // sends the event to the parent process first since APZ needs to handle it
+  // first.  However, some callers (e.g., keyboard apps on B2G and tests
+  // expecting synchronous dispatch) don't want this to do that.
   nsresult rv = NS_OK;
-  if (aEvent.AsInputEvent() &&
-      (!aEvent.mFlags.mIsSynthesizedForTests || gfxPrefs::TestEventsAsyncEnabled()))
-  {
-    aStatus = widget->DispatchInputEvent(aEvent.AsInputEvent());
+  if (aDispatchTo == eDispatchToParentProcess) {
+    aStatus = widget->DispatchInputEvent(&aEvent);
   } else {
     rv = widget->DispatchEvent(&aEvent, aStatus);
   }
 
   mDispatchingEvent--;
   return rv;
 }
 
@@ -281,26 +299,29 @@ TextEventDispatcher::NotifyIME(const IME
   }
   return rv;
 }
 
 bool
 TextEventDispatcher::DispatchKeyboardEvent(
                        uint32_t aMessage,
                        const WidgetKeyboardEvent& aKeyboardEvent,
-                       nsEventStatus& aStatus)
+                       nsEventStatus& aStatus,
+                       DispatchTo aDispatchTo)
 {
-  return DispatchKeyboardEventInternal(aMessage, aKeyboardEvent, aStatus);
+  return DispatchKeyboardEventInternal(aMessage, aKeyboardEvent, aStatus,
+                                       aDispatchTo);
 }
 
 bool
 TextEventDispatcher::DispatchKeyboardEventInternal(
                        uint32_t aMessage,
                        const WidgetKeyboardEvent& aKeyboardEvent,
                        nsEventStatus& aStatus,
+                       DispatchTo aDispatchTo,
                        uint32_t aIndexOfKeypress)
 {
   MOZ_ASSERT(aMessage == NS_KEY_DOWN || aMessage == NS_KEY_UP ||
              aMessage == NS_KEY_PRESS, "Invalid aMessage value");
   nsresult rv = GetState();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
@@ -369,24 +390,25 @@ TextEventDispatcher::DispatchKeyboardEve
   // XXX Currently, we don't support to dispatch key event with native key
   //     event information.
   keyEvent.mNativeKeyEvent = nullptr;
   // XXX Currently, we don't support to dispatch key events with data for
   // plugins.
   keyEvent.mPluginEvent.Clear();
   // TODO: Manage mUniqueId here.
 
-  DispatchEvent(mWidget, keyEvent, aStatus);
+  DispatchInputEvent(mWidget, keyEvent, aStatus, aDispatchTo);
   return true;
 }
 
 bool
 TextEventDispatcher::MaybeDispatchKeypressEvents(
                        const WidgetKeyboardEvent& aKeyboardEvent,
-                       nsEventStatus& aStatus)
+                       nsEventStatus& aStatus,
+                       DispatchTo aDispatchTo)
 {
   // If the key event was consumed, keypress event shouldn't be fired.
   if (aStatus == nsEventStatus_eConsumeNoDefault) {
     return false;
   }
 
   // If the key isn't a printable key or just inputting one character or
   // no character, we should dispatch only one keypress.  Otherwise, i.e.,
@@ -396,17 +418,17 @@ TextEventDispatcher::MaybeDispatchKeypre
     aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ?
       1 : std::max(static_cast<nsAString::size_type>(1),
                    aKeyboardEvent.mKeyValue.Length());
   bool isDispatched = false;
   bool consumed = false;
   for (size_t i = 0; i < keypressCount; i++) {
     aStatus = nsEventStatus_eIgnore;
     if (!DispatchKeyboardEventInternal(NS_KEY_PRESS, aKeyboardEvent,
-                                       aStatus, i)) {
+                                       aStatus, aDispatchTo, i)) {
       // The widget must have been gone.
       break;
     }
     isDispatched = true;
     if (!consumed) {
       consumed = (aStatus == nsEventStatus_eConsumeNoDefault);
     }
   }
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -175,51 +175,76 @@ public:
     mPendingComposition.Clear();
   }
 
   /**
    * @see nsIWidget::NotifyIME()
    */
   nsresult NotifyIME(const IMENotification& aIMENotification);
 
+
+  /**
+   * DispatchTo indicates whether the event may be dispatched to its parent
+   * process first (if there is) or not.  If the event is dispatched to the
+   * parent process, APZ will handle it first.  Otherwise, the event won't be
+   * handled by APZ if it's in a child process.
+   */
+  enum DispatchTo
+  {
+    // The event may be dispatched to its parent process if there is a parent.
+    // In such case, the event will be handled asynchronously.  Additionally,
+    // the event may be sent to its child process if a child process (including
+    // the dispatching process) has focus.
+    eDispatchToParentProcess = 0,
+    // The event must be dispatched in the current process.  But of course,
+    // the event may be sent to a child process when it has focus.  If there is
+    // no child process, the event may be handled synchronously.
+    eDispatchToCurrentProcess = 1
+  };
+
   /**
    * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
    *
    * @param aMessage        Must be NS_KEY_DOWN or NS_KEY_UP.
    *                        Use MaybeDispatchKeypressEvents() for dispatching
    *                        NS_KEY_PRESS.
    * @param aKeyboardEvent  A keyboard event.
    * @param aStatus         If dispatching event should be marked as consumed,
    *                        set nsEventStatus_eConsumeNoDefault.  Otherwise,
    *                        set nsEventStatus_eIgnore.  After dispatching
    *                        a event and it's consumed this returns
    *                        nsEventStatus_eConsumeNoDefault.
+   * @param aDispatchTo     See comments of DispatchTo.
    * @return                true if an event is dispatched.  Otherwise, false.
    */
   bool DispatchKeyboardEvent(uint32_t aMessage,
                              const WidgetKeyboardEvent& aKeyboardEvent,
-                             nsEventStatus& aStatus);
+                             nsEventStatus& aStatus,
+                             DispatchTo aDispatchTo = eDispatchToParentProcess);
 
   /**
    * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
    * generated from aKeydownEvent.
    *
    * @param aKeyboardEvent  A keyboard event.
    * @param aStatus         Sets the result when the caller dispatches
    *                        aKeyboardEvent.  Note that if the value is
    *                        nsEventStatus_eConsumeNoDefault, this does NOT
    *                        dispatch keypress events.
    *                        When this method dispatches one or more keypress
    *                        events and one of them is consumed, this returns
    *                        nsEventStatus_eConsumeNoDefault.
+   * @param aDispatchTo     See comments of DispatchTo.
    * @return                true if one or more events are dispatched.
    *                        Otherwise, false.
    */
   bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
-                                   nsEventStatus& aStatus);
+                                   nsEventStatus& aStatus,
+                                   DispatchTo aDispatchTo =
+                                     eDispatchToParentProcess);
 
 private:
   // mWidget is owner of the instance.  When this is created, this is set.
   // And when mWidget is released, this is cleared by OnDestroyWidget().
   // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
   // return true).
   nsIWidget* mWidget;
   // mListener is a weak reference to TextEventDispatcherListener.  That might
@@ -268,24 +293,35 @@ private:
              bool aForTests);
 
   /**
    * InitEvent() initializes aEvent.  This must be called before dispatching
    * the event.
    */
   void InitEvent(WidgetGUIEvent& aEvent) const;
 
+
   /**
    * DispatchEvent() dispatches aEvent on aWidget.
    */
   nsresult DispatchEvent(nsIWidget* aWidget,
                          WidgetGUIEvent& aEvent,
                          nsEventStatus& aStatus);
 
   /**
+   * DispatchInputEvent() dispatches aEvent on aWidget.
+   *
+   * @param aDispatchTo     See comments of DispatchTo.
+   */
+  nsresult DispatchInputEvent(nsIWidget* aWidget,
+                              WidgetInputEvent& aEvent,
+                              nsEventStatus& aStatus,
+                              DispatchTo aDispatchTo);
+
+  /**
    * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
    * been started it yet.
    *
    * @param aStatus         If it succeeded to start composition normally, this
    *                        returns nsEventStatus_eIgnore.  Otherwise, e.g.,
    *                        the composition is canceled during dispatching
    *                        compositionstart event, this returns
    *                        nsEventStatus_eConsumeNoDefault.  In this case,
@@ -303,27 +339,30 @@ private:
    * @param aKeyboardEvent  A keyboard event.  If aMessage is NS_KEY_PRESS and
    *                        the event is for second or later character, its
    *                        mKeyValue should be empty string.
    * @param aStatus         If dispatching event should be marked as consumed,
    *                        set nsEventStatus_eConsumeNoDefault.  Otherwise,
    *                        set nsEventStatus_eIgnore.  After dispatching
    *                        a event and it's consumed this returns
    *                        nsEventStatus_eConsumeNoDefault.
+   * @param aDispatchTo     See comments of DispatchTo.
    * @param aIndexOfKeypress    This must be 0 if aMessage isn't NS_KEY_PRESS or
    *                            aKeyboard.mKeyNameIndex isn't
    *                            KEY_NAME_INDEX_USE_STRING.  Otherwise, i.e.,
    *                            when an NS_KEY_PRESS event causes inputting
    *                            text, this must be between 0 and
    *                            mKeyValue.Length() - 1 since keypress events
    *                            sending only one character per event.
    * @return                true if an event is dispatched.  Otherwise, false.
    */
   bool DispatchKeyboardEventInternal(uint32_t aMessage,
                                      const WidgetKeyboardEvent& aKeyboardEvent,
                                      nsEventStatus& aStatus,
+                                     DispatchTo aDispatchTo =
+                                       eDispatchToParentProcess,
                                      uint32_t aIndexOfKeypress = 0);
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_