Bug 1137572 part.2 Add nsIWidget::GetNativeTextEventDispatcherListener() for TextEventDispatcher::NotifyIME() r=smaug, sr=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 16 Mar 2016 13:47:47 +0900
changeset 288884 b8af9e4c043927f01a858538b0d7e46b6e5c25b0
parent 288883 a9f1b8cc65f68a638e15ab489280288bcf90b985
child 288885 a8dbb4e58e546843c0b0710f8aa2b453f5cfcadc
push id73609
push usermasayuki@d-toybox.com
push dateWed, 16 Mar 2016 04:47:58 +0000
treeherdermozilla-inbound@eb7c36e2ef5d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, smaug
bugs1137572
milestone48.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 1137572 part.2 Add nsIWidget::GetNativeTextEventDispatcherListener() for TextEventDispatcher::NotifyIME() r=smaug, sr=smaug
dom/base/TextInputProcessor.cpp
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -626,18 +626,21 @@ TextInputProcessor::CancelCompositionInt
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                               const IMENotification& aNotification)
 {
   // If This is called while this is being initialized, ignore the call.
+  // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
+  // we can say, TextInputProcessor doesn't implement any handlers of the
+  // requests and notifications.
   if (!mDispatcher) {
-    return NS_ERROR_NOT_AVAILABLE;
+    return NS_ERROR_NOT_IMPLEMENTED;
   }
   MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
              "Wrong TextEventDispatcher notifies this");
   NS_ASSERTION(mForTests || mCallback,
                "mCallback can be null only when IME is initialized for tests");
   if (mCallback) {
     RefPtr<TextInputProcessorNotification> notification;
     switch (aNotification.mMessage) {
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -50,20 +50,27 @@ TextEventDispatcher::BeginInputTransacti
 nsresult
 TextEventDispatcher::BeginTestInputTransaction(
                        TextEventDispatcherListener* aListener)
 {
   return BeginInputTransactionInternal(aListener, eTestInputTransaction);
 }
 
 nsresult
-TextEventDispatcher::BeginNativeInputTransaction(
-                       TextEventDispatcherListener* aListener)
+TextEventDispatcher::BeginNativeInputTransaction()
 {
-  return BeginInputTransactionInternal(aListener, eNativeInputTransaction);
+  if (NS_WARN_IF(!mWidget)) {
+    return NS_ERROR_FAILURE;
+  }
+  RefPtr<TextEventDispatcherListener> listener =
+    mWidget->GetNativeTextEventDispatcherListener();
+  if (NS_WARN_IF(!listener)) {
+    return NS_ERROR_FAILURE;
+  }
+  return BeginInputTransactionInternal(listener, eNativeInputTransaction);
 }
 
 nsresult
 TextEventDispatcher::BeginInputTransactionInternal(
                        TextEventDispatcherListener* aListener,
                        InputTransactionType aType)
 {
   if (NS_WARN_IF(!aListener)) {
@@ -303,28 +310,60 @@ TextEventDispatcher::CommitComposition(n
   }
 
   return NS_OK;
 }
 
 nsresult
 TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
 {
+  nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
+
+  // First, send the notification to current input transaction's listener.
   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
-  if (!listener) {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  if (listener) {
+    rv = listener->NotifyIME(this, aIMENotification);
+  }
+
+  if (mInputTransactionType == eNativeInputTransaction || !mWidget) {
+    return rv;
   }
-  nsresult rv = listener->NotifyIME(this, aIMENotification);
-  // If the listener isn't available, it means that it cannot handle the
-  // notification or request for now.  In this case, we should return
-  // NS_ERROR_NOT_IMPLEMENTED because it's not implemented at such moment.
-  if (rv == NS_ERROR_NOT_AVAILABLE) {
-    return NS_ERROR_NOT_IMPLEMENTED;
+
+  // If current input transaction isn't for native event handler, we should
+  // send the notification to the native text event dispatcher listener
+  // since native event handler may need to do something from
+  // TextEventDispatcherListener::NotifyIME() even before there is no
+  // input transaction yet.  For example, native IME handler may need to
+  // create new context at receiving NOTIFY_IME_OF_FOCUS.  In this case,
+  // mListener may not be initialized since input transaction should be
+  // initialized immediately before dispatching every WidgetKeyboardEvent
+  // and WidgetCompositionEvent (dispatching events always occurs after
+  // focus move).
+  nsCOMPtr<TextEventDispatcherListener> nativeListener =
+    mWidget->GetNativeTextEventDispatcherListener();
+  if (!nativeListener) {
+    return rv;
   }
-  return rv;
+  switch (aIMENotification.mMessage) {
+    case REQUEST_TO_COMMIT_COMPOSITION:
+    case REQUEST_TO_CANCEL_COMPOSITION:
+      // It's not necessary to notify native IME of requests.
+      return rv;
+    default: {
+      // Even if current input transaction's listener returns NS_OK or
+      // something, we need to notify native IME of notifications because
+      // when user typing after TIP does something, the changed information
+      // is necessary for them.
+      nsresult rv2 =
+        nativeListener->NotifyIME(this, aIMENotification);
+      // But return the result from current listener except when the
+      // notification isn't handled.
+      return rv == NS_ERROR_NOT_IMPLEMENTED ? rv2 : rv;
+    }
+  }
 }
 
 bool
 TextEventDispatcher::DispatchKeyboardEvent(
                        EventMessage aMessage,
                        const WidgetKeyboardEvent& aKeyboardEvent,
                        nsEventStatus& aStatus,
                        DispatchTo aDispatchTo)
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -52,31 +52,33 @@ public:
    * @param aListener       Specify the listener to listen notifications and
    *                        requests.  This must not be null.
    *                        NOTE: aListener is stored as weak reference in
    *                              TextEventDispatcher.  See mListener
    *                              definition below.
    */
   nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
   nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener);
-  nsresult BeginNativeInputTransaction(TextEventDispatcherListener* aListener);
+  nsresult BeginNativeInputTransaction();
 
   /**
    * EndInputTransaction() should be called when the listener stops using
    * the TextEventDispatcher.
    *
    * @param aListener       The listener using the TextEventDispatcher instance.
    */
   void EndInputTransaction(TextEventDispatcherListener* aListener);
 
   /**
    * OnDestroyWidget() is called when mWidget is being destroyed.
    */
   void OnDestroyWidget();
 
+  nsIWidget* GetWidget() const { return mWidget; }
+
   /**
    * GetState() returns current state of this class.
    *
    * @return        NS_OK: Fine to compose text.
    *                NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
    *                                          BeginInputTransactionForTests()
    *                                          should be called.
    *                NS_ERROR_NOT_AVAILABLE: The widget isn't available for
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "mozilla/ArrayUtils.h"
 #include "mozilla/TextEventDispatcher.h"
+#include "mozilla/TextEventDispatcherListener.h"
 
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "nsBaseWidget.h"
 #include "nsDeviceContext.h"
 #include "nsCOMPtr.h"
 #include "nsGfxCIID.h"
@@ -1745,49 +1746,61 @@ nsBaseWidget::NotifyIME(const IMENotific
       // the request may be notified to mTextEventDispatcher or native IME
       // directly.  Therefore, if mTextEventDispatcher has a composition,
       // the request should be handled by the mTextEventDispatcher.
       if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) {
         return mTextEventDispatcher->NotifyIME(aIMENotification);
       }
       // Otherwise, it should be handled by native IME.
       return NotifyIMEInternal(aIMENotification);
-    case NOTIFY_IME_OF_FOCUS:
-      mIMEHasFocus = true;
-      // We should notify TextEventDispatcher of focus notification, first.
-      // After that, notify native IME too.
-      if (mTextEventDispatcher) {
-        mTextEventDispatcher->NotifyIME(aIMENotification);
+    default: {
+      if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
+        mIMEHasFocus = true;
       }
-      return NotifyIMEInternal(aIMENotification);
-    case NOTIFY_IME_OF_BLUR: {
-      // We should notify TextEventDispatcher of blur notification, first.
-      // After that, notify native IME too.
-      if (mTextEventDispatcher) {
-        mTextEventDispatcher->NotifyIME(aIMENotification);
+      EnsureTextEventDispatcher();
+      // If the platform specific widget uses TextEventDispatcher for handling
+      // native IME and keyboard events, IME event handler should be notified
+      // of the notification via TextEventDispatcher.  Otherwise, on the other
+      // platforms which have not used TextEventDispatcher yet, IME event
+      // handler should be notified by the old path (NotifyIMEInternal).
+      nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification);
+      nsresult rv2 = NotifyIMEInternal(aIMENotification);
+      if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
+        mIMEHasFocus = false;
       }
-      nsresult rv = NotifyIMEInternal(aIMENotification);
-      mIMEHasFocus = false;
-      return rv;
+      return rv2 == NS_ERROR_NOT_IMPLEMENTED ? rv : rv2;
     }
-    default:
-      // Otherwise, notify only native IME for now.
-      return NotifyIMEInternal(aIMENotification);
   }
 }
 
+void
+nsBaseWidget::EnsureTextEventDispatcher()
+{
+  if (mTextEventDispatcher) {
+    return;
+  }
+  mTextEventDispatcher = new TextEventDispatcher(this);
+}
+
 NS_IMETHODIMP_(nsIWidget::TextEventDispatcher*)
 nsBaseWidget::GetTextEventDispatcher()
 {
-  if (!mTextEventDispatcher) {
-    mTextEventDispatcher = new TextEventDispatcher(this);
-  }
+  EnsureTextEventDispatcher();
   return mTextEventDispatcher;
 }
 
+NS_IMETHODIMP_(TextEventDispatcherListener*)
+nsBaseWidget::GetNativeTextEventDispatcherListener()
+{
+  // TODO: If all platforms supported use of TextEventDispatcher for handling
+  //       native IME and keyboard events, this method should be removed since
+  //       in such case, this is overridden by all the subclasses.
+  return nullptr;
+}
+
 void
 nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId,
                          const FrameMetrics::ViewID& aViewId,
                          const CSSRect& aRect,
                          const uint32_t& aFlags)
 {
   if (!mCompositorParent || !mAPZC) {
     return;
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -244,16 +244,18 @@ public:
               nsWidgetInitData* aInitData = nullptr,
               bool aForceUseIWidgetParent = false) override;
   NS_IMETHOD              AttachViewToTopLevel(bool aUseAttachedEvents) override;
   virtual nsIWidgetListener* GetAttachedWidgetListener() override;
   virtual void               SetAttachedWidgetListener(nsIWidgetListener* aListener) override;
   virtual nsIWidgetListener* GetPreviouslyAttachedWidgetListener() override;
   virtual void               SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener) override;
   NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() override final;
+  NS_IMETHOD_(TextEventDispatcherListener*)
+    GetNativeTextEventDispatcherListener() override;
   virtual void ZoomToRect(const uint32_t& aPresShellId,
                           const FrameMetrics::ViewID& aViewId,
                           const CSSRect& aRect,
                           const uint32_t& aFlags) override;
   // Dispatch an event that must be first be routed through APZ.
   nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) override;
 
   void SetConfirmedTargetAPZC(uint64_t aInputBlockId,
@@ -478,16 +480,18 @@ protected:
   /**
    * Notify the widget that this window is being used with OMTC.
    */
   virtual void WindowUsesOMTC() {}
   virtual void RegisterTouchWindow() {}
 
   nsIDocument* GetDocument() const;
 
+ void EnsureTextEventDispatcher();
+
   // Notify the compositor that a device reset has occurred.
   void OnRenderingDeviceReset();
 
 protected:
   /**
    * Starts the OMTC compositor destruction sequence.
    *
    * When this function returns, the compositor should not be 
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -59,16 +59,17 @@ class PLayerTransactionChild;
 struct ScrollableLayerGuid;
 } // namespace layers
 namespace gfx {
 class DrawTarget;
 class SourceSurface;
 } // namespace gfx
 namespace widget {
 class TextEventDispatcher;
+class TextEventDispatcherListener;
 } // namespace widget
 } // namespace mozilla
 
 /**
  * Callback function that processes events.
  *
  * The argument is actually a subtype (subclass) of WidgetEvent which carries
  * platform specific information about the event. Platform specific code
@@ -337,16 +338,18 @@ class nsIWidget : public nsISupports {
     typedef mozilla::widget::IMEMessage IMEMessage;
     typedef mozilla::widget::IMENotification IMENotification;
     typedef mozilla::widget::IMEState IMEState;
     typedef mozilla::widget::InputContext InputContext;
     typedef mozilla::widget::InputContextAction InputContextAction;
     typedef mozilla::widget::NativeIMEContext NativeIMEContext;
     typedef mozilla::widget::SizeConstraints SizeConstraints;
     typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
+    typedef mozilla::widget::TextEventDispatcherListener
+      TextEventDispatcherListener;
     typedef mozilla::CompositorVsyncDispatcher CompositorVsyncDispatcher;
     typedef mozilla::LayoutDeviceIntMargin LayoutDeviceIntMargin;
     typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint;
     typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
     typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion;
     typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize;
     typedef mozilla::ScreenIntPoint ScreenIntPoint;
     typedef mozilla::DesktopIntRect DesktopIntRect;
@@ -2037,16 +2040,24 @@ public:
                                        const mozilla::Maybe<ZoomConstraints>& aConstraints) {};
 
     /**
      * GetTextEventDispatcher() returns TextEventDispatcher belonging to the
      * widget.  Note that this never returns nullptr.
      */
     NS_IMETHOD_(TextEventDispatcher*) GetTextEventDispatcher() = 0;
 
+    /**
+     * GetNativeTextEventDispatcherListener() returns a
+     * TextEventDispatcherListener instance which is used when the widget
+     * instance handles native IME and/or keyboard events.
+     */
+    NS_IMETHOD_(TextEventDispatcherListener*)
+      GetNativeTextEventDispatcherListener() = 0;
+
     virtual void ZoomToRect(const uint32_t& aPresShellId,
                             const FrameMetrics::ViewID& aViewId,
                             const CSSRect& aRect,
                             const uint32_t& aFlags) = 0;
 
 protected:
     /**
      * Like GetDefaultScale, but taking into account only the system settings