Bug 1217700 part.1 nsIWidget should return reference to IMENotificationRequests r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 15 Apr 2017 01:35:58 +0900
changeset 402342 640ff6dddc6cb411d55b9c897670fa1a690865d1
parent 402341 f866afd3d2d2d5aa026bd03227b13def53c50add
child 402343 0ea1fc4888f06e0b9dde84ad60aa7632fc2ccb1a
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1217700
milestone55.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 1217700 part.1 nsIWidget should return reference to IMENotificationRequests r=m_kato IMEContentObserver may need to change notifications to send when TextInputProcessor begins input transaction. In current design, IMEContentObserver needs to retrieve IMENotificationRequests at every change. However, if nsIWidget returns a reference to its IMENotificationRequests, IMEContentObserver can call it only once. For that purpose, this patch changes nsIWidget::GetIMENotificationRequests() to nsIWidget::IMENotificationRequestsRef() and make it return |const IMENotificationRequests&|. However, if the lifetime of the instance of IMENotificationRequest is shorter than the widget instance's, it's dangerous. Therefore, it always returns TextEventDispatcher::mIMENotificationRequests. TextEventDispatcher's lifetime is longer than the widget. Therefore, this guarantees the lifetime. On the other hand, widget needs to update TextEventDispatcher::mIMENotificationRequests before calls of nsIWidget::IMENotificationRequestsRef(). Therefore, this patch makes TextEventDispatcher update proper IMENotificationRequests when it gets focus or starts new input transaction and clear mIMENotificationRequests when it loses focus. Note that TextEventDispatcher gets proper requests both from native text event dispatcher listener (typically, implemented by native IME handler class) and TextInputProcessor when TextInputProcessor has input transaction because even if TextInputProcessor overrides native IME, native IME still needs to know the content changes since they may get new input transaction after that. However, there may not be native IME handler in content process. If it runs in Android, PuppetWidget may have native IME handler because widget directly handles IME in e10s mode for Android. Otherwise, native IME handler is in its parent process. So, if TextInputHandler has input transaction in content process, PuppetWidget needs to behave as native event handler. Therefore, this patch makes PuppetWidget inherit TextEventDispatcherListener and implements PuppetWidget::IMENotificationRequestsRef(). MozReview-Commit-ID: 2SW3moONTOX
dom/events/IMEContentObserver.cpp
dom/ipc/TabParent.cpp
widget/IMEData.h
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/TextEventDispatcher.cpp
widget/TextEventDispatcher.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -428,17 +428,17 @@ IMEContentObserver::ObserveEditableNode(
     return;
   }
 
   mIsObserving = true;
   if (mEditor) {
     mEditor->AddEditorObserver(this);
   }
 
-  mIMENotificationRequests = mWidget->GetIMENotificationRequests();
+  mIMENotificationRequests = mWidget->IMENotificationRequestsRef();
   if (!WasInitializedWithPlugin()) {
     // Add selection change listener only when this starts to observe
     // non-plugin content since we cannot detect selection changes in
     // plugins.
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
     NS_ENSURE_TRUE_VOID(selPrivate);
     nsresult rv = selPrivate->AddSelectionListener(this);
     NS_ENSURE_SUCCESS_VOID(rv);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1737,32 +1737,33 @@ TabParent::RecvNotifyIMEFocus(const Cont
     *aRequests = IMENotificationRequests();
     return IPC_OK();
   }
 
   mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
   IMEStateManager::NotifyIME(aIMENotification, widget, true);
 
   if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
-    *aRequests = widget->GetIMENotificationRequests();
+    *aRequests = widget->IMENotificationRequestsRef();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvNotifyIMETextChange(const ContentCache& aContentCache,
                                    const IMENotification& aIMENotification)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return IPC_OK();
   }
 
 #ifdef DEBUG
-  IMENotificationRequests requests = widget->GetIMENotificationRequests();
+  const IMENotificationRequests& requests =
+    widget->IMENotificationRequestsRef();
   NS_ASSERTION(requests.WantTextChange(),
     "Don't call Send/RecvNotifyIMETextChange without NOTIFY_TEXT_CHANGE");
 #endif
 
   mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
   mContentCache.MaybeNotifyIME(widget, aIMENotification);
   return IPC_OK();
 }
--- a/widget/IMEData.h
+++ b/widget/IMEData.h
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_widget_IMEData_h_
 #define mozilla_widget_IMEData_h_
 
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsStringGlue.h"
+#include "nsXULAppAPI.h"
+#include "Units.h"
 
 class nsIWidget;
 
 namespace mozilla {
 
 class WritingMode;
 
 namespace widget {
@@ -61,16 +63,21 @@ struct IMENotificationRequests final
     : mWantUpdates(aWantUpdates)
   {
   }
 
   IMENotificationRequests operator|(const IMENotificationRequests& aOther) const
   {
     return IMENotificationRequests(aOther.mWantUpdates | mWantUpdates);
   }
+  IMENotificationRequests& operator|=(const IMENotificationRequests& aOther)
+  {
+    mWantUpdates |= aOther.mWantUpdates;
+    return *this;
+  }
 
   bool WantTextChange() const
   {
     return !!(mWantUpdates & NOTIFY_TEXT_CHANGE);
   }
 
   bool WantPositionChanged() const
   {
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/Hal.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
+#include "mozilla/TextEventDispatcher.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/Unused.h"
 #include "BasicLayers.h"
 #include "PuppetWidget.h"
 #include "nsContentUtils.h"
 #include "nsIWidgetListener.h"
 #include "imgIContainer.h"
 #include "nsView.h"
@@ -75,17 +76,18 @@ MightNeedIMEFocus(const nsWidgetInitData
 }
 
 // Arbitrary, fungible.
 const size_t PuppetWidget::kMaxDimension = 4000;
 
 static bool gRemoteDesktopBehaviorEnabled = false;
 static bool gRemoteDesktopBehaviorInitialized = false;
 
-NS_IMPL_ISUPPORTS_INHERITED0(PuppetWidget, nsBaseWidget)
+NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget, nsBaseWidget
+                                        , TextEventDispatcherListener)
 
 PuppetWidget::PuppetWidget(TabChild* aTabChild)
   : mTabChild(aTabChild)
   , mMemoryPressureObserver(nullptr)
   , mDPI(-1)
   , mRounding(-1)
   , mDefaultScale(-1)
   , mCursorHotspotX(0)
@@ -861,43 +863,16 @@ PuppetWidget::NotifyIMEOfCompositionUpda
   if (mInputContext.mIMEState.mEnabled != IMEState::PLUGIN &&
       NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
     return NS_ERROR_FAILURE;
   }
   mTabChild->SendNotifyIMECompositionUpdate(mContentCache, aIMENotification);
   return NS_OK;
 }
 
-IMENotificationRequests
-PuppetWidget::GetIMENotificationRequests()
-{
-  if (mNativeTextEventDispatcherListener) {
-    // Use mNativeTextEventDispatcherListener for retrieving IME notification
-    // requests because non-native IME may have transaction.
-    return mNativeTextEventDispatcherListener->GetIMENotificationRequests();
-  }
-
-  // e10s requires IME content cache in in the TabParent for handling query
-  // content event only with the parent process.  Therefore, this process
-  // needs to receive a lot of information from the focused editor to sent
-  // the latest content to the parent process.
-  if (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
-    // But if a plugin has focus, we cannot receive text nor selection change
-    // in the plugin.  Therefore, PuppetWidget needs to receive only position
-    // change event for updating the editor rect cache.
-    return IMENotificationRequests(
-             mIMENotificationRequestsOfParent.mWantUpdates |
-             IMENotificationRequests::NOTIFY_POSITION_CHANGE);
-  }
-  return IMENotificationRequests(
-           mIMENotificationRequestsOfParent.mWantUpdates |
-           IMENotificationRequests::NOTIFY_TEXT_CHANGE |
-           IMENotificationRequests::NOTIFY_POSITION_CHANGE);
-}
-
 nsresult
 PuppetWidget::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
 {
   MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
              "Passed wrong notification");
   if (!mTabChild) {
     return NS_ERROR_FAILURE;
   }
@@ -1542,10 +1517,53 @@ PuppetWidget::OnWindowedPluginKeyEvent(c
   }
   if (NS_WARN_IF(!mTabChild->SendOnWindowedPluginKeyEvent(aKeyEventData))) {
     return NS_ERROR_FAILURE;
   }
   mKeyEventInPluginCallbacks.AppendElement(aCallback);
   return NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY;
 }
 
+// TextEventDispatcherListener
+
+NS_IMETHODIMP
+PuppetWidget::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
+                        const IMENotification& aNotification)
+{
+  MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(IMENotificationRequests)
+PuppetWidget::GetIMENotificationRequests()
+{
+  if (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
+    // If a plugin has focus, we cannot receive text nor selection change
+    // in the plugin.  Therefore, PuppetWidget needs to receive only position
+    // change event for updating the editor rect cache.
+    return IMENotificationRequests(
+             mIMENotificationRequestsOfParent.mWantUpdates |
+             IMENotificationRequests::NOTIFY_POSITION_CHANGE);
+  }
+  return IMENotificationRequests(
+           mIMENotificationRequestsOfParent.mWantUpdates |
+           IMENotificationRequests::NOTIFY_TEXT_CHANGE |
+           IMENotificationRequests::NOTIFY_POSITION_CHANGE);
+}
+
+NS_IMETHODIMP_(void)
+PuppetWidget::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
+{
+  MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
+}
+
+NS_IMETHODIMP_(void)
+PuppetWidget::WillDispatchKeyboardEvent(
+                TextEventDispatcher* aTextEventDispatcher,
+                WidgetKeyboardEvent& aKeyboardEvent,
+                uint32_t aIndexOfKeypress,
+                void* aData)
+{
+  MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -34,21 +34,28 @@ namespace dom {
 class TabChild;
 } // namespace dom
 
 namespace widget {
 
 struct AutoCacheNativeKeyCommands;
 
 class PuppetWidget : public nsBaseWidget
+                   , public TextEventDispatcherListener
 {
+  typedef mozilla::CSSRect CSSRect;
   typedef mozilla::dom::TabChild TabChild;
   typedef mozilla::gfx::DrawTarget DrawTarget;
+
+  // Avoiding to make compiler confused between mozilla::widget and nsIWidget.
+  typedef mozilla::widget::TextEventDispatcher TextEventDispatcher;
+  typedef mozilla::widget::TextEventDispatcherListener
+                             TextEventDispatcherListener;
+
   typedef nsBaseWidget Base;
-  typedef mozilla::CSSRect CSSRect;
 
   // The width and height of the "widget" are clamped to this.
   static const size_t kMaxDimension;
 
 public:
   explicit PuppetWidget(TabChild* aTabChild);
 
 protected:
@@ -178,19 +185,21 @@ public:
 
   // This is used after a compositor reset.
   LayerManager* RecreateLayerManager(PLayerTransactionChild* aShadowManager);
 
   virtual void SetInputContext(const InputContext& aContext,
                                const InputContextAction& aAction) override;
   virtual InputContext GetInputContext() override;
   virtual NativeIMEContext GetNativeIMEContext() override;
-  virtual IMENotificationRequests GetIMENotificationRequests() override;
   TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override
-  { return mNativeTextEventDispatcherListener; }
+  {
+    return mNativeTextEventDispatcherListener ?
+             mNativeTextEventDispatcherListener.get() : this;
+  }
   void SetNativeTextEventDispatcherListener(TextEventDispatcherListener* aListener)
   { mNativeTextEventDispatcherListener = aListener; }
 
   virtual void SetCursor(nsCursor aCursor) override;
   virtual nsresult SetCursor(imgIContainer* aCursor,
                              uint32_t aHotspotX, uint32_t aHotspotY) override;
 
   virtual void ClearCachedCursor() override;
@@ -286,16 +295,29 @@ public:
                      nsIKeyEventInPluginCallback* aCallback) override;
 
   virtual void LookUpDictionary(
                  const nsAString& aText,
                  const nsTArray<mozilla::FontRange>& aFontRangeArray,
                  const bool aIsVertical,
                  const LayoutDeviceIntPoint& aPoint) override;
 
+  // TextEventDispatcherListener
+  using nsBaseWidget::NotifyIME;
+  NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
+                       const IMENotification& aNotification) override;
+  NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;
+  NS_IMETHOD_(void) OnRemovedFrom(
+                      TextEventDispatcher* aTextEventDispatcher) override;
+  NS_IMETHOD_(void) WillDispatchKeyboardEvent(
+                      TextEventDispatcher* aTextEventDispatcher,
+                      WidgetKeyboardEvent& aKeyboardEvent,
+                      uint32_t aIndexOfKeypress,
+                      void* aData) override;
+
 protected:
   virtual nsresult NotifyIMEInternal(
                      const IMENotification& aIMENotification) override;
 
 private:
   nsresult Paint();
 
   void SetChild(PuppetWidget* aChild);
--- a/widget/TextEventDispatcher.cpp
+++ b/widget/TextEventDispatcher.cpp
@@ -22,27 +22,30 @@ namespace widget {
 
 bool TextEventDispatcher::sDispatchKeyEventsDuringComposition = false;
 
 TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
   : mWidget(aWidget)
   , mDispatchingEvent(0)
   , mInputTransactionType(eNoInputTransaction)
   , mIsComposing(false)
+  , mHasFocus(false)
 {
   MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
 
   static bool sInitialized = false;
   if (!sInitialized) {
     Preferences::AddBoolVarCache(
       &sDispatchKeyEventsDuringComposition,
       "dom.keyboardevent.dispatch_during_composition",
       false);
     sInitialized = true;
   }
+
+  ClearNotificationRequests();
 }
 
 nsresult
 TextEventDispatcher::BeginInputTransaction(
                        TextEventDispatcherListener* aListener)
 {
   return BeginInputTransactionInternal(aListener,
                                        eSameProcessSyncInputTransaction);
@@ -78,31 +81,33 @@ TextEventDispatcher::BeginInputTransacti
                        InputTransactionType aType)
 {
   if (NS_WARN_IF(!aListener)) {
     return NS_ERROR_INVALID_ARG;
   }
   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
   if (listener) {
     if (listener == aListener && mInputTransactionType == aType) {
+      UpdateNotificationRequests();
       return NS_OK;
     }
     // If this has composition or is dispatching an event, any other listener
     // can steal ownership.  Especially, if the latter case is allowed,
     // nobody cannot begin input transaction with this if a modal dialog is
     // opened during dispatching an event.
     if (IsComposing() || IsDispatchingEvent()) {
       return NS_ERROR_ALREADY_INITIALIZED;
     }
   }
   mListener = do_GetWeakReference(aListener);
   mInputTransactionType = aType;
   if (listener && listener != aListener) {
     listener->OnRemovedFrom(this);
   }
+  UpdateNotificationRequests();
   return NS_OK;
 }
 
 void
 TextEventDispatcher::EndInputTransaction(TextEventDispatcherListener* aListener)
 {
   if (NS_WARN_IF(IsComposing()) || NS_WARN_IF(IsDispatchingEvent())) {
     return;
@@ -116,22 +121,25 @@ TextEventDispatcher::EndInputTransaction
   }
 
   if (NS_WARN_IF(listener != aListener)) {
     return;
   }
 
   mListener = nullptr;
   listener->OnRemovedFrom(this);
+  UpdateNotificationRequests();
 }
 
 void
 TextEventDispatcher::OnDestroyWidget()
 {
   mWidget = nullptr;
+  mHasFocus = false;
+  ClearNotificationRequests();
   mPendingComposition.Clear();
   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
   mListener = nullptr;
   mInputTransactionType = eNoInputTransaction;
   if (listener) {
     listener->OnRemovedFrom(this);
   }
 }
@@ -333,56 +341,104 @@ TextEventDispatcher::CommitComposition(n
   return NS_OK;
 }
 
 nsresult
 TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
 {
   nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
 
+  if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
+    mHasFocus = false;
+    ClearNotificationRequests();
+  }
+
+
   // First, send the notification to current input transaction's listener.
   nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
   if (listener) {
     rv = listener->NotifyIME(this, aIMENotification);
   }
 
-  if (mInputTransactionType == eNativeInputTransaction || !mWidget) {
+  if (!mWidget) {
     return rv;
   }
 
   // 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;
+  if (listener != nativeListener && nativeListener) {
+    switch (aIMENotification.mMessage) {
+      case REQUEST_TO_COMMIT_COMPOSITION:
+      case REQUEST_TO_CANCEL_COMPOSITION:
+        // It's not necessary to notify native IME of requests.
+        break;
+      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.
+        if (rv == NS_ERROR_NOT_IMPLEMENTED) {
+          rv = rv2;
+        }
+        break;
+      }
+    }
+  }
+
+  if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
+    mHasFocus = true;
+    UpdateNotificationRequests();
   }
-  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;
+
+  return rv;
+}
+
+void
+TextEventDispatcher::ClearNotificationRequests()
+{
+  mIMENotificationRequests = IMENotificationRequests();
+}
+
+void
+TextEventDispatcher::UpdateNotificationRequests()
+{
+  ClearNotificationRequests();
+
+  // If it doesn't has focus, no notifications are available.
+  if (!mHasFocus || !mWidget) {
+    return;
+  }
+
+  // If there is a listener, its requests are necessary.
+  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
+  if (listener) {
+    mIMENotificationRequests = listener->GetIMENotificationRequests();
+  }
+
+  // Even if this is in non-native input transaction, native IME needs
+  // requests.  So, add native IME requests too.
+  if (!IsInNativeInputTransaction()) {
+    nsCOMPtr<TextEventDispatcherListener> nativeListener =
+      mWidget->GetNativeTextEventDispatcherListener();
+    if (nativeListener) {
+      mIMENotificationRequests |= nativeListener->GetIMENotificationRequests();
     }
   }
 }
 
 bool
 TextEventDispatcher::DispatchKeyboardEvent(
                        EventMessage aMessage,
                        const WidgetKeyboardEvent& aKeyboardEvent,
--- a/widget/TextEventDispatcher.h
+++ b/widget/TextEventDispatcher.h
@@ -7,24 +7,23 @@
 #define mozilla_textcompositionsynthesizer_h_
 
 #include "mozilla/RefPtr.h"
 #include "nsString.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/TextEventDispatcherListener.h"
 #include "mozilla/TextRange.h"
+#include "mozilla/widget/IMEData.h"
 
 class nsIWidget;
 
 namespace mozilla {
 namespace widget {
 
-struct IMENotification;
-
 /**
  * TextEventDispatcher is a helper class for dispatching widget events defined
  * in TextEvents.h.  Currently, this is a helper for dispatching
  * WidgetCompositionEvent and WidgetKeyboardEvent.  This manages the behavior
  * of them for conforming to DOM Level 3 Events.
  * An instance of this class is created by nsIWidget instance and owned by it.
  * This is typically created only by the top level widgets because only they
  * handle IME.
@@ -70,16 +69,21 @@ public:
 
   /**
    * OnDestroyWidget() is called when mWidget is being destroyed.
    */
   void OnDestroyWidget();
 
   nsIWidget* GetWidget() const { return mWidget; }
 
+  const IMENotificationRequests& IMENotificationRequestsRef() const
+  {
+    return mIMENotificationRequests;
+  }
+
   /**
    * 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
@@ -302,16 +306,19 @@ private:
   // return true).
   nsIWidget* mWidget;
   // mListener is a weak reference to TextEventDispatcherListener.  That might
   // be referred by JS.  Therefore, the listener might be difficult to release
   // itself if this is a strong reference.  Additionally, it's difficult to
   // check if a method to uninstall the listener is called by valid instance.
   // So, using weak reference is the best way in this case.
   nsWeakPtr mListener;
+  // mIMENotificationRequests should store current IME's notification requests.
+  // So, this may be invalid when IME doesn't have focus.
+  IMENotificationRequests mIMENotificationRequests;
 
   // mPendingComposition stores new composition string temporarily.
   // These values will be used for dispatching eCompositionChange event
   // in Flush().  When Flush() is called, the members will be cleared
   // automatically.
   class PendingComposition
   {
   public:
@@ -407,16 +414,20 @@ private:
       default:
         MOZ_CRASH("Define the behavior of new InputTransactionType");
     }
   }
 
   // See IsComposing().
   bool mIsComposing;
 
+  // true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not
+  // received yet.  Otherwise, false.
+  bool mHasFocus;
+
   // If this is true, keydown and keyup events are dispatched even when there
   // is a composition.
   static bool sDispatchKeyEventsDuringComposition;
 
   nsresult BeginInputTransactionInternal(
              TextEventDispatcherListener* aListener,
              InputTransactionType aType);
 
@@ -490,14 +501,27 @@ private:
    * @return                true if an event is dispatched.  Otherwise, false.
    */
   bool DispatchKeyboardEventInternal(EventMessage aMessage,
                                      const WidgetKeyboardEvent& aKeyboardEvent,
                                      nsEventStatus& aStatus,
                                      void* aData,
                                      uint32_t aIndexOfKeypress = 0,
                                      bool aNeedsCallback = false);
+
+  /**
+   * ClearNotificationRequests() clears mIMENotificationRequests.
+   */
+  void ClearNotificationRequests();
+
+  /**
+   * UpdateNotificationRequests() updates mIMENotificationRequests with
+   * current state.  If the instance doesn't have focus, this clears
+   * mIMENotificationRequests.  Otherwise, updates it with both requests of
+   * current listener and native listener.
+   */
+  void UpdateNotificationRequests();
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1718,17 +1718,17 @@ nsBaseWidget::NotifySizeMoveDone()
 
 void
 nsBaseWidget::NotifyWindowMoved(int32_t aX, int32_t aY)
 {
   if (mWidgetListener) {
     mWidgetListener->WindowMoved(this, aX, aY);
   }
 
-  if (mIMEHasFocus && GetIMENotificationRequests().WantPositionChanged()) {
+  if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) {
     NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE));
   }
 }
 
 void
 nsBaseWidget::NotifySysColorChanged()
 {
   if (!mWidgetListener || mWidgetListener->GetXULWindow())
@@ -1794,28 +1794,16 @@ nsBaseWidget::NotifyIME(const IMENotific
       if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
         mIMEHasFocus = false;
       }
       return rv2 == NS_ERROR_NOT_IMPLEMENTED ? rv : rv2;
     }
   }
 }
 
-IMENotificationRequests
-nsBaseWidget::GetIMENotificationRequests()
-{
-  RefPtr<TextEventDispatcherListener> listener =
-    GetNativeTextEventDispatcherListener();
-  if (!listener) {
-    // Default is to not send additional change notifications to NotifyIME.
-    return IMENotificationRequests();
-  }
-  return listener->GetIMENotificationRequests();
-}
-
 void
 nsBaseWidget::EnsureTextEventDispatcher()
 {
   if (mTextEventDispatcher) {
     return;
   }
   mTextEventDispatcher = new TextEventDispatcher(this);
 }
@@ -2301,16 +2289,23 @@ nsBaseWidget::DefaultFillScrollCapture(D
 #endif
 
 nsIWidget::NativeIMEContext
 nsIWidget::GetNativeIMEContext()
 {
   return NativeIMEContext(this);
 }
 
+const IMENotificationRequests&
+nsIWidget::IMENotificationRequestsRef()
+{
+  TextEventDispatcher* dispatcher = GetTextEventDispatcher();
+  return dispatcher->IMENotificationRequestsRef();
+}
+
 nsresult
 nsIWidget::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
                                     nsIKeyEventInPluginCallback* aCallback)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 namespace mozilla {
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -281,17 +281,16 @@ public:
   virtual MOZ_MUST_USE nsresult AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) override { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual bool            ExecuteNativeKeyBinding(
                             NativeKeyBindingsType aType,
                             const mozilla::WidgetKeyboardEvent& aEvent,
                             DoCommandCallback aCallback,
                             void* aCallbackData) override { return false; }
   bool                    ComputeShouldAccelerate();
   virtual bool            WidgetTypeSupportsAcceleration() { return true; }
-  virtual IMENotificationRequests GetIMENotificationRequests() override;
   virtual MOZ_MUST_USE nsresult OnDefaultButtonLoaded(const LayoutDeviceIntRect& aButtonRect) override { return NS_ERROR_NOT_IMPLEMENTED; }
   virtual already_AddRefed<nsIWidget>
   CreateChild(const LayoutDeviceIntRect& aRect,
               nsWidgetInitData* aInitData = nullptr,
               bool aForceUseIWidgetParent = false) override;
   virtual void            AttachViewToTopLevel(bool aUseAttachedEvents) override;
   virtual nsIWidgetListener* GetAttachedWidgetListener() override;
   virtual void               SetAttachedWidgetListener(nsIWidgetListener* aListener) override;
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1813,19 +1813,22 @@ public:
     };
     virtual bool ExecuteNativeKeyBinding(
                         NativeKeyBindingsType aType,
                         const mozilla::WidgetKeyboardEvent& aEvent,
                         DoCommandCallback aCallback,
                         void* aCallbackData) = 0;
 
     /*
-     * Retrieves preference for IME updates
+     * Retrieves a reference to notification requests of IME.  Note that the
+     * reference is valid while the nsIWidget instance is alive.  So, if you
+     * need to store the reference for a long time, you need to grab the widget
+     * instance too.
      */
-    virtual IMENotificationRequests GetIMENotificationRequests() = 0;
+    const IMENotificationRequests& IMENotificationRequestsRef();
 
     /*
      * Call this method when a dialog is opened which has a default button.
      * The button's rectangle should be supplied in aButtonRect.
      */
     virtual MOZ_MUST_USE nsresult
     OnDefaultButtonLoaded(const LayoutDeviceIntRect& aButtonRect) = 0;