Bug 1377672 - part3: IMEStateManager::NotifyIME() should ignore notifications and requests which comes from unexpected process r=m_kato,smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 06 Jul 2017 00:47:40 +0900
changeset 418919 14a41922c96a6f28192698ad0b3a5b1b9220d8fe
parent 418918 cd648a42778f4910d8588f57ad5954162def726d
child 418920 feb3d54b7cca74e14e5a606fb44c867995cd096f
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato, smaug
bugs1377672
milestone56.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 1377672 - part3: IMEStateManager::NotifyIME() should ignore notifications and requests which comes from unexpected process r=m_kato,smaug IME should receive notifications and requests only from proper process. E.g., IME shouldn't commit composition by a request which came from previous focused process. This patch makes that IMEStateManager::NotifyIME() takes pointer to TabParent optionally. If the request or notification came from remote process, it should be non-nullptr. Then, this makes it ignore notifications and requests from unexpected process. Note that this patch also touches some gfx headers because they use |ipc::| but compiler is confused at the ambiguousness between |mozilla::ipc::| and |mozilla::dom::ipc::|. Finally, this patch changes the NS_ASSERTION in IMEHandler::OnDestroyWindow() to MOZ_ASSERT because the orange caused by the NS_ASSERTION was not realized since there was already an intermittent orange bug caused by different NS_ASSERTION. MozReview-Commit-ID: 9CgKXQRJWmN
dom/events/IMEStateManager.cpp
dom/events/IMEStateManager.h
dom/events/TextComposition.cpp
dom/events/TextComposition.h
dom/ipc/TabParent.cpp
gfx/ipc/GPUProcessHost.h
gfx/ipc/GPUProcessManager.h
widget/ContentCache.cpp
widget/ContentCache.h
widget/windows/WinIMEHandler.cpp
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -131,30 +131,42 @@ GetIMEStateSetOpenName(IMEState::Open aO
       return "OPEN";
     case IMEState::CLOSED:
       return "CLOSED";
     default:
       return "illegal value";
   }
 }
 
+static bool
+IsSameProcess(const TabParent* aTabParent1, const TabParent* aTabParent2)
+{
+  if (aTabParent1 == aTabParent2) {
+    return true;
+  }
+  if (!aTabParent1 != !aTabParent2) {
+    return false;
+  }
+  return aTabParent1->Manager() == aTabParent2->Manager();
+}
+
 StaticRefPtr<nsIContent> IMEStateManager::sContent;
 StaticRefPtr<nsPresContext> IMEStateManager::sPresContext;
 nsIWidget* IMEStateManager::sWidget = nullptr;
 nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
+StaticRefPtr<TabParent> IMEStateManager::sFocusedIMETabParent;
 nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
 StaticRefPtr<TabParent> IMEStateManager::sActiveTabParent;
 StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
 InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN;
 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
 bool IMEStateManager::sIsGettingNewIMEState = false;
 bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
 bool IMEStateManager::sInputModeSupported = false;
-bool IMEStateManager::sRemoteHasFocus = false;
 
 // static
 void
 IMEStateManager::Init()
 {
   Preferences::AddBoolVarCache(
     &sCheckForIMEUnawareWebApps,
     "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
@@ -221,17 +233,17 @@ IMEStateManager::StopIMEStateManagement(
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("StopIMEStateManagement()"));
 
   // NOTE: Don't set input context from here since this has already lost
   //       the rights to change input context.
 
   if (sTextCompositions && sPresContext) {
-    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext);
+    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext, sActiveTabParent);
   }
   sActiveInputContextWidget = nullptr;
   sPresContext = nullptr;
   sContent = nullptr;
   sActiveTabParent = nullptr;
   DestroyIMEContentObserver();
 }
 
@@ -439,17 +451,17 @@ IMEStateManager::OnChangeFocusInternal(n
   bool focusActuallyChanging =
     (sContent != aContent || sPresContext != aPresContext ||
      oldWidget != newWidget || sActiveTabParent != newTabParent);
 
   if (oldWidget && focusActuallyChanging) {
     // If we're deactivating, we shouldn't commit composition forcibly because
     // the user may want to continue the composition.
     if (aPresContext) {
-      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+      NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget, sFocusedIMETabParent);
     }
   }
 
   if (sActiveIMEContentObserver) {
     // If there is active IMEContentObserver, it means that focused content was
     // in this process.  So, if a tab parent gets focus, it means that the
     // focused editor in this process is being blurred.
     if (newTabParent) {
@@ -473,21 +485,17 @@ IMEStateManager::OnChangeFocusInternal(n
 
   if (!aPresContext) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnChangeFocusInternal(), "
        "no nsPresContext is being activated"));
     return NS_OK;
   }
 
-  nsIContentParent* currentContentParent =
-    sActiveTabParent ? sActiveTabParent->Manager() : nullptr;
-  nsIContentParent* newContentParent =
-    newTabParent ? newTabParent->Manager() : nullptr;
-  if (sActiveTabParent && currentContentParent != newContentParent) {
+  if (sActiveTabParent && !IsSameProcess(sActiveTabParent, newTabParent)) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnChangeFocusInternal(), notifying previous "
        "focused child process of parent process or another child process "
        "getting focus"));
     Unused << sActiveTabParent->SendStopIMEStateManagement();
   }
 
   if (NS_WARN_IF(!newWidget)) {
@@ -560,17 +568,18 @@ IMEStateManager::OnChangeFocusInternal(n
            "neither focus nor IME state is changing"));
         return NS_OK;
       }
       aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
 
       // Even if focus isn't changing actually, we should commit current
       // composition here since the IME state is changing.
       if (sPresContext && oldWidget && !focusActuallyChanging) {
-        NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
+        NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
+                  sFocusedIMETabParent);
       }
     } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
       // If aContent isn't null or aContent is null but editable, somebody gets
       // focus.
       bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
       aAction.mFocusChange =
         gotFocus ? InputContextAction::GOT_FOCUS :
                    InputContextAction::LOST_FOCUS;
@@ -918,17 +927,17 @@ IMEStateManager::UpdateIMEState(const IM
   if (NS_WARN_IF(widget->Destroyed())) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  UpdateIMEState(), widget has gone during getting input context"));
     return;
   }
 
   if (updateIMEState) {
     // commit current composition before modifying IME state.
-    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
+    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget, sFocusedIMETabParent);
     if (NS_WARN_IF(widget->Destroyed())) {
       MOZ_LOG(sISMLog, LogLevel::Error,
         ("  UpdateIMEState(), widget has gone during committing composition"));
       return;
     }
   }
 
   if (createTextStateManager) {
@@ -1438,191 +1447,193 @@ IMEStateManager::OnCompositionEventDisca
   }
   composition->OnCompositionEventDiscarded(aCompositionEvent);
 }
 
 // static
 nsresult
 IMEStateManager::NotifyIME(IMEMessage aMessage,
                            nsIWidget* aWidget,
-                           bool aOriginIsRemote)
+                           TabParent* aTabParent)
 {
   return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget,
-                                    aOriginIsRemote);
+                                    aTabParent);
 }
 
 // static
 nsresult
 IMEStateManager::NotifyIME(const IMENotification& aNotification,
                            nsIWidget* aWidget,
-                           bool aOriginIsRemote)
+                           TabParent* aTabParent)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("NotifyIME(aNotification={ mMessage=%s }, "
-     "aWidget=0x%p, aOriginIsRemote=%s), sFocusedIMEWidget=0x%p, "
-     "sRemoteHasFocus=%s",
+     "aWidget=0x%p, aTabParent=0x%p), sFocusedIMEWidget=0x%p, "
+     "sActiveTabParent=0x%p, sFocusedIMETabParent=0x%p, "
+     "IsSameProcess(aTabParent, sActiveTabParent)=%s, "
+     "IsSameProcess(aTabParent, sFocusedIMETabParent)=%s",
      ToChar(aNotification.mMessage), aWidget,
-     GetBoolName(aOriginIsRemote), sFocusedIMEWidget,
-     GetBoolName(sRemoteHasFocus)));
+     aTabParent, sFocusedIMEWidget, sActiveTabParent.get(),
+     sFocusedIMETabParent.get(),
+     GetBoolName(IsSameProcess(aTabParent, sActiveTabParent)),
+     GetBoolName(IsSameProcess(aTabParent, sFocusedIMETabParent))));
 
   if (NS_WARN_IF(!aWidget)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  NotifyIME(), FAILED due to no widget"));
     return NS_ERROR_INVALID_ARG;
   }
 
   switch (aNotification.mMessage) {
     case NOTIFY_IME_OF_FOCUS: {
+      // If focus notification comes from a remote process which already lost
+      // focus, we shouldn't accept the focus notification.  Then, following
+      // notifications from the process will be ignored.
+      if (NS_WARN_IF(!IsSameProcess(aTabParent, sActiveTabParent))) {
+        MOZ_ASSERT(aTabParent,
+          "Why was the input context initialized for a remote process but "
+          "does this process get IME focus?");
+        MOZ_LOG(sISMLog, LogLevel::Warning,
+          ("  NotifyIME(), WARNING, the received focus notification is ignored "
+           "because input context was initialized for %s, perhaps, it came "
+           "from a busy remote process",
+           sActiveTabParent ? "another remote process" : "current process"));
+        return NS_OK;
+      }
+      // If there is pending blur notification for current focused IME,
+      // we should notify IME of blur by ourselves.  Then, we should ignore
+      // following notifications coming from the process.
       if (sFocusedIMEWidget) {
-        if (NS_WARN_IF(!sRemoteHasFocus && !aOriginIsRemote)) {
-          MOZ_LOG(sISMLog, LogLevel::Error,
-            ("  NotifyIME(), although, this process is "
-             "getting IME focus but there was focused IME widget"));
-        } else {
-          MOZ_LOG(sISMLog, LogLevel::Info,
-            ("  NotifyIME(), tries to notify IME of "
-             "blur first because remote process's blur notification hasn't "
-             "been received yet..."));
-        }
+        MOZ_ASSERT(sFocusedIMETabParent || aTabParent,
+          "This case shouldn't be caused by focus move in this process");
         nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
         sFocusedIMEWidget = nullptr;
-        sRemoteHasFocus = false;
+        sFocusedIMETabParent = nullptr;
         focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
       }
-      sRemoteHasFocus = aOriginIsRemote;
+      sFocusedIMETabParent = aTabParent;
       sFocusedIMEWidget = aWidget;
       nsCOMPtr<nsIWidget> widget(aWidget);
       return widget->NotifyIME(aNotification);
     }
     case NOTIFY_IME_OF_BLUR: {
-      if (!sRemoteHasFocus && aOriginIsRemote) {
-        MOZ_LOG(sISMLog, LogLevel::Info,
-          ("  NotifyIME(), received blur notification "
-           "after another one has focus, nothing to do..."));
+      if (!IsSameProcess(aTabParent, sFocusedIMETabParent)) {
+        MOZ_LOG(sISMLog, LogLevel::Warning,
+          ("  NotifyIME(), WARNING, the received blur notification is ignored "
+           "because it's not from current focused IME process"));
         return NS_OK;
       }
-      if (NS_WARN_IF(sRemoteHasFocus && !aOriginIsRemote)) {
+      if (!sFocusedIMEWidget) {
         MOZ_LOG(sISMLog, LogLevel::Error,
-          ("  NotifyIME(), FAILED, received blur "
-           "notification from this process but the remote has focus"));
-        return NS_OK;
-      }
-      if (!sFocusedIMEWidget && aOriginIsRemote) {
-        MOZ_LOG(sISMLog, LogLevel::Info,
-          ("  NotifyIME(), received blur notification "
-           "but the remote has already lost focus"));
-        return NS_OK;
-      }
-      if (NS_WARN_IF(!sFocusedIMEWidget)) {
-        MOZ_LOG(sISMLog, LogLevel::Error,
-          ("  NotifyIME(), FAILED, received blur "
-           "notification but there is no focused IME widget"));
+          ("  NotifyIME(), WARNING, received blur notification but there is "
+           "no focused IME widget"));
         return NS_OK;
       }
       if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
-        MOZ_LOG(sISMLog, LogLevel::Error,
-          ("  NotifyIME(), FAILED, received blur "
-           "notification but there is no focused IME widget"));
+        MOZ_LOG(sISMLog, LogLevel::Warning,
+          ("  NotifyIME(), WARNING, the received blur notification is ignored "
+           "because it's not for current focused IME widget"));
         return NS_OK;
       }
       nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
       sFocusedIMEWidget = nullptr;
-      sRemoteHasFocus = false;
+      sFocusedIMETabParent = nullptr;
       return focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
     }
     case NOTIFY_IME_OF_SELECTION_CHANGE:
     case NOTIFY_IME_OF_TEXT_CHANGE:
     case NOTIFY_IME_OF_POSITION_CHANGE:
     case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
     case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
-      if (!sRemoteHasFocus && aOriginIsRemote) {
-        MOZ_LOG(sISMLog, LogLevel::Info,
-          ("  NotifyIME(), received content change "
-           "notification from the remote but it's already lost focus"));
-        return NS_OK;
-      }
-      if (NS_WARN_IF(sRemoteHasFocus && !aOriginIsRemote)) {
-        MOZ_LOG(sISMLog, LogLevel::Error,
-          ("  NotifyIME(), FAILED, received content "
-           "change notification from this process but the remote has already "
-           "gotten focus"));
+      if (!IsSameProcess(aTabParent, sFocusedIMETabParent)) {
+        MOZ_LOG(sISMLog, LogLevel::Warning,
+          ("  NotifyIME(), WARNING, the received content change notification "
+           "is ignored because it's not from current focused IME process"));
         return NS_OK;
       }
       if (!sFocusedIMEWidget) {
-        MOZ_LOG(sISMLog, LogLevel::Info,
-          ("  NotifyIME(), received content change "
-           "notification but there is no focused IME widget"));
+        MOZ_LOG(sISMLog, LogLevel::Warning,
+          ("  NotifyIME(), WARNING, the received content change notification "
+           "is ignored because there is no focused IME widget"));
         return NS_OK;
       }
       if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
-        MOZ_LOG(sISMLog, LogLevel::Error,
-          ("  NotifyIME(), FAILED, received content "
-           "change notification for IME which has already lost focus, so, "
-           "nothing to do..."));
+        MOZ_LOG(sISMLog, LogLevel::Warning,
+          ("  NotifyIME(), WARNING, the received content change notification "
+           "is ignored because it's not for current focused IME widget"));
         return NS_OK;
       }
       nsCOMPtr<nsIWidget> widget(aWidget);
       return widget->NotifyIME(aNotification);
     }
     default:
       // Other notifications should be sent only when there is composition.
       // So, we need to handle the others below.
       break;
   }
 
-  RefPtr<TextComposition> composition;
-  if (sTextCompositions) {
-    composition = sTextCompositions->GetCompositionFor(aWidget);
+  if (!sTextCompositions) {
+    MOZ_LOG(sISMLog, LogLevel::Info,
+      ("  NotifyIME(), the request to IME is ignored because "
+       "there have been no compositions yet"));
+    return NS_OK;
   }
 
-  bool isSynthesizedForTests =
-    composition && composition->IsSynthesizedForTests();
+  RefPtr<TextComposition> composition =
+    sTextCompositions->GetCompositionFor(aWidget);
+  if (!composition) {
+    MOZ_LOG(sISMLog, LogLevel::Info,
+      ("  NotifyIME(), the request to IME is ignored because "
+       "there is no active composition"));
+    return NS_OK;
+  }
 
-  MOZ_LOG(sISMLog, LogLevel::Info,
-    ("  NotifyIME(), composition=0x%p, "
-     "composition->IsSynthesizedForTests()=%s",
-     composition.get(), GetBoolName(isSynthesizedForTests)));
+  if (!IsSameProcess(aTabParent, composition->GetTabParent())) {
+    MOZ_LOG(sISMLog, LogLevel::Warning,
+      ("  NotifyIME(), WARNING, the request to IME is ignored because "
+       "it does not come from the remote process which has the composition "
+       "on aWidget"));
+    return NS_OK;
+  }
 
   switch (aNotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
-      return composition ?
-        composition->RequestToCommit(aWidget, false) : NS_OK;
+      return composition->RequestToCommit(aWidget, false);
     case REQUEST_TO_CANCEL_COMPOSITION:
-      return composition ?
-        composition->RequestToCommit(aWidget, true) : NS_OK;
+      return composition->RequestToCommit(aWidget, true);
     default:
       MOZ_CRASH("Unsupported notification");
   }
   MOZ_CRASH(
     "Failed to handle the notification for non-synthesized composition");
   return NS_ERROR_FAILURE;
 }
 
 // static
 nsresult
 IMEStateManager::NotifyIME(IMEMessage aMessage,
                            nsPresContext* aPresContext,
-                           bool aOriginIsRemote)
+                           TabParent* aTabParent)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
-    ("NotifyIME(aMessage=%s, aPresContext=0x%p, aOriginIsRemote=%s)",
-     ToChar(aMessage), aPresContext, GetBoolName(aOriginIsRemote)));
+    ("NotifyIME(aMessage=%s, aPresContext=0x%p, aTabParent=0x%p)",
+     ToChar(aMessage), aPresContext, aTabParent));
 
   if (NS_WARN_IF(!CanHandleWith(aPresContext))) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsIWidget* widget = aPresContext->GetRootWidget();
   if (NS_WARN_IF(!widget)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  NotifyIME(), FAILED due to no widget for the "
        "nsPresContext"));
     return NS_ERROR_NOT_AVAILABLE;
   }
-  return NotifyIME(aMessage, widget, aOriginIsRemote);
+  return NotifyIME(aMessage, widget, aTabParent);
 }
 
 // static
 bool
 IMEStateManager::IsEditable(nsINode* node)
 {
   if (node->IsEditable()) {
     return true;
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -219,23 +219,23 @@ public:
     GetTextCompositionFor(nsPresContext* aPresContext);
 
   /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
   static nsresult NotifyIME(const IMENotification& aNotification,
                             nsIWidget* aWidget,
-                            bool aOriginIsRemote = false);
+                            TabParent* aTabParent = nullptr);
   static nsresult NotifyIME(IMEMessage aMessage,
                             nsIWidget* aWidget,
-                            bool aOriginIsRemote = false);
+                            TabParent* aTabParent = nullptr);
   static nsresult NotifyIME(IMEMessage aMessage,
                             nsPresContext* aPresContext,
-                            bool aOriginIsRemote = false);
+                            TabParent* aTabParent = nullptr);
 
   static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
                                       nsIContent* aContent);
 
   /**
    * Returns active IMEContentObserver but may be nullptr if focused content
    * isn't editable or focus in a remote process.
    */
@@ -275,21 +275,20 @@ protected:
   // document has focus but there is no focused element, sContent may be
   // nullptr.
   static StaticRefPtr<nsIContent> sContent;
   static StaticRefPtr<nsPresContext> sPresContext;
   // sWidget is cache for the root widget of sPresContext.  Even afer
   // sPresContext has gone, we need to clean up some IME state on the widget
   // if the widget is available.
   static nsIWidget* sWidget;
-  // sFocusedIMEWidget is, the widget which was sent to "focus" notification
-  // from IMEContentObserver and not yet sent "blur" notification.
-  // So, if this is not nullptr, the widget needs to receive "blur"
-  // notification.
+  // sFocusedIMETabParent is the tab parent, which send "focus" notification to
+  // sFocusedIMEWidget (and didn't yet sent "blur" notification).
   static nsIWidget* sFocusedIMEWidget;
+  static StaticRefPtr<TabParent> sFocusedIMETabParent;
   // sActiveInputContextWidget is the last widget whose SetInputContext() is
   // called.  This is important to reduce sync IPC cost with parent process.
   // If IMEStateManager set input context to different widget, PuppetWidget can
   // return cached input context safely.
   static nsIWidget* sActiveInputContextWidget;
   static StaticRefPtr<TabParent> sActiveTabParent;
   // sActiveIMEContentObserver points to the currently active
   // IMEContentObserver.  This is null if there is no focused editor.
@@ -303,17 +302,16 @@ protected:
 
   // Origin type of current process.
   static InputContext::Origin sOrigin;
 
   static bool           sInstalledMenuKeyboardListener;
   static bool           sIsGettingNewIMEState;
   static bool           sCheckForIMEUnawareWebApps;
   static bool           sInputModeSupported;
-  static bool           sRemoteHasFocus;
 
   class MOZ_STACK_CLASS GettingNewIMEStateBlocker final
   {
   public:
     GettingNewIMEStateBlocker()
       : mOldValue(IMEStateManager::sIsGettingNewIMEState)
     {
       IMEStateManager::sIsGettingNewIMEState = true;
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -611,17 +611,17 @@ TextComposition::RequestToCommit(nsIWidg
   }
   return NS_OK;
 }
 
 nsresult
 TextComposition::NotifyIME(IMEMessage aMessage)
 {
   NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
-  return IMEStateManager::NotifyIME(aMessage, mPresContext);
+  return IMEStateManager::NotifyIME(aMessage, mPresContext, mTabParent);
 }
 
 void
 TextComposition::EditorWillHandleCompositionChangeEvent(
                    const WidgetCompositionEvent* aCompositionChangeEvent)
 {
   mIsComposing = aCompositionChangeEvent->IsComposing();
   mRanges = aCompositionChangeEvent->mRanges;
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -70,16 +70,21 @@ public:
   // XXX We should return |const TextRangeArray*| here, but it causes compile
   //     error due to inaccessible Release() method.
   TextRangeArray* GetRanges() const { return mRanges; }
   // Returns the widget which is proper to call NotifyIME().
   nsIWidget* GetWidget() const
   {
     return mPresContext ? mPresContext->GetRootWidget() : nullptr;
   }
+  // Returns the tab parent which has this composition in its remote process.
+  TabParent* GetTabParent() const
+  {
+    return mTabParent;
+  }
   // Returns true if the composition is started with synthesized event which
   // came from nsDOMWindowUtils.
   bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
 
   const widget::NativeIMEContext& GetNativeIMEContext() const
   {
     return mNativeContext;
   }
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -138,16 +138,17 @@ NS_IMPL_ISUPPORTS(TabParent,
                   nsIWebBrowserPersistable)
 
 TabParent::TabParent(nsIContentParent* aManager,
                      const TabId& aTabId,
                      const TabContext& aContext,
                      uint32_t aChromeFlags)
   : TabContext(aContext)
   , mFrameElement(nullptr)
+  , mContentCache(*this)
   , mRect(0, 0, 0, 0)
   , mDimensions(0, 0)
   , mOrientation(0)
   , mDPI(0)
   , mRounding(0)
   , mDefaultScale(0)
   , mUpdatedDimensions(false)
   , mSizeMode(nsSizeMode_Normal)
@@ -1740,17 +1741,17 @@ TabParent::RecvNotifyIMEFocus(const Cont
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     *aRequests = IMENotificationRequests();
     return IPC_OK();
   }
 
   mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
-  IMEStateManager::NotifyIME(aIMENotification, widget, true);
+  IMEStateManager::NotifyIME(aIMENotification, widget, this);
 
   if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
     *aRequests = widget->IMENotificationRequestsRef();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
@@ -1819,17 +1820,17 @@ TabParent::RecvNotifyIMEMouseButtonEvent
              bool* aConsumedByIME)
 {
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     *aConsumedByIME = false;
     return IPC_OK();
   }
-  nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, true);
+  nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, this);
   *aConsumedByIME = rv == NS_SUCCESS_EVENT_CONSUMED;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvNotifyIMEPositionChange(const ContentCache& aContentCache,
                                        const IMENotification& aIMENotification)
 {
--- a/gfx/ipc/GPUProcessHost.h
+++ b/gfx/ipc/GPUProcessHost.h
@@ -22,17 +22,17 @@ class GPUChild;
 
 // GPUProcessHost is the "parent process" container for a subprocess handle and
 // IPC connection. It owns the parent process IPDL actor, which in this case,
 // is a GPUChild.
 //
 // GPUProcessHosts are allocated and managed by GPUProcessManager. For all
 // intents and purposes it is a singleton, though more than one may be allocated
 // at a time due to its shutdown being asynchronous.
-class GPUProcessHost final : public ipc::GeckoChildProcessHost
+class GPUProcessHost final : public mozilla::ipc::GeckoChildProcessHost
 {
   friend class GPUChild;
 
 public:
   class Listener {
   public:
     virtual void OnProcessLaunchComplete(GPUProcessHost* aHost)
     {}
@@ -116,17 +116,17 @@ private:
   void KillHard(const char* aReason);
 
   void DestroyProcess();
 
 private:
   DISALLOW_COPY_AND_ASSIGN(GPUProcessHost);
 
   Listener* mListener;
-  ipc::TaskFactory<GPUProcessHost> mTaskFactory;
+  mozilla::ipc::TaskFactory<GPUProcessHost> mTaskFactory;
 
   enum class LaunchPhase {
     Unlaunched,
     Waiting,
     Complete
   };
   LaunchPhase mLaunchPhase;
 
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -94,20 +94,20 @@ public:
     LayerManager* aLayerManager,
     CSSToLayoutDeviceScale aScale,
     const CompositorOptions& aOptions,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   bool CreateContentBridges(
     base::ProcessId aOtherProcess,
-    ipc::Endpoint<PCompositorManagerChild>* aOutCompositor,
-    ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
-    ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
-    ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager,
+    mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutCompositor,
+    mozilla::ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
+    mozilla::ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
+    mozilla::ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager,
     nsTArray<uint32_t>* aNamespaces);
 
   // This returns a reference to the APZCTreeManager to which
   // pan/zoom-related events can be sent.
   already_AddRefed<IAPZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
 
   // Maps the layer tree and process together so that aOwningPID is allowed
   // to access aLayersId across process.
@@ -180,23 +180,23 @@ public:
     return mNumProcessAttempts > 0;
   }
 
 private:
   // Called from our xpcom-shutdown observer.
   void OnXPCOMShutdown();
 
   bool CreateContentCompositorManager(base::ProcessId aOtherProcess,
-                                      ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint);
+                                      mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
-                                ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
+                                mozilla::ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
   bool CreateContentVRManager(base::ProcessId aOtherProcess,
-                              ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
+                              mozilla::ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
   void CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
-                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
+                                        mozilla::ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
 
   // Called from RemoteCompositorSession. We track remote sessions so we can
   // notify their owning widgets that the session must be restarted.
   void RegisterRemoteProcessSession(RemoteCompositorSession* aSession);
   void UnregisterRemoteProcessSession(RemoteCompositorSession* aSession);
 
   // Called from InProcessCompositorSession. We track in process sessino so we can
   // notify their owning widgets that the session must be restarted
@@ -253,17 +253,17 @@ private:
     GPUProcessManager* mManager;
   };
   friend class Observer;
 
 private:
   bool mDecodeVideoOnGpuProcess = true;
 
   RefPtr<Observer> mObserver;
-  ipc::TaskFactory<GPUProcessManager> mTaskFactory;
+  mozilla::ipc::TaskFactory<GPUProcessManager> mTaskFactory;
   RefPtr<VsyncIOThreadHolder> mVsyncIOThread;
   uint32_t mNextNamespace;
   uint32_t mIdNamespace;
   uint32_t mResourceId;
   uint32_t mNumProcessAttempts;
 
   nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions;
   nsTArray<RefPtr<InProcessCompositorSession>> mInProcessSessions;
--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -1,28 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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/ContentCache.h"
+
 #include "mozilla/IMEStateManager.h"
+#include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Logging.h"
+#include "mozilla/Move.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/SizePrintfMacros.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
+#include "mozilla/dom/TabParent.h"
 #include "nsIWidget.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/Move.h"
-#include "mozilla/IntegerPrintfMacros.h"
-#include "mozilla/SizePrintfMacros.h"
 
 namespace mozilla {
 
+using namespace dom;
 using namespace widget;
 
 static const char*
 GetBoolName(bool aBool)
 {
   return aBool ? "true" : "false";
 }
 
@@ -506,18 +509,19 @@ ContentCacheInChild::SetSelection(nsIWid
   }
   Unused << NS_WARN_IF(!CacheTextRects(aWidget));
 }
 
 /*****************************************************************************
  * mozilla::ContentCacheInParent
  *****************************************************************************/
 
-ContentCacheInParent::ContentCacheInParent()
+ContentCacheInParent::ContentCacheInParent(TabParent& aTabParent)
   : ContentCache()
+  , mTabParent(aTabParent)
   , mCommitStringByRequest(nullptr)
   , mPendingEventsNeedingAck(0)
   , mCompositionStartInChild(UINT32_MAX)
   , mPendingCompositionCount(0)
   , mWidgetHasComposition(false)
 {
 }
 
@@ -1229,16 +1233,20 @@ ContentCacheInParent::RequestIMEToCommit
     MOZ_LOG(sContentCacheLog, LogLevel::Warning,
       ("  0x%p RequestToCommitComposition(), "
        "does nothing due to no composition", this));
     return false;
   }
 
   mCommitStringByRequest = &aCommittedString;
 
+  // TODO: This request may be too late.  For example, while the remote process
+  //       was busy, focus may be already changed to the main process and the
+  //       composition has already been canceled by IMEStateManager.  So, this
+  //       should check if IME focus is in the TabParent.
   aWidget->NotifyIME(IMENotification(aCancel ? REQUEST_TO_CANCEL_COMPOSITION :
                                                REQUEST_TO_COMMIT_COMPOSITION));
 
   mCommitStringByRequest = nullptr;
 
   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     ("  0x%p RequestToCommitComposition(), "
      "mWidgetHasComposition=%s, the composition %s committed synchronously",
@@ -1269,17 +1277,17 @@ ContentCacheInParent::RequestIMEToCommit
   return true;
 }
 
 void
 ContentCacheInParent::MaybeNotifyIME(nsIWidget* aWidget,
                                      const IMENotification& aNotification)
 {
   if (!mPendingEventsNeedingAck) {
-    IMEStateManager::NotifyIME(aNotification, aWidget, true);
+    IMEStateManager::NotifyIME(aNotification, aWidget, &mTabParent);
     return;
   }
 
   switch (aNotification.mMessage) {
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       mPendingSelectionChange.MergeWith(aNotification);
       break;
     case NOTIFY_IME_OF_TEXT_CHANGE:
@@ -1310,45 +1318,45 @@ ContentCacheInParent::FlushPendingNotifi
 
   // First, text change notification should be sent because selection change
   // notification notifies IME of current selection range in the latest content.
   // So, IME may need the latest content before that.
   if (mPendingTextChange.HasNotification()) {
     IMENotification notification(mPendingTextChange);
     if (!aWidget->Destroyed()) {
       mPendingTextChange.Clear();
-      IMEStateManager::NotifyIME(notification, aWidget, true);
+      IMEStateManager::NotifyIME(notification, aWidget, &mTabParent);
     }
   }
 
   if (mPendingSelectionChange.HasNotification()) {
     IMENotification notification(mPendingSelectionChange);
     if (!aWidget->Destroyed()) {
       mPendingSelectionChange.Clear();
-      IMEStateManager::NotifyIME(notification, aWidget, true);
+      IMEStateManager::NotifyIME(notification, aWidget, &mTabParent);
     }
   }
 
   // Layout change notification should be notified after selection change
   // notification because IME may want to query position of new caret position.
   if (mPendingLayoutChange.HasNotification()) {
     IMENotification notification(mPendingLayoutChange);
     if (!aWidget->Destroyed()) {
       mPendingLayoutChange.Clear();
-      IMEStateManager::NotifyIME(notification, aWidget, true);
+      IMEStateManager::NotifyIME(notification, aWidget, &mTabParent);
     }
   }
 
   // Finally, send composition update notification because it notifies IME of
   // finishing handling whole sending events.
   if (mPendingCompositionUpdate.HasNotification()) {
     IMENotification notification(mPendingCompositionUpdate);
     if (!aWidget->Destroyed()) {
       mPendingCompositionUpdate.Clear();
-      IMEStateManager::NotifyIME(notification, aWidget, true);
+      IMEStateManager::NotifyIME(notification, aWidget, &mTabParent);
     }
   }
 
   if (!--mPendingEventsNeedingAck && !aWidget->Destroyed() &&
       (mPendingTextChange.HasNotification() ||
        mPendingSelectionChange.HasNotification() ||
        mPendingLayoutChange.HasNotification() ||
        mPendingCompositionUpdate.HasNotification())) {
--- a/widget/ContentCache.h
+++ b/widget/ContentCache.h
@@ -18,16 +18,20 @@
 #include "nsString.h"
 #include "nsTArray.h"
 #include "Units.h"
 
 namespace mozilla {
 
 class ContentCacheInParent;
 
+namespace dom {
+class TabParent;
+} // namespace dom
+
 /**
  * ContentCache stores various information of the child content.
  * This class has members which are necessary both in parent process and
  * content process.
  */
 
 class ContentCache
 {
@@ -313,17 +317,17 @@ private:
                   const IMENotification* aNotification = nullptr);
   bool CacheTextRects(nsIWidget* aWidget,
                       const IMENotification* aNotification = nullptr);
 };
 
 class ContentCacheInParent final : public ContentCache
 {
 public:
-  ContentCacheInParent();
+  explicit ContentCacheInParent(dom::TabParent& aTabParent);
 
   /**
    * AssignContent() is called when TabParent receives ContentCache from
    * the content process.  This doesn't copy composition information because
    * it's managed by TabParent itself.
    */
   void AssignContent(const ContentCache& aOther,
                      nsIWidget* aWidget,
@@ -401,16 +405,18 @@ public:
                       const IMENotification& aNotification);
 
 private:
   IMENotification mPendingSelectionChange;
   IMENotification mPendingTextChange;
   IMENotification mPendingLayoutChange;
   IMENotification mPendingCompositionUpdate;
 
+  // mTabParent is owner of the instance.
+  dom::TabParent& MOZ_NON_OWNING_REF mTabParent;
   // This is not nullptr only while the instance is requesting IME to
   // composition.  Then, data value of dispatched composition events should
   // be stored into the instance.
   nsAString* mCommitStringByRequest;
   // mPendingEventsNeedingAck is increased before sending a composition event or
   // a selection event and decreased after they are received in the child
   // process.
   uint32_t mPendingEventsNeedingAck;
@@ -427,16 +433,18 @@ private:
   // mPendingCompositionCount is number of compositions which started in widget
   // but not yet handled in the child process.
   uint8_t mPendingCompositionCount;
   // mWidgetHasComposition is true when the widget in this process thinks that
   // IME has composition.  So, this is set to true when eCompositionStart is
   // dispatched and set to false when eCompositionCommit(AsIs) is dispatched.
   bool mWidgetHasComposition;
 
+  ContentCacheInParent() = delete;
+
   /**
    * When following methods' aRoundToExistingOffset is true, even if specified
    * offset or range is out of bounds, the result is computed with the existing
    * cache forcibly.
    */
   bool GetCaretRect(uint32_t aOffset,
                     bool aRoundToExistingOffset,
                     LayoutDeviceIntRect& aCaretRect) const;
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -393,18 +393,19 @@ IMEHandler::GetOpenState(nsWindow* aWind
 void
 IMEHandler::OnDestroyWindow(nsWindow* aWindow)
 {
   // When focus is in remote process, but the window is being destroyed, we
   // need to clean up TSFTextStore here since NOTIFY_IME_OF_BLUR won't reach
   // here because TabParent already lost the reference to the nsWindow when
   // it receives from the remote process.
   if (sFocusedWindow == aWindow) {
-    NS_ASSERTION(aWindow->GetInputContext().IsOriginContentProcess(),
-      "input context of focused widget should be set from a remote process");
+    MOZ_ASSERT(aWindow->GetInputContext().IsOriginContentProcess(),
+      "input context of focused widget should've been set by a remote process "
+      "if IME focus isn't cleared before destroying the widget");
     NotifyIME(aWindow, IMENotification(NOTIFY_IME_OF_BLUR));
   }
 
 #ifdef NS_ENABLE_TSF
   // We need to do nothing here for TSF. Just restore the default context
   // if it's been disassociated.
   if (!sIsInTSFMode) {
     // MSDN says we need to set IS_DEFAULT to avoid memory leak when we use