Bug 1342552 - IMEStateManager should cache nsIWidget for sPresContext and use it. r=smaug, a=gchang
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 20 Apr 2017 13:44:46 +0900
changeset 355769 ad995e90916bd6abbde54ba1c5233c0523dd094c
parent 355768 8886f9cd5dd3b6659f0f84095537e3643374467e
child 355770 4ae71415fecf32123ba4d039f6f8b94b90476c10
push id7074
push userryanvm@gmail.com
push dateSat, 06 May 2017 00:57:21 +0000
treeherdermozilla-esr52@4ae71415fecf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, gchang
bugs1342552
milestone52.1.2
Bug 1342552 - IMEStateManager should cache nsIWidget for sPresContext and use it. r=smaug, a=gchang IMEStateManager should cache nsIWidget for sPresContext at caching sPresContext. Then, even if sPresContext has gone, IMEStateManager can clean up with the nsIWidget cache. Unfortunately, editor has some bugs about calling IMEStateManager::UpdateIMEState(). That is, calling it *before* IMEStateManager::OnChangeFocus(). In such case, this patch makes UpdateIMEState() ignore the call.
dom/events/IMEStateManager.cpp
dom/events/IMEStateManager.h
editor/libeditor/EditorBase.cpp
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -4,16 +4,17 @@
  * 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/Logging.h"
 
 #include "mozilla/IMEStateManager.h"
 
 #include "mozilla/Attributes.h"
+#include "mozilla/EditorBase.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/Unused.h"
@@ -23,17 +24,16 @@
 #include "HTMLInputElement.h"
 #include "IMEContentObserver.h"
 
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMMouseEvent.h"
-#include "nsIEditor.h"
 #include "nsIForm.h"
 #include "nsIFormControl.h"
 #include "nsINode.h"
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsISelection.h"
 #include "nsISupports.h"
 #include "nsPresContext.h"
@@ -132,16 +132,17 @@ GetIMEStateSetOpenName(IMEState::Open aO
       return "CLOSED";
     default:
       return "illegal value";
   }
 }
 
 StaticRefPtr<nsIContent> IMEStateManager::sContent;
 StaticRefPtr<nsPresContext> IMEStateManager::sPresContext;
+nsIWidget* IMEStateManager::sWidget = nullptr;
 nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
 nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
 StaticRefPtr<TabParent> IMEStateManager::sActiveTabParent;
 StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
 TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
 bool IMEStateManager::sInstalledMenuKeyboardListener = false;
 bool IMEStateManager::sIsGettingNewIMEState = false;
 bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
@@ -187,16 +188,19 @@ IMEStateManager::OnTabParentDestroying(T
   // TODO: Need to cancel composition without TextComposition and make
   //       disable IME.
 }
 
 // static
 void
 IMEStateManager::WidgetDestroyed(nsIWidget* aWidget)
 {
+  if (sWidget == aWidget) {
+    sWidget = nullptr;
+  }
   if (sFocusedIMEWidget == aWidget) {
     sFocusedIMEWidget = nullptr;
   }
   if (sActiveInputContextWidget == aWidget) {
     sActiveInputContextWidget = nullptr;
   }
 }
 
@@ -284,23 +288,23 @@ IMEStateManager::OnDestroyPresContext(ns
 
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("OnDestroyPresContext(aPresContext=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
      aPresContext, sPresContext.get(), sContent.get(), sTextCompositions));
 
   DestroyIMEContentObserver();
 
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (widget) {
+  if (sWidget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
-    SetIMEState(newState, nullptr, widget, action);
+    SetIMEState(newState, nullptr, sWidget, action);
   }
+  sWidget = nullptr;
   sContent = nullptr;
   sPresContext = nullptr;
   sActiveTabParent = nullptr;
   return NS_OK;
 }
 
 // static
 nsresult
@@ -318,18 +322,16 @@ IMEStateManager::OnRemoveContent(nsPresC
       MOZ_LOG(sISMLog, LogLevel::Debug,
         ("  OnRemoveContent(), "
          "composition is in the content"));
 
       // Try resetting the native IME state.  Be aware, typically, this method
       // is called during the content being removed.  Then, the native
       // composition events which are caused by following APIs are ignored due
       // to unsafe to run script (in PresShell::HandleEvent()).
-      DebugOnly<void*> widget = aPresContext->GetRootWidget();
-      MOZ_ASSERT(widget, "Why is there no widget?");
       nsresult rv =
         compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
       if (NS_FAILED(rv)) {
         compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
       }
     }
   }
 
@@ -341,32 +343,41 @@ IMEStateManager::OnRemoveContent(nsPresC
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("OnRemoveContent(aPresContext=0x%p, aContent=0x%p), "
      "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
      aPresContext, aContent, sPresContext.get(), sContent.get(), sTextCompositions));
 
   DestroyIMEContentObserver();
 
   // Current IME transaction should commit
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (widget) {
+  if (sWidget) {
     IMEState newState = GetNewIMEState(sPresContext, nullptr);
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::LOST_FOCUS);
-    SetIMEState(newState, nullptr, widget, action);
+    SetIMEState(newState, nullptr, sWidget, action);
   }
 
+  sWidget = nullptr;
   sContent = nullptr;
   sPresContext = nullptr;
   sActiveTabParent = nullptr;
 
   return NS_OK;
 }
 
 // static
+bool
+IMEStateManager::CanHandleWith(nsPresContext* aPresContext)
+{
+  return aPresContext &&
+         aPresContext->GetPresShell() &&
+         !aPresContext->PresShell()->IsDestroying();
+}
+
+// static
 nsresult
 IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
                                nsIContent* aContent,
                                InputContextAction::Cause aCause)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("OnChangeFocus(aPresContext=0x%p, aContent=0x%p, aCause=%s)",
      aPresContext, aContent, GetActionCauseName(aCause)));
@@ -379,33 +390,46 @@ IMEStateManager::OnChangeFocus(nsPresCon
 nsresult
 IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
                                        nsIContent* aContent,
                                        InputContextAction aAction)
 {
   RefPtr<TabParent> newTabParent = TabParent::GetFrom(aContent);
 
   MOZ_LOG(sISMLog, LogLevel::Info,
-    ("OnChangeFocusInternal(aPresContext=0x%p, "
+    ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), "
      "aContent=0x%p (TabParent=0x%p), aAction={ mCause=%s, mFocusChange=%s }), "
-     "sPresContext=0x%p, sContent=0x%p, sActiveTabParent=0x%p, "
+     "sPresContext=0x%p (available: %s), sContent=0x%p, "
+     "sWidget=0x%p (available: %s), sActiveTabParent=0x%p, "
      "sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s",
-     aPresContext, aContent, newTabParent.get(),
-     GetActionCauseName(aAction.mCause),
+     aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
+     newTabParent.get(), GetActionCauseName(aAction.mCause),
      GetActionFocusChangeName(aAction.mFocusChange),
-     sPresContext.get(), sContent.get(), sActiveTabParent.get(),
-     sActiveIMEContentObserver.get(),
+     sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)),
+     sContent.get(), sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
+     sActiveTabParent.get(), sActiveIMEContentObserver.get(),
      GetBoolName(sInstalledMenuKeyboardListener)));
 
+  // If new aPresShell has been destroyed, this should handle the focus change
+  // as nobody is getting focus.
+  if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) {
+    MOZ_LOG(sISMLog, LogLevel::Warning,
+      ("  OnChangeFocusInternal(), called with destroyed PresShell, "
+       "handling this call as nobody getting focus"));
+    aPresContext = nullptr;
+    aContent = nullptr;
+  }
+
+  nsCOMPtr<nsIWidget> oldWidget = sWidget;
+  nsCOMPtr<nsIWidget> newWidget =
+    aPresContext ? aPresContext->GetRootWidget() : nullptr;
   bool focusActuallyChanging =
     (sContent != aContent || sPresContext != aPresContext ||
-     sActiveTabParent != newTabParent);
+     oldWidget != newWidget || sActiveTabParent != newTabParent);
 
-  nsCOMPtr<nsIWidget> oldWidget =
-    sPresContext ? sPresContext->GetRootWidget() : nullptr;
   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);
     }
   }
 
@@ -429,26 +453,27 @@ IMEStateManager::OnChangeFocusInternal(n
   if (sActiveTabParent && currentContentParent != newContentParent) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnChangeFocusInternal(), notifying previous "
        "focused child process of parent process or another child process "
        "getting focus"));
     Unused << sActiveTabParent->SendStopIMEStateManagement();
   }
 
-  nsCOMPtr<nsIWidget> widget =
-    (sPresContext == aPresContext) ? oldWidget.get() :
-                                     aPresContext->GetRootWidget();
-  if (NS_WARN_IF(!widget)) {
+  if (NS_WARN_IF(!newWidget)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  OnChangeFocusInternal(), FAILED due to "
        "no widget to manage its IME state"));
     return NS_OK;
   }
 
+  // Update the cached widget since root view of the presContext may be
+  // changed to different view.
+  sWidget = newWidget;
+
   // If a child process has focus, we should disable IME state until the child
   // process actually gets focus because if user types keys before that they
   // are handled by IME.
   IMEState newState =
     newTabParent ? IMEState(IMEState::DISABLED) :
                    GetNewIMEState(aPresContext, aContent);
   bool setIMEState = true;
 
@@ -458,17 +483,17 @@ IMEStateManager::OnChangeFocusInternal(n
       // XXX When menu keyboard listener is being uninstalled, IME state needs
       //     to be restored by the child process asynchronously.  Therefore,
       //     some key events which are fired immediately after closing menu
       //     may not be handled by IME.
       Unused << newTabParent->
         SendMenuKeyboardListenerInstalled(sInstalledMenuKeyboardListener);
       setIMEState = sInstalledMenuKeyboardListener;
     } else if (focusActuallyChanging) {
-      InputContext context = widget->GetInputContext();
+      InputContext context = newWidget->GetInputContext();
       if (context.mIMEState.mEnabled == IMEState::DISABLED) {
         setIMEState = false;
         MOZ_LOG(sISMLog, LogLevel::Debug,
           ("  OnChangeFocusInternal(), doesn't set IME "
            "state because focused element (or document) is in a child process "
            "and the IME state is already disabled"));
       } else {
         MOZ_LOG(sISMLog, LogLevel::Debug,
@@ -488,17 +513,17 @@ IMEStateManager::OnChangeFocusInternal(n
          "process"));
     }
   }
 
   if (setIMEState) {
     if (!focusActuallyChanging) {
       // actual focus isn't changing, but if IME enabled state is changing,
       // we should do it.
-      InputContext context = widget->GetInputContext();
+      InputContext context = newWidget->GetInputContext();
       if (context.mIMEState.mEnabled == newState.mEnabled) {
         MOZ_LOG(sISMLog, LogLevel::Debug,
           ("  OnChangeFocusInternal(), "
            "neither focus nor IME state is changing"));
         return NS_OK;
       }
       aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
 
@@ -512,17 +537,17 @@ IMEStateManager::OnChangeFocusInternal(n
       // focus.
       bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
       aAction.mFocusChange =
         gotFocus ? InputContextAction::GOT_FOCUS :
                    InputContextAction::LOST_FOCUS;
     }
 
     // Update IME state for new focus widget
-    SetIMEState(newState, aContent, widget, aAction);
+    SetIMEState(newState, aContent, newWidget, aAction);
   }
 
   sActiveTabParent = newTabParent;
   sPresContext = aPresContext;
   sContent = aContent;
 
   // Don't call CreateIMEContentObserver() here except when a plugin gets
   // focus because it will be called from the focus event handler of focused
@@ -618,28 +643,33 @@ IMEStateManager::OnMouseButtonEventInEdi
 // static
 void
 IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
                                  nsIContent* aContent,
                                  nsIDOMMouseEvent* aMouseEvent)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("OnClickInEditor(aPresContext=0x%p, aContent=0x%p, aMouseEvent=0x%p), "
-     "sPresContext=0x%p, sContent=0x%p",
-     aPresContext, aContent, aMouseEvent, sPresContext.get(), sContent.get()));
+     "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s)",
+     aPresContext, aContent, aMouseEvent, sPresContext.get(), sContent.get(),
+     sWidget, GetBoolName(sWidget && !sWidget->Destroyed())));
 
-  if (sPresContext != aPresContext || sContent != aContent) {
+  if (sPresContext != aPresContext || sContent != aContent ||
+      NS_WARN_IF(!sPresContext) || NS_WARN_IF(!sWidget) ||
+      NS_WARN_IF(sWidget->Destroyed())) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnClickInEditor(), "
        "the mouse event isn't fired on the editor managed by ISM"));
     return;
   }
 
-  nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
-  NS_ENSURE_TRUE_VOID(widget);
+  nsCOMPtr<nsIWidget> widget(sWidget);
+
+  MOZ_ASSERT(!sPresContext->GetRootWidget() ||
+             sPresContext->GetRootWidget() == widget);
 
   bool isTrusted;
   nsresult rv = aMouseEvent->AsEvent()->GetIsTrusted(&isTrusted);
   NS_ENSURE_SUCCESS_VOID(rv);
   if (!isTrusted) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnClickInEditor(), "
        "the mouse event isn't a trusted event"));
@@ -752,106 +782,174 @@ IMEStateManager::OnEditorDestroying(nsIE
   // is finished.
   sActiveIMEContentObserver->SuppressNotifyingIME();
 }
 
 // static
 void
 IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
                                 nsIContent* aContent,
-                                nsIEditor* aEditor)
+                                EditorBase& aEditorBase)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("UpdateIMEState(aNewIMEState={ mEnabled=%s, "
-     "mOpen=%s }, aContent=0x%p, aEditor=0x%p), "
-     "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p, "
-     "sIsGettingNewIMEState=%s",
+     "mOpen=%s }, aContent=0x%p, aEditorBase=0x%p), "
+     "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
+     "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
      GetIMEStateEnabledName(aNewIMEState.mEnabled),
-     GetIMEStateSetOpenName(aNewIMEState.mOpen), aContent, aEditor,
-     sPresContext.get(), sContent.get(), sActiveIMEContentObserver.get(),
+     GetIMEStateSetOpenName(aNewIMEState.mOpen), aContent, &aEditorBase,
+     sPresContext.get(), sContent.get(),
+     sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
+     sActiveIMEContentObserver.get(),
      GetBoolName(sIsGettingNewIMEState)));
 
   if (sIsGettingNewIMEState) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  UpdateIMEState(), "
        "does nothing because of called while getting new IME state"));
     return;
   }
 
+  nsCOMPtr<nsIPresShell> presShell = aEditorBase.GetPresShell();
+  if (NS_WARN_IF(!presShell)) {
+    MOZ_LOG(sISMLog, LogLevel::Error,
+      ("  UpdateIMEState(), FAILED due to "
+       "editor doesn't have PresShell"));
+    return;
+  }
+
+  nsPresContext* presContext = presShell->GetPresContext();
+  if (NS_WARN_IF(!presContext)) {
+    MOZ_LOG(sISMLog, LogLevel::Error,
+      ("  UpdateIMEState(), FAILED due to "
+       "editor doesn't have PresContext"));
+    return;
+  }
+
+  // IMEStateManager::UpdateIMEState() should be called after
+  // IMEStateManager::OnChangeFocus() is called for setting focus to aContent
+  // and aEditorBase.  However, when aEditorBase is an HTMLEditor, this may be
+  // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus().
+  // Similarly, when aEditorBase is a TextEditor, this may be called by
+  // nsIEditor::SetFlags().  In such cases, this method should do nothing
+  // because input context should be updated when
+  // IMEStateManager::OnChangeFocus() is called later.
+  if (sPresContext != presContext) {
+    MOZ_LOG(sISMLog, LogLevel::Warning,
+      ("  UpdateIMEState(), does nothing due to "
+       "the editor hasn't managed by IMEStateManager yet"));
+    return;
+  }
+
+  // If IMEStateManager doesn't manage any document, this cannot update IME
+  // state of any widget.
   if (NS_WARN_IF(!sPresContext)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  UpdateIMEState(), FAILED due to "
        "no managing nsPresContext"));
     return;
   }
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (NS_WARN_IF(!widget)) {
+
+  if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  UpdateIMEState(), FAILED due to "
-       "no widget for the managing nsPresContext"));
+       "the widget for the managing nsPresContext has gone"));
     return;
   }
 
+  nsCOMPtr<nsIWidget> widget(sWidget);
+
+  MOZ_ASSERT(!sPresContext->GetRootWidget() ||
+             sPresContext->GetRootWidget() == widget);
+
   // Even if there is active IMEContentObserver, it may not be observing the
   // editor with current editable root content due to reframed.  In such case,
   // We should try to reinitialize the IMEContentObserver.
   if (sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState)) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  UpdateIMEState(), try to reinitialize the "
        "active IMEContentObserver"));
     if (!sActiveIMEContentObserver->MaybeReinitialize(widget, sPresContext,
-                                                      aContent, aEditor)) {
+                                                      aContent, &aEditorBase)) {
       MOZ_LOG(sISMLog, LogLevel::Error,
         ("  UpdateIMEState(), failed to reinitialize the "
          "active IMEContentObserver"));
     }
+    if (NS_WARN_IF(widget->Destroyed())) {
+      MOZ_LOG(sISMLog, LogLevel::Error,
+        ("  UpdateIMEState(), widget has gone during reinitializing the "
+         "active IMEContentObserver"));
+      return;
+    }
   }
 
   // If there is no active IMEContentObserver or it isn't observing the
   // editor correctly, we should recreate it.
   bool createTextStateManager =
     (!sActiveIMEContentObserver ||
      !sActiveIMEContentObserver->IsManaging(sPresContext, aContent));
 
   bool updateIMEState =
     (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
+  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);
+    if (NS_WARN_IF(widget->Destroyed())) {
+      MOZ_LOG(sISMLog, LogLevel::Error,
+        ("  UpdateIMEState(), widget has gone during committing composition"));
+      return;
+    }
   }
 
   if (createTextStateManager) {
     DestroyIMEContentObserver();
   }
 
   if (updateIMEState) {
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::FOCUS_NOT_CHANGED);
     SetIMEState(aNewIMEState, aContent, widget, action);
+    if (NS_WARN_IF(widget->Destroyed())) {
+      MOZ_LOG(sISMLog, LogLevel::Error,
+        ("  UpdateIMEState(), widget has gone during setting input context"));
+      return;
+    }
   }
 
   if (createTextStateManager) {
     // XXX In this case, it might not be enough safe to notify IME of anything.
     //     So, don't try to flush pending notifications of IMEContentObserver
     //     here.
-    CreateIMEContentObserver(aEditor);
+    CreateIMEContentObserver(&aEditorBase);
   }
 }
 
 // static
 IMEState
 IMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
                                 nsIContent*    aContent)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("GetNewIMEState(aPresContext=0x%p, aContent=0x%p), "
      "sInstalledMenuKeyboardListener=%s",
      aPresContext, aContent, GetBoolName(sInstalledMenuKeyboardListener)));
 
+  if (!CanHandleWith(aPresContext)) {
+    MOZ_LOG(sISMLog, LogLevel::Debug,
+      ("  GetNewIMEState() returns DISABLED because "
+       "the nsPresContext has been destroyed"));
+    return IMEState(IMEState::DISABLED);
+  }
+
   // On Printing or Print Preview, we don't need IME.
   if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
       aPresContext->Type() == nsPresContext::eContext_Print) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  GetNewIMEState() returns DISABLED because "
        "the nsPresContext is for print or print preview"));
     return IMEState(IMEState::DISABLED);
   }
@@ -920,49 +1018,55 @@ IMEStateManager::SetInputContextForChild
                    TabParent* aTabParent,
                    const InputContext& aInputContext,
                    const InputContextAction& aAction)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("SetInputContextForChildProcess(aTabParent=0x%p, "
      "aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, "
      "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
-     "aAction={ mCause=%s, mAction=%s }, aTabParent=0x%p), sPresContext=0x%p, "
+     "aAction={ mCause=%s, mAction=%s }), "
+     "sPresContext=0x%p (available: %s), sWidget=0x%p (available: %s), "
      "sActiveTabParent=0x%p",
      aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
      GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
      NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
      GetActionCauseName(aAction.mCause),
      GetActionFocusChangeName(aAction.mFocusChange),
-     sPresContext.get(), sActiveTabParent.get()));
+     sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)),
+     sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
+     sActiveTabParent.get()));
 
   if (aTabParent != sActiveTabParent) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  SetInputContextForChildProcess(), FAILED, "
        "because non-focused tab parent tries to set input context"));
     return;
   }
 
-  if (NS_WARN_IF(!sPresContext)) {
+  if (NS_WARN_IF(!CanHandleWith(sPresContext))) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  SetInputContextForChildProcess(), FAILED, "
        "due to no focused presContext"));
     return;
   }
 
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (NS_WARN_IF(!widget)) {
+  if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  SetInputContextForChildProcess(), FAILED, "
-       "due to no widget in the focused presContext"));
+       "due to the widget for the nsPresContext has gone"));
     return;
   }
 
+  nsCOMPtr<nsIWidget> widget(sWidget);
+
+  MOZ_ASSERT(!sPresContext->GetRootWidget() ||
+             sPresContext->GetRootWidget() == widget);
   MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
 
   SetInputContext(widget, aInputContext, aAction);
 }
 
 // static
 void
 IMEStateManager::SetIMEState(const IMEState& aState,
@@ -1080,18 +1184,19 @@ IMEStateManager::SetInputContext(nsIWidg
      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
      NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
      GetActionCauseName(aAction.mCause),
      GetActionFocusChangeName(aAction.mFocusChange),
      sActiveTabParent.get()));
 
   MOZ_RELEASE_ASSERT(aWidget);
 
-  aWidget->SetInputContext(aInputContext, aAction);
-  sActiveInputContextWidget = aWidget;
+  nsCOMPtr<nsIWidget> widget(aWidget);
+  widget->SetInputContext(aInputContext, aAction);
+  sActiveInputContextWidget = widget;
 }
 
 // static
 void
 IMEStateManager::EnsureTextCompositionArray()
 {
   if (sTextCompositions) {
     return;
@@ -1331,17 +1436,17 @@ IMEStateManager::NotifyIME(const IMENoti
 
   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:
+    case NOTIFY_IME_OF_FOCUS: {
       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 "
@@ -1350,17 +1455,19 @@ IMEStateManager::NotifyIME(const IMENoti
         }
         nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
         sFocusedIMEWidget = nullptr;
         sRemoteHasFocus = false;
         focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
       }
       sRemoteHasFocus = aOriginIsRemote;
       sFocusedIMEWidget = aWidget;
-      return aWidget->NotifyIME(aNotification);
+      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..."));
         return NS_OK;
       }
       if (NS_WARN_IF(sRemoteHasFocus && !aOriginIsRemote)) {
@@ -1391,17 +1498,17 @@ IMEStateManager::NotifyIME(const IMENoti
       sFocusedIMEWidget = nullptr;
       sRemoteHasFocus = false;
       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:
+    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,
@@ -1418,17 +1525,19 @@ IMEStateManager::NotifyIME(const IMENoti
       }
       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..."));
         return NS_OK;
       }
-      return aWidget->NotifyIME(aNotification);
+      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) {
@@ -1463,17 +1572,19 @@ nsresult
 IMEStateManager::NotifyIME(IMEMessage aMessage,
                            nsPresContext* aPresContext,
                            bool aOriginIsRemote)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("NotifyIME(aMessage=%s, aPresContext=0x%p, aOriginIsRemote=%s)",
      ToChar(aMessage), aPresContext, GetBoolName(aOriginIsRemote)));
 
-  NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
+  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;
   }
@@ -1558,46 +1669,59 @@ IMEStateManager::DestroyIMEContentObserv
 }
 
 // static
 void
 IMEStateManager::CreateIMEContentObserver(nsIEditor* aEditor)
 {
   MOZ_LOG(sISMLog, LogLevel::Info,
     ("CreateIMEContentObserver(aEditor=0x%p), "
-     "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p, "
+     "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
+     "sActiveIMEContentObserver=0x%p, "
      "sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
-     aEditor, sPresContext.get(), sContent.get(), sActiveIMEContentObserver.get(),
+     aEditor, sPresContext.get(), sContent.get(),
+     sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
+     sActiveIMEContentObserver.get(),
      GetBoolName(sActiveIMEContentObserver ?
        sActiveIMEContentObserver->IsManaging(sPresContext, sContent) : false)));
 
   if (NS_WARN_IF(sActiveIMEContentObserver)) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  CreateIMEContentObserver(), FAILED due to "
        "there is already an active IMEContentObserver"));
     MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
     return;
   }
 
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (!widget) {
+  if (!sWidget || NS_WARN_IF(sWidget->Destroyed())) {
     MOZ_LOG(sISMLog, LogLevel::Error,
       ("  CreateIMEContentObserver(), FAILED due to "
-       "there is a root widget for the nsPresContext"));
+       "the widget for the nsPresContext has gone"));
     return; // Sometimes, there are no widgets.
   }
 
+  nsCOMPtr<nsIWidget> widget(sWidget);
+
   // If it's not text editable, we don't need to create IMEContentObserver.
   if (!IsIMEObserverNeeded(widget->GetInputContext().mIMEState)) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  CreateIMEContentObserver() doesn't create "
        "IMEContentObserver because of non-editable IME state"));
     return;
   }
 
+  if (NS_WARN_IF(widget->Destroyed())) {
+    MOZ_LOG(sISMLog, LogLevel::Error,
+      ("  CreateIMEContentObserver(), FAILED due to "
+       "the widget for the nsPresContext has gone"));
+    return;
+  }
+
+  MOZ_ASSERT(sPresContext->GetRootWidget() == widget);
+
   MOZ_LOG(sISMLog, LogLevel::Debug,
     ("  CreateIMEContentObserver() is creating an "
      "IMEContentObserver instance..."));
   sActiveIMEContentObserver = new IMEContentObserver();
 
   // IMEContentObserver::Init() might create another IMEContentObserver
   // instance.  So, sActiveIMEContentObserver would be replaced with new one.
   // We should hold the current instance here.
--- a/dom/events/IMEStateManager.h
+++ b/dom/events/IMEStateManager.h
@@ -9,23 +9,23 @@
 
 #include "mozilla/EventForwards.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsIWidget.h"
 
 class nsIContent;
 class nsIDOMMouseEvent;
-class nsIEditor;
 class nsINode;
 class nsPresContext;
 class nsISelection;
 
 namespace mozilla {
 
+class EditorBase;
 class EventDispatchingCallback;
 class IMEContentObserver;
 class TextCompositionArray;
 class TextComposition;
 
 /**
  * IMEStateManager manages InputContext (e.g., active editor type, IME enabled
  * state and IME open state) of nsIWidget instances, manages IMEContentObserver
@@ -130,17 +130,17 @@ public:
   static nsresult GetFocusSelectionAndRoot(nsISelection** aSel,
                                            nsIContent** aRoot);
   // This method updates the current IME state.  However, if the enabled state
   // isn't changed by the new state, this method does nothing.
   // Note that this method changes the IME state of the active element in the
   // widget.  So, the caller must have focus.
   static void UpdateIMEState(const IMEState &aNewIMEState,
                              nsIContent* aContent,
-                             nsIEditor* aEditor);
+                             EditorBase& aEditorBase);
 
   // This method is called when user operates mouse button in focused editor
   // and before the editor handles it.
   // Returns true if IME consumes the event.  Otherwise, false.
   static bool OnMouseButtonEventInEditor(nsPresContext* aPresContext,
                                          nsIContent* aContent,
                                          nsIDOMMouseEvent* aMouseEvent);
 
@@ -261,21 +261,39 @@ protected:
   static void DestroyIMEContentObserver();
 
   static bool IsEditable(nsINode* node);
 
   static bool IsIMEObserverNeeded(const IMEState& aState);
 
   static nsIContent* GetRootContent(nsPresContext* aPresContext);
 
+  /**
+   * CanHandleWith() returns false if aPresContext is nullptr or it's destroyed.
+   */
+  static bool CanHandleWith(nsPresContext* aPresContext);
+
+  // sContent and sPresContext are the focused content and PresContext.  If a
+  // 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.
   static nsIWidget* sFocusedIMEWidget;
   // sActiveInputContextWidget is the last widget whose SetInputContext() is
-  // called.
+  // 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.
   static StaticRefPtr<IMEContentObserver> sActiveIMEContentObserver;
 
   // All active compositions in the process are stored by this array.
   // When you get an item of this array and use it, please be careful.
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -323,17 +323,17 @@ EditorBase::PostCreate()
     EditorEventListener* listener =
       reinterpret_cast<EditorEventListener*>(mEventListener.get());
     listener->SpellCheckIfNeeded();
 
     IMEState newState;
     rv = GetPreferredIMEState(&newState);
     NS_ENSURE_SUCCESS(rv, NS_OK);
     nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
-    IMEStateManager::UpdateIMEState(newState, content, this);
+    IMEStateManager::UpdateIMEState(newState, content, *this);
   }
 
   // FYI: This call might cause destroying this editor.
   IMEStateManager::OnEditorInitialized(this);
 
   return NS_OK;
 }
 
@@ -511,17 +511,17 @@ EditorBase::SetFlags(uint32_t aFlags)
   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
   if (focusedContent) {
     IMEState newState;
     nsresult rv = GetPreferredIMEState(&newState);
     if (NS_SUCCEEDED(rv)) {
       // NOTE: When the enabled state isn't going to be modified, this method
       // is going to do nothing.
       nsCOMPtr<nsIContent> content = GetFocusedContentForIME();
-      IMEStateManager::UpdateIMEState(newState, content, this);
+      IMEStateManager::UpdateIMEState(newState, content, *this);
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::GetIsSelectionEditable(bool* aIsSelectionEditable)