Bug 978023 part.1 nsTextStateManager should be mozilla::IMEContentObserver and separated from nsIMEStateManager.cpp r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 08 Mar 2014 10:20:07 +0900
changeset 190849 ba18273bd056f540f29caef41b27c75d649a81d6
parent 190848 9cf34c82f975bdcaff30c22c299dfcd1a66df99a
child 190850 21ed4b9b06153df0d9ba04e0acc6e7a40491bf21
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs978023
milestone30.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 978023 part.1 nsTextStateManager should be mozilla::IMEContentObserver and separated from nsIMEStateManager.cpp r=smaug
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
dom/events/moz.build
dom/events/nsIMEStateManager.cpp
dom/events/nsIMEStateManager.h
copy from dom/events/nsIMEStateManager.cpp
copy to dom/events/IMEContentObserver.cpp
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1,711 +1,56 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIMEStateManager.h"
-
-#include "HTMLInputElement.h"
-#include "nsCOMPtr.h"
-#include "nsIPresShell.h"
-#include "nsISupports.h"
+#include "IMEContentObserver.h"
+#include "mozilla/dom/Element.h"
+#include "nsAutoPtr.h"
+#include "nsAsyncDOMEvent.h"
+#include "nsContentEventHandler.h"
+#include "nsContentUtils.h"
+#include "nsGkAtoms.h"
+#include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
-#include "nsPresContext.h"
-#include "nsIDOMMouseEvent.h"
-#include "nsContentUtils.h"
-#include "nsINode.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMRange.h"
 #include "nsIFrame.h"
-#include "nsRange.h"
-#include "nsIDOMRange.h"
-#include "nsISelection.h"
-#include "nsISelectionPrivate.h"
-#include "nsISelectionListener.h"
+#include "nsIMEStateManager.h"
+#include "nsINode.h"
+#include "nsIPresShell.h"
 #include "nsISelectionController.h"
-#include "nsIMutationObserver.h"
-#include "nsContentEventHandler.h"
-#include "nsIObserverService.h"
-#include "mozilla/Services.h"
-#include "nsIFormControl.h"
-#include "nsIForm.h"
-#include "mozilla/dom/HTMLFormElement.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/TextEvents.h"
-#include "TextComposition.h"
-#include "mozilla/Preferences.h"
-#include "nsAsyncDOMEvent.h"
-#include "nsIDocShell.h"
-#include "nsIReflowObserver.h"
-#include "nsIScrollObserver.h"
+#include "nsISelectionPrivate.h"
+#include "nsISupports.h"
+#include "nsIWidget.h"
+#include "nsPresContext.h"
+#include "nsThreadUtils.h"
 #include "nsWeakReference.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-using namespace mozilla::widget;
-
-// nsTextStateManager notifies widget of any text and selection changes
-//  in the currently focused editor
-// sTextStateObserver points to the currently active nsTextStateManager
-// sTextStateObserver is null if there is no focused editor
-
-class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
-                                     public nsStubMutationObserver,
-                                     public nsIReflowObserver,
-                                     public nsIScrollObserver,
-                                     public nsSupportsWeakReference
-{
-public:
-  nsTextStateManager()
-  {
-  }
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSISELECTIONLISTENER
-  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
-  NS_DECL_NSIREFLOWOBSERVER
-
-  // nsIScrollObserver
-  virtual void ScrollPositionChanged() MOZ_OVERRIDE;
-
-  void     Init(nsIWidget* aWidget,
-                nsPresContext* aPresContext,
-                nsIContent* aContent);
-  void     Destroy(void);
-  bool     IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
-  bool     IsEditorHandlingEventForComposition() const;
-  bool     KeepAliveDuringDeactive() const
-  {
-    return mUpdatePreference.WantDuringDeactive();
-  }
-
-  nsCOMPtr<nsIWidget>            mWidget;
-  nsCOMPtr<nsISelection>         mSel;
-  nsCOMPtr<nsIContent>           mRootContent;
-  nsCOMPtr<nsINode>              mEditableNode;
-
-private:
-  void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
-  void ObserveEditableNode();
-
-  nsCOMPtr<nsIDocShell>          mDocShell;
-  nsIMEUpdatePreference mUpdatePreference;
-  uint32_t mPreAttrChangeLength;
-};
-
-/******************************************************************/
-/* nsIMEStateManager                                              */
-/******************************************************************/
-
-nsIContent*    nsIMEStateManager::sContent      = nullptr;
-nsPresContext* nsIMEStateManager::sPresContext  = nullptr;
-bool           nsIMEStateManager::sInstalledMenuKeyboardListener = false;
-bool           nsIMEStateManager::sIsTestingIME = false;
-
-nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
-TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr;
-
-void
-nsIMEStateManager::Shutdown()
-{
-  MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
-  delete sTextCompositions;
-  sTextCompositions = nullptr;
-}
-
-nsresult
-nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
-{
-  NS_ENSURE_ARG_POINTER(aPresContext);
-
-  // First, if there is a composition in the aPresContext, clean up it.
-  if (sTextCompositions) {
-    TextCompositionArray::index_type i =
-      sTextCompositions->IndexOf(aPresContext);
-    if (i != TextCompositionArray::NoIndex) {
-      // there should be only one composition per presContext object.
-      sTextCompositions->ElementAt(i)->Destroy();
-      sTextCompositions->RemoveElementAt(i);
-      MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) ==
-                   TextCompositionArray::NoIndex);
-    }
-  }
-
-  if (aPresContext != sPresContext)
-    return NS_OK;
-
-  DestroyTextStateManager();
-
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (widget) {
-    IMEState newState = GetNewIMEState(sPresContext, nullptr);
-    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-                              InputContextAction::LOST_FOCUS);
-    SetIMEState(newState, nullptr, widget, action);
-  }
-  NS_IF_RELEASE(sContent);
-  sPresContext = nullptr;
-  return NS_OK;
-}
-
-nsresult
-nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
-                                   nsIContent* aContent)
-{
-  NS_ENSURE_ARG_POINTER(aPresContext);
-
-  // First, if there is a composition in the aContent, clean up it.
-  if (sTextCompositions) {
-    nsRefPtr<TextComposition> compositionInContent =
-      sTextCompositions->GetCompositionInContent(aPresContext, aContent);
+#include "TextComposition.h"
 
-    if (compositionInContent) {
-      // 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()).
-      nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
-      if (widget) {
-        nsresult rv =
-          compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
-        if (NS_FAILED(rv)) {
-          compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
-        }
-        // By calling the APIs, the composition may have been finished normally.
-        compositionInContent =
-          sTextCompositions->GetCompositionFor(
-                               compositionInContent->GetPresContext(),
-                               compositionInContent->GetEventTargetNode());
-      }
-    }
-
-    // If the compositionInContent is still available, we should finish the
-    // composition just on the content forcibly.
-    if (compositionInContent) {
-      compositionInContent->SynthesizeCommit(true);
-    }
-  }
-
-  if (!sPresContext || !sContent ||
-      !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
-    return NS_OK;
-  }
-
-  DestroyTextStateManager();
-
-  // Current IME transaction should commit
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (widget) {
-    IMEState newState = GetNewIMEState(sPresContext, nullptr);
-    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-                              InputContextAction::LOST_FOCUS);
-    SetIMEState(newState, nullptr, widget, action);
-  }
+namespace mozilla {
 
-  NS_IF_RELEASE(sContent);
-  sPresContext = nullptr;
-
-  return NS_OK;
-}
-
-nsresult
-nsIMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
-                                 nsIContent* aContent,
-                                 InputContextAction::Cause aCause)
-{
-  InputContextAction action(aCause);
-  return OnChangeFocusInternal(aPresContext, aContent, action);
-}
-
-nsresult
-nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
-                                         nsIContent* aContent,
-                                         InputContextAction aAction)
-{
-  bool focusActuallyChanging =
-    (sContent != aContent || sPresContext != aPresContext);
-
-  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);
-    }
-  }
-
-  if (sTextStateObserver &&
-      (aPresContext || !sTextStateObserver->KeepAliveDuringDeactive()) &&
-      !sTextStateObserver->IsManaging(aPresContext, aContent)) {
-    DestroyTextStateManager();
-  }
-
-  if (!aPresContext) {
-    return NS_OK;
-  }
+using namespace widget;
 
-  nsCOMPtr<nsIWidget> widget =
-    (sPresContext == aPresContext) ? oldWidget.get() :
-                                     aPresContext->GetRootWidget();
-  if (!widget) {
-    return NS_OK;
-  }
-
-  IMEState newState = GetNewIMEState(aPresContext, aContent);
-  if (!focusActuallyChanging) {
-    // actual focus isn't changing, but if IME enabled state is changing,
-    // we should do it.
-    InputContext context = widget->GetInputContext();
-    if (context.mIMEState.mEnabled == newState.mEnabled) {
-      // the enabled state isn't 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);
-    }
-  } 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;
-  }
-
-  // Update IME state for new focus widget
-  SetIMEState(newState, aContent, widget, aAction);
-
-  sPresContext = aPresContext;
-  if (sContent != aContent) {
-    NS_IF_RELEASE(sContent);
-    NS_IF_ADDREF(sContent = aContent);
-  }
-
-  // Don't call CreateTextStateManager() here, it should be called from
-  // focus event handler of editor.
-
-  return NS_OK;
-}
+NS_IMPL_ISUPPORTS5(IMEContentObserver,
+                   nsIMutationObserver,
+                   nsISelectionListener,
+                   nsIReflowObserver,
+                   nsIScrollObserver,
+                   nsISupportsWeakReference)
 
-void
-nsIMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling)
-{
-  sInstalledMenuKeyboardListener = aInstalling;
-
-  InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-    aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS :
-                  InputContextAction::MENU_LOST_PSEUDO_FOCUS);
-  OnChangeFocusInternal(sPresContext, sContent, action);
-}
-
-void
-nsIMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
-                                   nsIContent* aContent,
-                                   nsIDOMMouseEvent* aMouseEvent)
+IMEContentObserver::IMEContentObserver()
 {
-  if (sPresContext != aPresContext || sContent != aContent) {
-    return;
-  }
-
-  nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
-  NS_ENSURE_TRUE_VOID(widget);
-
-  bool isTrusted;
-  nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (!isTrusted) {
-    return; // ignore untrusted event.
-  }
-
-  int16_t button;
-  rv = aMouseEvent->GetButton(&button);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (button != 0) {
-    return; // not a left click event.
-  }
-
-  int32_t clickCount;
-  rv = aMouseEvent->GetDetail(&clickCount);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (clickCount != 1) {
-    return; // should notify only first click event.
-  }
-
-  InputContextAction action(InputContextAction::CAUSE_MOUSE,
-                            InputContextAction::FOCUS_NOT_CHANGED);
-  IMEState newState = GetNewIMEState(aPresContext, aContent);
-  SetIMEState(newState, aContent, widget, action);
 }
 
 void
-nsIMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
-                                   nsIContent* aContent)
-{
-  if (sPresContext != aPresContext || sContent != aContent) {
-    return;
-  }
-
-  // If the nsTextStateManager instance isn't managing the editor actually,
-  // we need to recreate the instance.
-  if (sTextStateObserver) {
-    if (sTextStateObserver->IsManaging(aPresContext, aContent)) {
-      return;
-    }
-    DestroyTextStateManager();
-  }
-
-  CreateTextStateManager();
-}
-
-void
-nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState,
-                                  nsIContent* aContent)
-{
-  if (!sPresContext) {
-    NS_WARNING("ISM doesn't know which editor has focus");
-    return;
-  }
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (!widget) {
-    NS_WARNING("focused widget is not found");
-    return;
-  }
-
-  // If the nsTextStateManager instance isn't managing the editor's current
-  // editable root content, the editor frame might be reframed.  We should
-  // recreate the instance at that time.
-  bool createTextStateManager =
-    (!sTextStateObserver ||
-     !sTextStateObserver->IsManaging(sPresContext, aContent));
-
-  bool updateIMEState =
-    (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
-
-  if (updateIMEState) {
-    // commit current composition before modifying IME state.
-    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
-  }
-
-  if (createTextStateManager) {
-    DestroyTextStateManager();
-  }
-
-  if (updateIMEState) {
-    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-                              InputContextAction::FOCUS_NOT_CHANGED);
-    SetIMEState(aNewIMEState, aContent, widget, action);
-  }
-
-  if (createTextStateManager) {
-    CreateTextStateManager();
-  }
-}
-
-IMEState
-nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
-                                  nsIContent*    aContent)
-{
-  // On Printing or Print Preview, we don't need IME.
-  if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
-      aPresContext->Type() == nsPresContext::eContext_Print) {
-    return IMEState(IMEState::DISABLED);
-  }
-
-  if (sInstalledMenuKeyboardListener) {
-    return IMEState(IMEState::DISABLED);
-  }
-
-  if (!aContent) {
-    // Even if there are no focused content, the focused document might be
-    // editable, such case is design mode.
-    nsIDocument* doc = aPresContext->Document();
-    if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
-      return IMEState(IMEState::ENABLED);
-    }
-    return IMEState(IMEState::DISABLED);
-  }
-
-  return aContent->GetDesiredIMEState();
-}
-
-// Helper class, used for IME enabled state change notification
-class IMEEnabledStateChangedEvent : public nsRunnable {
-public:
-  IMEEnabledStateChangedEvent(uint32_t aState)
-    : mState(aState)
-  {
-  }
-
-  NS_IMETHOD Run() {
-    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
-    if (observerService) {
-      nsAutoString state;
-      state.AppendInt(mState);
-      observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get());
-    }
-    return NS_OK;
-  }
-
-private:
-  uint32_t mState;
-};
-
-void
-nsIMEStateManager::SetIMEState(const IMEState &aState,
-                               nsIContent* aContent,
-                               nsIWidget* aWidget,
-                               InputContextAction aAction)
-{
-  NS_ENSURE_TRUE_VOID(aWidget);
-
-  InputContext oldContext = aWidget->GetInputContext();
-
-  InputContext context;
-  context.mIMEState = aState;
-
-  if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
-      (aContent->Tag() == nsGkAtoms::input ||
-       aContent->Tag() == nsGkAtoms::textarea)) {
-    if (aContent->Tag() != nsGkAtoms::textarea) {
-      // <input type=number> has an anonymous <input type=text> descendant
-      // that gets focus whenever anyone tries to focus the number control. We
-      // need to check if aContent is one of those anonymous text controls and,
-      // if so, use the number control instead:
-      nsIContent* content = aContent;
-      HTMLInputElement* inputElement =
-        HTMLInputElement::FromContentOrNull(aContent);
-      if (inputElement) {
-        HTMLInputElement* ownerNumberControl =
-          inputElement->GetOwnerNumberControl();
-        if (ownerNumberControl) {
-          content = ownerNumberControl; // an <input type=number>
-        }
-      }
-      content->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
-                       context.mHTMLInputType);
-    } else {
-      context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
-    }
-
-    if (Preferences::GetBool("dom.forms.inputmode", false)) {
-      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
-                        context.mHTMLInputInputmode);
-    }
-
-    aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
-                      context.mActionHint);
-
-    // if we don't have an action hint and  return won't submit the form use "next"
-    if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) {
-      bool willSubmit = false;
-      nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
-      mozilla::dom::Element* formElement = control->GetFormElement();
-      nsCOMPtr<nsIForm> form;
-      if (control) {
-        // is this a form and does it have a default submit element?
-        if ((form = do_QueryInterface(formElement)) && form->GetDefaultSubmitElement()) {
-          willSubmit = true;
-        // is this an html form and does it only have a single text input element?
-        } else if (formElement && formElement->Tag() == nsGkAtoms::form && formElement->IsHTML() &&
-                   !static_cast<dom::HTMLFormElement*>(formElement)->ImplicitSubmissionIsDisabled()) {
-          willSubmit = true;
-        }
-      }
-      context.mActionHint.Assign(willSubmit ? control->GetType() == NS_FORM_INPUT_SEARCH
-                                                ? NS_LITERAL_STRING("search")
-                                                : NS_LITERAL_STRING("go")
-                                            : formElement
-                                                ? NS_LITERAL_STRING("next")
-                                                : EmptyString());
-    }
-  }
-
-  // XXX I think that we should use nsContentUtils::IsCallerChrome() instead
-  //     of the process type.
-  if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
-      XRE_GetProcessType() != GeckoProcessType_Content) {
-    aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
-  }
-
-  aWidget->SetInputContext(context, aAction);
-  if (oldContext.mIMEState.mEnabled != context.mIMEState.mEnabled) {
-    nsContentUtils::AddScriptRunner(
-      new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled));
-  }
-}
-
-void
-nsIMEStateManager::EnsureTextCompositionArray()
-{
-  if (sTextCompositions) {
-    return;
-  }
-  sTextCompositions = new TextCompositionArray();
-}
-
-void
-nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
-                                            nsPresContext* aPresContext,
-                                            WidgetEvent* aEvent,
-                                            nsEventStatus* aStatus,
-                                            nsDispatchingCallback* aCallBack)
-{
-  MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
-             aEvent->eventStructType == NS_TEXT_EVENT);
-  if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) {
-    return;
-  }
-
-  EnsureTextCompositionArray();
-
-  WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent();
-
-  nsRefPtr<TextComposition> composition =
-    sTextCompositions->GetCompositionFor(GUIEvent->widget);
-  if (!composition) {
-    MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
-    composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent);
-    sTextCompositions->AppendElement(composition);
-  }
-#ifdef DEBUG
-  else {
-    MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
-  }
-#endif // #ifdef DEBUG
-
-  // Dispatch the event on composing target.
-  composition->DispatchEvent(GUIEvent, aStatus, aCallBack);
-
-  // WARNING: the |composition| might have been destroyed already.
-
-  // Remove the ended composition from the array.
-  if (aEvent->message == NS_COMPOSITION_END) {
-    TextCompositionArray::index_type i =
-      sTextCompositions->IndexOf(GUIEvent->widget);
-    if (i != TextCompositionArray::NoIndex) {
-      sTextCompositions->ElementAt(i)->Destroy();
-      sTextCompositions->RemoveElementAt(i);
-    }
-  }
-}
-
-// static
-nsresult
-nsIMEStateManager::NotifyIME(IMEMessage aMessage,
-                             nsIWidget* aWidget)
-{
-  NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
-
-  nsRefPtr<TextComposition> composition;
-  if (sTextCompositions) {
-    composition = sTextCompositions->GetCompositionFor(aWidget);
-  }
-  if (!composition || !composition->IsSynthesizedForTests()) {
-    switch (aMessage) {
-      case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
-        return aWidget->NotifyIME(IMENotification(aMessage));
-      case REQUEST_TO_COMMIT_COMPOSITION:
-      case REQUEST_TO_CANCEL_COMPOSITION:
-      case NOTIFY_IME_OF_COMPOSITION_UPDATE:
-        return composition ?
-          aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK;
-      default:
-        MOZ_CRASH("Unsupported notification");
-    }
-    MOZ_CRASH(
-      "Failed to handle the notification for non-synthesized composition");
-  }
-
-  // If the composition is synthesized events for automated tests, we should
-  // dispatch composition events for emulating the native composition behavior.
-  // NOTE: The dispatched events are discarded if it's not safe to run script.
-  switch (aMessage) {
-    case REQUEST_TO_COMMIT_COMPOSITION: {
-      nsCOMPtr<nsIWidget> widget(aWidget);
-      nsEventStatus status = nsEventStatus_eIgnore;
-      if (!composition->LastData().IsEmpty()) {
-        WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = composition->LastData();
-        textEvent.mFlags.mIsSynthesizedForTests = true;
-        widget->DispatchEvent(&textEvent, status);
-        if (widget->Destroyed()) {
-          return NS_OK;
-        }
-      }
-
-      status = nsEventStatus_eIgnore;
-      WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-      endEvent.data = composition->LastData();
-      endEvent.mFlags.mIsSynthesizedForTests = true;
-      widget->DispatchEvent(&endEvent, status);
-
-      return NS_OK;
-    }
-    case REQUEST_TO_CANCEL_COMPOSITION: {
-      nsCOMPtr<nsIWidget> widget(aWidget);
-      nsEventStatus status = nsEventStatus_eIgnore;
-      if (!composition->LastData().IsEmpty()) {
-        WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
-        updateEvent.data = composition->LastData();
-        updateEvent.mFlags.mIsSynthesizedForTests = true;
-        widget->DispatchEvent(&updateEvent, status);
-        if (widget->Destroyed()) {
-          return NS_OK;
-        }
-
-        status = nsEventStatus_eIgnore;
-        WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = composition->LastData();
-        textEvent.mFlags.mIsSynthesizedForTests = true;
-        widget->DispatchEvent(&textEvent, status);
-        if (widget->Destroyed()) {
-          return NS_OK;
-        }
-      }
-
-      status = nsEventStatus_eIgnore;
-      WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-      endEvent.data = composition->LastData();
-      endEvent.mFlags.mIsSynthesizedForTests = true;
-      widget->DispatchEvent(&endEvent, status);
-
-      return NS_OK;
-    }
-    default:
-      return NS_OK;
-  }
-}
-
-// static
-nsresult
-nsIMEStateManager::NotifyIME(IMEMessage aMessage,
-                             nsPresContext* aPresContext)
-{
-  NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
-
-  nsIWidget* widget = aPresContext->GetRootWidget();
-  if (!widget) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-  return NotifyIME(aMessage, widget);
-}
-
-void
-nsTextStateManager::Init(nsIWidget* aWidget,
+IMEContentObserver::Init(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
                          nsIContent* aContent)
 {
   mWidget = aWidget;
   mEditableNode =
     nsIMEStateManager::GetRootEditableNode(aPresContext, aContent);
   if (!mEditableNode) {
     return;
@@ -744,38 +89,38 @@ nsTextStateManager::Init(nsIWidget* aWid
   }
   if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // The document node is editable, but there are no contents, this document
     // is not editable.
     return;
   }
   NS_ENSURE_TRUE_VOID(mRootContent);
 
-  if (nsIMEStateManager::sIsTestingIME) {
+  if (nsIMEStateManager::IsTestingIME()) {
     nsIDocument* doc = aPresContext->Document();
     (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
                          false, false))->RunDOMEventWhenSafe();
   }
 
   aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
 
-  // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager
+  // NOTIFY_IME_OF_FOCUS might cause recreating IMEContentObserver
   // instance via nsIMEStateManager::UpdateIMEState().  So, this
   // instance might already have been destroyed, check it.
   if (!mRootContent) {
     return;
   }
 
   mDocShell = aPresContext->GetDocShell();
 
   ObserveEditableNode();
 }
 
 void
-nsTextStateManager::ObserveEditableNode()
+IMEContentObserver::ObserveEditableNode()
 {
   MOZ_ASSERT(mSel);
   MOZ_ASSERT(mRootContent);
 
   mUpdatePreference = mWidget->GetIMEUpdatePreference();
   if (mUpdatePreference.WantSelectionChange()) {
     // add selection change listener
     nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
@@ -793,22 +138,22 @@ nsTextStateManager::ObserveEditableNode(
     // Add scroll position listener and reflow observer to detect position and
     // size changes
     mDocShell->AddWeakScrollObserver(this);
     mDocShell->AddWeakReflowObserver(this);
   }
 }
 
 void
-nsTextStateManager::Destroy(void)
+IMEContentObserver::Destroy(void)
 {
   // If CreateTextStateManager failed, mRootContent will be null,
   // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
   if (mRootContent) {
-    if (nsIMEStateManager::sIsTestingIME && mEditableNode) {
+    if (nsIMEStateManager::IsTestingIME() && mEditableNode) {
       nsIDocument* doc = mEditableNode->OwnerDoc();
       (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
                            false, false))->RunDOMEventWhenSafe();
     }
     mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
   }
   // Even if there are some pending notification, it'll never notify the widget.
   mWidget = nullptr;
@@ -827,78 +172,73 @@ nsTextStateManager::Destroy(void)
   }
   mRootContent = nullptr;
   mEditableNode = nullptr;
   mDocShell = nullptr;
   mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
 }
 
 bool
-nsTextStateManager::IsManaging(nsPresContext* aPresContext,
+IMEContentObserver::IsManaging(nsPresContext* aPresContext,
                                nsIContent* aContent)
 {
   if (!mSel || !mRootContent || !mEditableNode) {
     return false; // failed to initialize.
   }
   if (!mRootContent->IsInDoc()) {
     return false; // the focused editor has already been reframed.
   }
   return mEditableNode == nsIMEStateManager::GetRootEditableNode(aPresContext,
                                                                  aContent);
 }
 
 bool
-nsTextStateManager::IsEditorHandlingEventForComposition() const
+IMEContentObserver::IsEditorHandlingEventForComposition() const
 {
   if (!mWidget) {
     return false;
   }
   nsRefPtr<TextComposition> composition =
     nsIMEStateManager::GetTextCompositionFor(mWidget);
   if (!composition) {
     return false;
   }
   return composition->IsEditorHandlingEvent();
 }
 
-NS_IMPL_ISUPPORTS5(nsTextStateManager,
-                   nsIMutationObserver,
-                   nsISelectionListener,
-                   nsIReflowObserver,
-                   nsIScrollObserver,
-                   nsISupportsWeakReference)
-
 // Helper class, used for selection change notification
-class SelectionChangeEvent : public nsRunnable {
+class SelectionChangeEvent : public nsRunnable
+{
 public:
-  SelectionChangeEvent(nsTextStateManager *aDispatcher,
+  SelectionChangeEvent(IMEContentObserver* aDispatcher,
                        bool aCausedByComposition)
     : mDispatcher(aDispatcher)
     , mCausedByComposition(aCausedByComposition)
   {
     MOZ_ASSERT(mDispatcher);
   }
 
-  NS_IMETHOD Run() {
+  NS_IMETHOD Run()
+  {
     if (mDispatcher->mWidget) {
       IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
       notification.mSelectionChangeData.mCausedByComposition =
          mCausedByComposition;
       mDispatcher->mWidget->NotifyIME(notification);
     }
     return NS_OK;
   }
 
 private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
+  nsRefPtr<IMEContentObserver> mDispatcher;
   bool mCausedByComposition;
 };
 
 nsresult
-nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc,
+IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDoc,
                                            nsISelection* aSel,
                                            int16_t aReason)
 {
   bool causedByComposition = IsEditorHandlingEventForComposition();
   if (causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return NS_OK;
   }
@@ -908,99 +248,104 @@ nsTextStateManager::NotifySelectionChang
   NS_ENSURE_SUCCESS(rv, rv);
   if (count > 0 && mWidget) {
     nsContentUtils::AddScriptRunner(
       new SelectionChangeEvent(this, causedByComposition));
   }
   return NS_OK;
 }
 
+// Helper class, used for position change notification
+class PositionChangeEvent MOZ_FINAL : public nsRunnable
+{
+public:
+  PositionChangeEvent(IMEContentObserver* aDispatcher)
+    : mDispatcher(aDispatcher)
+  {
+    MOZ_ASSERT(mDispatcher);
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (mDispatcher->mWidget) {
+      mDispatcher->mWidget->NotifyIME(
+        IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
+    }
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<IMEContentObserver> mDispatcher;
+};
+
+void
+IMEContentObserver::ScrollPositionChanged()
+{
+  if (mWidget) {
+    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
+  }
+}
+
+NS_IMETHODIMP
+IMEContentObserver::Reflow(DOMHighResTimeStamp aStart,
+                           DOMHighResTimeStamp aEnd)
+{
+  if (mWidget) {
+    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart,
+                                        DOMHighResTimeStamp aEnd)
+{
+  if (mWidget) {
+    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
+  }
+  return NS_OK;
+}
+
 // Helper class, used for text change notification
-class TextChangeEvent : public nsRunnable {
+class TextChangeEvent : public nsRunnable
+{
 public:
-  TextChangeEvent(nsTextStateManager* aDispatcher,
+  TextChangeEvent(IMEContentObserver* aDispatcher,
                   uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd,
                   bool aCausedByComposition)
     : mDispatcher(aDispatcher)
     , mStart(aStart)
     , mOldEnd(aOldEnd)
     , mNewEnd(aNewEnd)
     , mCausedByComposition(aCausedByComposition)
   {
     MOZ_ASSERT(mDispatcher);
   }
 
-  NS_IMETHOD Run() {
+  NS_IMETHOD Run()
+  {
     if (mDispatcher->mWidget) {
       IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
       notification.mTextChangeData.mStartOffset = mStart;
       notification.mTextChangeData.mOldEndOffset = mOldEnd;
       notification.mTextChangeData.mNewEndOffset = mNewEnd;
       notification.mTextChangeData.mCausedByComposition = mCausedByComposition;
       mDispatcher->mWidget->NotifyIME(notification);
     }
     return NS_OK;
   }
 
 private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
+  nsRefPtr<IMEContentObserver> mDispatcher;
   uint32_t mStart, mOldEnd, mNewEnd;
   bool mCausedByComposition;
 };
 
-class PositionChangeEvent MOZ_FINAL : public nsRunnable
-{
-public:
-  PositionChangeEvent(nsTextStateManager* aDispatcher)
-    : mDispatcher(aDispatcher) {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      mDispatcher->mWidget->NotifyIME(
-        IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-};
-
 void
-nsTextStateManager::ScrollPositionChanged()
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-}
-
-NS_IMETHODIMP
-nsTextStateManager::Reflow(DOMHighResTimeStamp aStart,
-                           DOMHighResTimeStamp aEnd)
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart,
-                                        DOMHighResTimeStamp aEnd)
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-  return NS_OK;
-}
-
-void
-nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument,
+IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
                                          nsIContent* aContent,
                                          CharacterDataChangeInfo* aInfo)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "character data changed for non-text node");
 
   bool causedByComposition = IsEditorHandlingEventForComposition();
   if (causedByComposition &&
@@ -1017,17 +362,17 @@ nsTextStateManager::CharacterDataChanged
   uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
   uint32_t newEnd = offset + aInfo->mReplaceLength;
 
   nsContentUtils::AddScriptRunner(
     new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition));
 }
 
 void
-nsTextStateManager::NotifyContentAdded(nsINode* aContainer,
+IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
                                        int32_t aStartIndex,
                                        int32_t aEndIndex)
 {
   bool causedByComposition = IsEditorHandlingEventForComposition();
   if (causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return;
   }
@@ -1047,37 +392,37 @@ nsTextStateManager::NotifyContentAdded(n
   if (newOffset) {
     nsContentUtils::AddScriptRunner(
       new TextChangeEvent(this, offset, offset, offset + newOffset,
                           causedByComposition));
   }
 }
 
 void
-nsTextStateManager::ContentAppended(nsIDocument* aDocument,
+IMEContentObserver::ContentAppended(nsIDocument* aDocument,
                                     nsIContent* aContainer,
                                     nsIContent* aFirstNewContent,
                                     int32_t aNewIndexInContainer)
 {
   NotifyContentAdded(aContainer, aNewIndexInContainer,
                      aContainer->GetChildCount());
 }
 
 void
-nsTextStateManager::ContentInserted(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aChild,
-                                     int32_t aIndexInContainer)
+IMEContentObserver::ContentInserted(nsIDocument* aDocument,
+                                    nsIContent* aContainer,
+                                    nsIContent* aChild,
+                                    int32_t aIndexInContainer)
 {
   NotifyContentAdded(NODE_FROM(aContainer, aDocument),
                      aIndexInContainer, aIndexInContainer + 1);
 }
 
 void
-nsTextStateManager::ContentRemoved(nsIDocument* aDocument,
+IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
                                    nsIContent* aContainer,
                                    nsIContent* aChild,
                                    int32_t aIndexInContainer,
                                    nsIContent* aPreviousSibling)
 {
   bool causedByComposition = IsEditorHandlingEventForComposition();
   if (causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
@@ -1104,39 +449,40 @@ nsTextStateManager::ContentRemoved(nsIDo
   if (childOffset) {
     nsContentUtils::AddScriptRunner(
       new TextChangeEvent(this, offset, offset + childOffset, offset,
                           causedByComposition));
   }
 }
 
 static nsIContent*
-GetContentBR(mozilla::dom::Element *aElement) {
+GetContentBR(dom::Element* aElement)
+{
   if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
     return nullptr;
   }
   nsIContent *content = static_cast<nsIContent*>(aElement);
   return content->IsHTML(nsGkAtoms::br) ? content : nullptr;
 }
 
 void
-nsTextStateManager::AttributeWillChange(nsIDocument* aDocument,
-                                        mozilla::dom::Element* aElement,
-                                        int32_t      aNameSpaceID,
-                                        nsIAtom*     aAttribute,
-                                        int32_t      aModType)
+IMEContentObserver::AttributeWillChange(nsIDocument* aDocument,
+                                        dom::Element* aElement,
+                                        int32_t aNameSpaceID,
+                                        nsIAtom* aAttribute,
+                                        int32_t aModType)
 {
   nsIContent *content = GetContentBR(aElement);
   mPreAttrChangeLength = content ?
     nsContentEventHandler::GetNativeTextLength(content) : 0;
 }
 
 void
-nsTextStateManager::AttributeChanged(nsIDocument* aDocument,
-                                     mozilla::dom::Element* aElement,
+IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
+                                     dom::Element* aElement,
                                      int32_t aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      int32_t aModType)
 {
   bool causedByComposition = IsEditorHandlingEventForComposition();
   if (causedByComposition &&
       !mUpdatePreference.WantChangesCausedByComposition()) {
     return;
@@ -1154,139 +500,9 @@ nsTextStateManager::AttributeChanged(nsI
         mRootContent, content, 0, &start))) {
       nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start,
         start + mPreAttrChangeLength, start + postAttrChangeLength,
         causedByComposition));
     }
   }
 }
 
-bool
-nsIMEStateManager::IsEditable(nsINode* node)
-{
-  if (node->IsEditable()) {
-    return true;
-  }
-  // |node| might be readwrite (for example, a text control)
-  if (node->IsElement() && node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
-    return true;
-  }
-  return false;
-}
-
-nsINode*
-nsIMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
-                                       nsIContent* aContent)
-{
-  if (aContent) {
-    nsINode* root = nullptr;
-    nsINode* node = aContent;
-    while (node && IsEditable(node)) {
-      root = node;
-      node = node->GetParentNode();
-    }
-    return root;
-  }
-  if (aPresContext) {
-    nsIDocument* document = aPresContext->Document();
-    if (document && document->IsEditable())
-      return document;
-  }
-  return nullptr;
-}
-
-bool
-nsIMEStateManager::IsEditableIMEState(nsIWidget* aWidget)
-{
-  switch (aWidget->GetInputContext().mIMEState.mEnabled) {
-    case widget::IMEState::ENABLED:
-    case widget::IMEState::PASSWORD:
-      return true;
-    case widget::IMEState::PLUGIN:
-    case widget::IMEState::DISABLED:
-      return false;
-    default:
-      MOZ_CRASH("Unknown IME enable state");
-  }
-}
-
-void
-nsIMEStateManager::DestroyTextStateManager()
-{
-  if (!sTextStateObserver) {
-    return;
-  }
-
-  nsRefPtr<nsTextStateManager> tsm;
-  tsm.swap(sTextStateObserver);
-  tsm->Destroy();
-}
-
-void
-nsIMEStateManager::CreateTextStateManager()
-{
-  if (sTextStateObserver) {
-    NS_WARNING("text state observer has been there already");
-    MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent));
-    return;
-  }
-
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (!widget) {
-    return; // Sometimes, there are no widgets.
-  }
-
-  // If it's not text ediable, we don't need to create nsTextStateManager.
-  if (!IsEditableIMEState(widget)) {
-    return;
-  }
-
-  static bool sInitializeIsTestingIME = true;
-  if (sInitializeIsTestingIME) {
-    Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false);
-    sInitializeIsTestingIME = false;
-  }
-
-  sTextStateObserver = new nsTextStateManager();
-  NS_ADDREF(sTextStateObserver);
-
-  // nsTextStateManager::Init() might create another nsTextStateManager
-  // instance.  So, sTextStateObserver would be replaced with new one.
-  // We should hold the current instance here.
-  nsRefPtr<nsTextStateManager> kungFuDeathGrip(sTextStateObserver);
-  sTextStateObserver->Init(widget, sPresContext, sContent);
-}
-
-nsresult
-nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel,
-                                            nsIContent** aRoot)
-{
-  if (!sTextStateObserver || !sTextStateObserver->mEditableNode ||
-      !sTextStateObserver->mSel)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
-               "uninitialized text state observer");
-  NS_ADDREF(*aSel = sTextStateObserver->mSel);
-  NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
-  return NS_OK;
-}
-
-// static
-already_AddRefed<TextComposition>
-nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
-{
-  if (!sTextCompositions) {
-    return nullptr;
-  }
-  nsRefPtr<TextComposition> textComposition =
-    sTextCompositions->GetCompositionFor(aWidget);
-  return textComposition.forget();
-}
-
-// static
-already_AddRefed<TextComposition>
-nsIMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent)
-{
-  MOZ_ASSERT(aEvent->AsCompositionEvent() || aEvent->AsTextEvent(),
-             "aEvent has to be WidgetCompositionEvent or WidgetTextEvent");
-  return GetTextCompositionFor(aEvent->widget);
-}
+} // namespace mozilla
copy from dom/events/nsIMEStateManager.cpp
copy to dom/events/IMEContentObserver.h
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/IMEContentObserver.h
@@ -1,69 +1,44 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIMEStateManager.h"
+#ifndef mozilla_IMEContentObserver_h_
+#define mozilla_IMEContentObserver_h_
 
-#include "HTMLInputElement.h"
+#include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
-#include "nsIPresShell.h"
-#include "nsISupports.h"
-#include "nsIContent.h"
-#include "nsIDocument.h"
-#include "nsPresContext.h"
-#include "nsIDOMMouseEvent.h"
-#include "nsContentUtils.h"
-#include "nsINode.h"
-#include "nsIFrame.h"
-#include "nsRange.h"
-#include "nsIDOMRange.h"
-#include "nsISelection.h"
-#include "nsISelectionPrivate.h"
+#include "nsIDocShell.h" // XXX Why does only this need to be included here?
+#include "nsIReflowObserver.h"
 #include "nsISelectionListener.h"
-#include "nsISelectionController.h"
-#include "nsIMutationObserver.h"
-#include "nsContentEventHandler.h"
-#include "nsIObserverService.h"
-#include "mozilla/Services.h"
-#include "nsIFormControl.h"
-#include "nsIForm.h"
-#include "mozilla/dom/HTMLFormElement.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/TextEvents.h"
-#include "TextComposition.h"
-#include "mozilla/Preferences.h"
-#include "nsAsyncDOMEvent.h"
-#include "nsIDocShell.h"
-#include "nsIReflowObserver.h"
 #include "nsIScrollObserver.h"
+#include "nsIWidget.h" // for nsIMEUpdatePreference
+#include "nsStubMutationObserver.h"
 #include "nsWeakReference.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
-using namespace mozilla::widget;
+class nsIContent;
+class nsINode;
+class nsISelection;
+class nsPresContext;
 
-// nsTextStateManager notifies widget of any text and selection changes
-//  in the currently focused editor
-// sTextStateObserver points to the currently active nsTextStateManager
-// sTextStateObserver is null if there is no focused editor
+namespace mozilla {
 
-class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
+// IMEContentObserver notifies widget of any text and selection changes
+// in the currently focused editor
+class IMEContentObserver MOZ_FINAL : public nsISelectionListener,
                                      public nsStubMutationObserver,
                                      public nsIReflowObserver,
                                      public nsIScrollObserver,
                                      public nsSupportsWeakReference
 {
 public:
-  nsTextStateManager()
-  {
-  }
+  IMEContentObserver();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISELECTIONLISTENER
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
@@ -93,1200 +68,11 @@ private:
   void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
   void ObserveEditableNode();
 
   nsCOMPtr<nsIDocShell>          mDocShell;
   nsIMEUpdatePreference mUpdatePreference;
   uint32_t mPreAttrChangeLength;
 };
 
-/******************************************************************/
-/* nsIMEStateManager                                              */
-/******************************************************************/
-
-nsIContent*    nsIMEStateManager::sContent      = nullptr;
-nsPresContext* nsIMEStateManager::sPresContext  = nullptr;
-bool           nsIMEStateManager::sInstalledMenuKeyboardListener = false;
-bool           nsIMEStateManager::sIsTestingIME = false;
-
-nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
-TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr;
-
-void
-nsIMEStateManager::Shutdown()
-{
-  MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
-  delete sTextCompositions;
-  sTextCompositions = nullptr;
-}
-
-nsresult
-nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
-{
-  NS_ENSURE_ARG_POINTER(aPresContext);
-
-  // First, if there is a composition in the aPresContext, clean up it.
-  if (sTextCompositions) {
-    TextCompositionArray::index_type i =
-      sTextCompositions->IndexOf(aPresContext);
-    if (i != TextCompositionArray::NoIndex) {
-      // there should be only one composition per presContext object.
-      sTextCompositions->ElementAt(i)->Destroy();
-      sTextCompositions->RemoveElementAt(i);
-      MOZ_ASSERT(sTextCompositions->IndexOf(aPresContext) ==
-                   TextCompositionArray::NoIndex);
-    }
-  }
-
-  if (aPresContext != sPresContext)
-    return NS_OK;
-
-  DestroyTextStateManager();
-
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (widget) {
-    IMEState newState = GetNewIMEState(sPresContext, nullptr);
-    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-                              InputContextAction::LOST_FOCUS);
-    SetIMEState(newState, nullptr, widget, action);
-  }
-  NS_IF_RELEASE(sContent);
-  sPresContext = nullptr;
-  return NS_OK;
-}
-
-nsresult
-nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
-                                   nsIContent* aContent)
-{
-  NS_ENSURE_ARG_POINTER(aPresContext);
-
-  // First, if there is a composition in the aContent, clean up it.
-  if (sTextCompositions) {
-    nsRefPtr<TextComposition> compositionInContent =
-      sTextCompositions->GetCompositionInContent(aPresContext, aContent);
-
-    if (compositionInContent) {
-      // 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()).
-      nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
-      if (widget) {
-        nsresult rv =
-          compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
-        if (NS_FAILED(rv)) {
-          compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
-        }
-        // By calling the APIs, the composition may have been finished normally.
-        compositionInContent =
-          sTextCompositions->GetCompositionFor(
-                               compositionInContent->GetPresContext(),
-                               compositionInContent->GetEventTargetNode());
-      }
-    }
-
-    // If the compositionInContent is still available, we should finish the
-    // composition just on the content forcibly.
-    if (compositionInContent) {
-      compositionInContent->SynthesizeCommit(true);
-    }
-  }
-
-  if (!sPresContext || !sContent ||
-      !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
-    return NS_OK;
-  }
-
-  DestroyTextStateManager();
-
-  // Current IME transaction should commit
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (widget) {
-    IMEState newState = GetNewIMEState(sPresContext, nullptr);
-    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-                              InputContextAction::LOST_FOCUS);
-    SetIMEState(newState, nullptr, widget, action);
-  }
-
-  NS_IF_RELEASE(sContent);
-  sPresContext = nullptr;
-
-  return NS_OK;
-}
-
-nsresult
-nsIMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
-                                 nsIContent* aContent,
-                                 InputContextAction::Cause aCause)
-{
-  InputContextAction action(aCause);
-  return OnChangeFocusInternal(aPresContext, aContent, action);
-}
-
-nsresult
-nsIMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
-                                         nsIContent* aContent,
-                                         InputContextAction aAction)
-{
-  bool focusActuallyChanging =
-    (sContent != aContent || sPresContext != aPresContext);
-
-  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);
-    }
-  }
-
-  if (sTextStateObserver &&
-      (aPresContext || !sTextStateObserver->KeepAliveDuringDeactive()) &&
-      !sTextStateObserver->IsManaging(aPresContext, aContent)) {
-    DestroyTextStateManager();
-  }
-
-  if (!aPresContext) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIWidget> widget =
-    (sPresContext == aPresContext) ? oldWidget.get() :
-                                     aPresContext->GetRootWidget();
-  if (!widget) {
-    return NS_OK;
-  }
-
-  IMEState newState = GetNewIMEState(aPresContext, aContent);
-  if (!focusActuallyChanging) {
-    // actual focus isn't changing, but if IME enabled state is changing,
-    // we should do it.
-    InputContext context = widget->GetInputContext();
-    if (context.mIMEState.mEnabled == newState.mEnabled) {
-      // the enabled state isn't 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);
-    }
-  } 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;
-  }
-
-  // Update IME state for new focus widget
-  SetIMEState(newState, aContent, widget, aAction);
-
-  sPresContext = aPresContext;
-  if (sContent != aContent) {
-    NS_IF_RELEASE(sContent);
-    NS_IF_ADDREF(sContent = aContent);
-  }
-
-  // Don't call CreateTextStateManager() here, it should be called from
-  // focus event handler of editor.
-
-  return NS_OK;
-}
-
-void
-nsIMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling)
-{
-  sInstalledMenuKeyboardListener = aInstalling;
-
-  InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-    aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS :
-                  InputContextAction::MENU_LOST_PSEUDO_FOCUS);
-  OnChangeFocusInternal(sPresContext, sContent, action);
-}
-
-void
-nsIMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
-                                   nsIContent* aContent,
-                                   nsIDOMMouseEvent* aMouseEvent)
-{
-  if (sPresContext != aPresContext || sContent != aContent) {
-    return;
-  }
-
-  nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
-  NS_ENSURE_TRUE_VOID(widget);
-
-  bool isTrusted;
-  nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (!isTrusted) {
-    return; // ignore untrusted event.
-  }
-
-  int16_t button;
-  rv = aMouseEvent->GetButton(&button);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (button != 0) {
-    return; // not a left click event.
-  }
-
-  int32_t clickCount;
-  rv = aMouseEvent->GetDetail(&clickCount);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  if (clickCount != 1) {
-    return; // should notify only first click event.
-  }
-
-  InputContextAction action(InputContextAction::CAUSE_MOUSE,
-                            InputContextAction::FOCUS_NOT_CHANGED);
-  IMEState newState = GetNewIMEState(aPresContext, aContent);
-  SetIMEState(newState, aContent, widget, action);
-}
-
-void
-nsIMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
-                                   nsIContent* aContent)
-{
-  if (sPresContext != aPresContext || sContent != aContent) {
-    return;
-  }
-
-  // If the nsTextStateManager instance isn't managing the editor actually,
-  // we need to recreate the instance.
-  if (sTextStateObserver) {
-    if (sTextStateObserver->IsManaging(aPresContext, aContent)) {
-      return;
-    }
-    DestroyTextStateManager();
-  }
-
-  CreateTextStateManager();
-}
-
-void
-nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState,
-                                  nsIContent* aContent)
-{
-  if (!sPresContext) {
-    NS_WARNING("ISM doesn't know which editor has focus");
-    return;
-  }
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (!widget) {
-    NS_WARNING("focused widget is not found");
-    return;
-  }
-
-  // If the nsTextStateManager instance isn't managing the editor's current
-  // editable root content, the editor frame might be reframed.  We should
-  // recreate the instance at that time.
-  bool createTextStateManager =
-    (!sTextStateObserver ||
-     !sTextStateObserver->IsManaging(sPresContext, aContent));
-
-  bool updateIMEState =
-    (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
-
-  if (updateIMEState) {
-    // commit current composition before modifying IME state.
-    NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
-  }
-
-  if (createTextStateManager) {
-    DestroyTextStateManager();
-  }
-
-  if (updateIMEState) {
-    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
-                              InputContextAction::FOCUS_NOT_CHANGED);
-    SetIMEState(aNewIMEState, aContent, widget, action);
-  }
-
-  if (createTextStateManager) {
-    CreateTextStateManager();
-  }
-}
-
-IMEState
-nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
-                                  nsIContent*    aContent)
-{
-  // On Printing or Print Preview, we don't need IME.
-  if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
-      aPresContext->Type() == nsPresContext::eContext_Print) {
-    return IMEState(IMEState::DISABLED);
-  }
-
-  if (sInstalledMenuKeyboardListener) {
-    return IMEState(IMEState::DISABLED);
-  }
-
-  if (!aContent) {
-    // Even if there are no focused content, the focused document might be
-    // editable, such case is design mode.
-    nsIDocument* doc = aPresContext->Document();
-    if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
-      return IMEState(IMEState::ENABLED);
-    }
-    return IMEState(IMEState::DISABLED);
-  }
-
-  return aContent->GetDesiredIMEState();
-}
-
-// Helper class, used for IME enabled state change notification
-class IMEEnabledStateChangedEvent : public nsRunnable {
-public:
-  IMEEnabledStateChangedEvent(uint32_t aState)
-    : mState(aState)
-  {
-  }
-
-  NS_IMETHOD Run() {
-    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
-    if (observerService) {
-      nsAutoString state;
-      state.AppendInt(mState);
-      observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get());
-    }
-    return NS_OK;
-  }
-
-private:
-  uint32_t mState;
-};
-
-void
-nsIMEStateManager::SetIMEState(const IMEState &aState,
-                               nsIContent* aContent,
-                               nsIWidget* aWidget,
-                               InputContextAction aAction)
-{
-  NS_ENSURE_TRUE_VOID(aWidget);
-
-  InputContext oldContext = aWidget->GetInputContext();
-
-  InputContext context;
-  context.mIMEState = aState;
-
-  if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
-      (aContent->Tag() == nsGkAtoms::input ||
-       aContent->Tag() == nsGkAtoms::textarea)) {
-    if (aContent->Tag() != nsGkAtoms::textarea) {
-      // <input type=number> has an anonymous <input type=text> descendant
-      // that gets focus whenever anyone tries to focus the number control. We
-      // need to check if aContent is one of those anonymous text controls and,
-      // if so, use the number control instead:
-      nsIContent* content = aContent;
-      HTMLInputElement* inputElement =
-        HTMLInputElement::FromContentOrNull(aContent);
-      if (inputElement) {
-        HTMLInputElement* ownerNumberControl =
-          inputElement->GetOwnerNumberControl();
-        if (ownerNumberControl) {
-          content = ownerNumberControl; // an <input type=number>
-        }
-      }
-      content->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
-                       context.mHTMLInputType);
-    } else {
-      context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
-    }
-
-    if (Preferences::GetBool("dom.forms.inputmode", false)) {
-      aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
-                        context.mHTMLInputInputmode);
-    }
-
-    aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
-                      context.mActionHint);
-
-    // if we don't have an action hint and  return won't submit the form use "next"
-    if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) {
-      bool willSubmit = false;
-      nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
-      mozilla::dom::Element* formElement = control->GetFormElement();
-      nsCOMPtr<nsIForm> form;
-      if (control) {
-        // is this a form and does it have a default submit element?
-        if ((form = do_QueryInterface(formElement)) && form->GetDefaultSubmitElement()) {
-          willSubmit = true;
-        // is this an html form and does it only have a single text input element?
-        } else if (formElement && formElement->Tag() == nsGkAtoms::form && formElement->IsHTML() &&
-                   !static_cast<dom::HTMLFormElement*>(formElement)->ImplicitSubmissionIsDisabled()) {
-          willSubmit = true;
-        }
-      }
-      context.mActionHint.Assign(willSubmit ? control->GetType() == NS_FORM_INPUT_SEARCH
-                                                ? NS_LITERAL_STRING("search")
-                                                : NS_LITERAL_STRING("go")
-                                            : formElement
-                                                ? NS_LITERAL_STRING("next")
-                                                : EmptyString());
-    }
-  }
-
-  // XXX I think that we should use nsContentUtils::IsCallerChrome() instead
-  //     of the process type.
-  if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
-      XRE_GetProcessType() != GeckoProcessType_Content) {
-    aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
-  }
-
-  aWidget->SetInputContext(context, aAction);
-  if (oldContext.mIMEState.mEnabled != context.mIMEState.mEnabled) {
-    nsContentUtils::AddScriptRunner(
-      new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled));
-  }
-}
-
-void
-nsIMEStateManager::EnsureTextCompositionArray()
-{
-  if (sTextCompositions) {
-    return;
-  }
-  sTextCompositions = new TextCompositionArray();
-}
-
-void
-nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
-                                            nsPresContext* aPresContext,
-                                            WidgetEvent* aEvent,
-                                            nsEventStatus* aStatus,
-                                            nsDispatchingCallback* aCallBack)
-{
-  MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
-             aEvent->eventStructType == NS_TEXT_EVENT);
-  if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) {
-    return;
-  }
-
-  EnsureTextCompositionArray();
-
-  WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent();
-
-  nsRefPtr<TextComposition> composition =
-    sTextCompositions->GetCompositionFor(GUIEvent->widget);
-  if (!composition) {
-    MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
-    composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent);
-    sTextCompositions->AppendElement(composition);
-  }
-#ifdef DEBUG
-  else {
-    MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
-  }
-#endif // #ifdef DEBUG
-
-  // Dispatch the event on composing target.
-  composition->DispatchEvent(GUIEvent, aStatus, aCallBack);
-
-  // WARNING: the |composition| might have been destroyed already.
-
-  // Remove the ended composition from the array.
-  if (aEvent->message == NS_COMPOSITION_END) {
-    TextCompositionArray::index_type i =
-      sTextCompositions->IndexOf(GUIEvent->widget);
-    if (i != TextCompositionArray::NoIndex) {
-      sTextCompositions->ElementAt(i)->Destroy();
-      sTextCompositions->RemoveElementAt(i);
-    }
-  }
-}
-
-// static
-nsresult
-nsIMEStateManager::NotifyIME(IMEMessage aMessage,
-                             nsIWidget* aWidget)
-{
-  NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
-
-  nsRefPtr<TextComposition> composition;
-  if (sTextCompositions) {
-    composition = sTextCompositions->GetCompositionFor(aWidget);
-  }
-  if (!composition || !composition->IsSynthesizedForTests()) {
-    switch (aMessage) {
-      case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
-        return aWidget->NotifyIME(IMENotification(aMessage));
-      case REQUEST_TO_COMMIT_COMPOSITION:
-      case REQUEST_TO_CANCEL_COMPOSITION:
-      case NOTIFY_IME_OF_COMPOSITION_UPDATE:
-        return composition ?
-          aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK;
-      default:
-        MOZ_CRASH("Unsupported notification");
-    }
-    MOZ_CRASH(
-      "Failed to handle the notification for non-synthesized composition");
-  }
-
-  // If the composition is synthesized events for automated tests, we should
-  // dispatch composition events for emulating the native composition behavior.
-  // NOTE: The dispatched events are discarded if it's not safe to run script.
-  switch (aMessage) {
-    case REQUEST_TO_COMMIT_COMPOSITION: {
-      nsCOMPtr<nsIWidget> widget(aWidget);
-      nsEventStatus status = nsEventStatus_eIgnore;
-      if (!composition->LastData().IsEmpty()) {
-        WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = composition->LastData();
-        textEvent.mFlags.mIsSynthesizedForTests = true;
-        widget->DispatchEvent(&textEvent, status);
-        if (widget->Destroyed()) {
-          return NS_OK;
-        }
-      }
-
-      status = nsEventStatus_eIgnore;
-      WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-      endEvent.data = composition->LastData();
-      endEvent.mFlags.mIsSynthesizedForTests = true;
-      widget->DispatchEvent(&endEvent, status);
-
-      return NS_OK;
-    }
-    case REQUEST_TO_CANCEL_COMPOSITION: {
-      nsCOMPtr<nsIWidget> widget(aWidget);
-      nsEventStatus status = nsEventStatus_eIgnore;
-      if (!composition->LastData().IsEmpty()) {
-        WidgetCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
-        updateEvent.data = composition->LastData();
-        updateEvent.mFlags.mIsSynthesizedForTests = true;
-        widget->DispatchEvent(&updateEvent, status);
-        if (widget->Destroyed()) {
-          return NS_OK;
-        }
-
-        status = nsEventStatus_eIgnore;
-        WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget);
-        textEvent.theText = composition->LastData();
-        textEvent.mFlags.mIsSynthesizedForTests = true;
-        widget->DispatchEvent(&textEvent, status);
-        if (widget->Destroyed()) {
-          return NS_OK;
-        }
-      }
-
-      status = nsEventStatus_eIgnore;
-      WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
-      endEvent.data = composition->LastData();
-      endEvent.mFlags.mIsSynthesizedForTests = true;
-      widget->DispatchEvent(&endEvent, status);
-
-      return NS_OK;
-    }
-    default:
-      return NS_OK;
-  }
-}
-
-// static
-nsresult
-nsIMEStateManager::NotifyIME(IMEMessage aMessage,
-                             nsPresContext* aPresContext)
-{
-  NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
+} // namespace mozilla
 
-  nsIWidget* widget = aPresContext->GetRootWidget();
-  if (!widget) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-  return NotifyIME(aMessage, widget);
-}
-
-void
-nsTextStateManager::Init(nsIWidget* aWidget,
-                         nsPresContext* aPresContext,
-                         nsIContent* aContent)
-{
-  mWidget = aWidget;
-  mEditableNode =
-    nsIMEStateManager::GetRootEditableNode(aPresContext, aContent);
-  if (!mEditableNode) {
-    return;
-  }
-
-  nsIPresShell* presShell = aPresContext->PresShell();
-
-  // get selection and root content
-  nsCOMPtr<nsISelectionController> selCon;
-  if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
-    nsIFrame* frame =
-      static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
-    NS_ENSURE_TRUE_VOID(frame);
-
-    frame->GetSelectionController(aPresContext,
-                                  getter_AddRefs(selCon));
-  } else {
-    // mEditableNode is a document
-    selCon = do_QueryInterface(presShell);
-  }
-  NS_ENSURE_TRUE_VOID(selCon);
-
-  selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
-                       getter_AddRefs(mSel));
-  NS_ENSURE_TRUE_VOID(mSel);
-
-  nsCOMPtr<nsIDOMRange> selDomRange;
-  if (NS_SUCCEEDED(mSel->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
-    nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
-    NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent());
-
-    mRootContent = selRange->GetStartParent()->
-                     GetSelectionRootContent(presShell);
-  } else {
-    mRootContent = mEditableNode->GetSelectionRootContent(presShell);
-  }
-  if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
-    // The document node is editable, but there are no contents, this document
-    // is not editable.
-    return;
-  }
-  NS_ENSURE_TRUE_VOID(mRootContent);
-
-  if (nsIMEStateManager::sIsTestingIME) {
-    nsIDocument* doc = aPresContext->Document();
-    (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
-                         false, false))->RunDOMEventWhenSafe();
-  }
-
-  aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
-
-  // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager
-  // instance via nsIMEStateManager::UpdateIMEState().  So, this
-  // instance might already have been destroyed, check it.
-  if (!mRootContent) {
-    return;
-  }
-
-  mDocShell = aPresContext->GetDocShell();
-
-  ObserveEditableNode();
-}
-
-void
-nsTextStateManager::ObserveEditableNode()
-{
-  MOZ_ASSERT(mSel);
-  MOZ_ASSERT(mRootContent);
-
-  mUpdatePreference = mWidget->GetIMEUpdatePreference();
-  if (mUpdatePreference.WantSelectionChange()) {
-    // add selection change listener
-    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
-    NS_ENSURE_TRUE_VOID(selPrivate);
-    nsresult rv = selPrivate->AddSelectionListener(this);
-    NS_ENSURE_SUCCESS_VOID(rv);
-  }
-
-  if (mUpdatePreference.WantTextChange()) {
-    // add text change observer
-    mRootContent->AddMutationObserver(this);
-  }
-
-  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
-    // Add scroll position listener and reflow observer to detect position and
-    // size changes
-    mDocShell->AddWeakScrollObserver(this);
-    mDocShell->AddWeakReflowObserver(this);
-  }
-}
-
-void
-nsTextStateManager::Destroy(void)
-{
-  // If CreateTextStateManager failed, mRootContent will be null,
-  // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
-  if (mRootContent) {
-    if (nsIMEStateManager::sIsTestingIME && mEditableNode) {
-      nsIDocument* doc = mEditableNode->OwnerDoc();
-      (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
-                           false, false))->RunDOMEventWhenSafe();
-    }
-    mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
-  }
-  // Even if there are some pending notification, it'll never notify the widget.
-  mWidget = nullptr;
-  if (mUpdatePreference.WantSelectionChange() && mSel) {
-    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
-    if (selPrivate)
-      selPrivate->RemoveSelectionListener(this);
-  }
-  mSel = nullptr;
-  if (mUpdatePreference.WantTextChange() && mRootContent) {
-    mRootContent->RemoveMutationObserver(this);
-  }
-  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
-    mDocShell->RemoveWeakScrollObserver(this);
-    mDocShell->RemoveWeakReflowObserver(this);
-  }
-  mRootContent = nullptr;
-  mEditableNode = nullptr;
-  mDocShell = nullptr;
-  mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
-}
-
-bool
-nsTextStateManager::IsManaging(nsPresContext* aPresContext,
-                               nsIContent* aContent)
-{
-  if (!mSel || !mRootContent || !mEditableNode) {
-    return false; // failed to initialize.
-  }
-  if (!mRootContent->IsInDoc()) {
-    return false; // the focused editor has already been reframed.
-  }
-  return mEditableNode == nsIMEStateManager::GetRootEditableNode(aPresContext,
-                                                                 aContent);
-}
-
-bool
-nsTextStateManager::IsEditorHandlingEventForComposition() const
-{
-  if (!mWidget) {
-    return false;
-  }
-  nsRefPtr<TextComposition> composition =
-    nsIMEStateManager::GetTextCompositionFor(mWidget);
-  if (!composition) {
-    return false;
-  }
-  return composition->IsEditorHandlingEvent();
-}
-
-NS_IMPL_ISUPPORTS5(nsTextStateManager,
-                   nsIMutationObserver,
-                   nsISelectionListener,
-                   nsIReflowObserver,
-                   nsIScrollObserver,
-                   nsISupportsWeakReference)
-
-// Helper class, used for selection change notification
-class SelectionChangeEvent : public nsRunnable {
-public:
-  SelectionChangeEvent(nsTextStateManager *aDispatcher,
-                       bool aCausedByComposition)
-    : mDispatcher(aDispatcher)
-    , mCausedByComposition(aCausedByComposition)
-  {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-      notification.mSelectionChangeData.mCausedByComposition =
-         mCausedByComposition;
-      mDispatcher->mWidget->NotifyIME(notification);
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-  bool mCausedByComposition;
-};
-
-nsresult
-nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc,
-                                           nsISelection* aSel,
-                                           int16_t aReason)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return NS_OK;
-  }
-
-  int32_t count = 0;
-  nsresult rv = aSel->GetRangeCount(&count);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (count > 0 && mWidget) {
-    nsContentUtils::AddScriptRunner(
-      new SelectionChangeEvent(this, causedByComposition));
-  }
-  return NS_OK;
-}
-
-// Helper class, used for text change notification
-class TextChangeEvent : public nsRunnable {
-public:
-  TextChangeEvent(nsTextStateManager* aDispatcher,
-                  uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd,
-                  bool aCausedByComposition)
-    : mDispatcher(aDispatcher)
-    , mStart(aStart)
-    , mOldEnd(aOldEnd)
-    , mNewEnd(aNewEnd)
-    , mCausedByComposition(aCausedByComposition)
-  {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
-      notification.mTextChangeData.mStartOffset = mStart;
-      notification.mTextChangeData.mOldEndOffset = mOldEnd;
-      notification.mTextChangeData.mNewEndOffset = mNewEnd;
-      notification.mTextChangeData.mCausedByComposition = mCausedByComposition;
-      mDispatcher->mWidget->NotifyIME(notification);
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-  uint32_t mStart, mOldEnd, mNewEnd;
-  bool mCausedByComposition;
-};
-
-class PositionChangeEvent MOZ_FINAL : public nsRunnable
-{
-public:
-  PositionChangeEvent(nsTextStateManager* aDispatcher)
-    : mDispatcher(aDispatcher) {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      mDispatcher->mWidget->NotifyIME(
-        IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-};
-
-void
-nsTextStateManager::ScrollPositionChanged()
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-}
-
-NS_IMETHODIMP
-nsTextStateManager::Reflow(DOMHighResTimeStamp aStart,
-                           DOMHighResTimeStamp aEnd)
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart,
-                                        DOMHighResTimeStamp aEnd)
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-  return NS_OK;
-}
-
-void
-nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument,
-                                         nsIContent* aContent,
-                                         CharacterDataChangeInfo* aInfo)
-{
-  NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
-               "character data changed for non-text node");
-
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  uint32_t offset = 0;
-  // get offsets of change and fire notification
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    mRootContent, aContent, aInfo->mChangeStart, &offset)))
-    return;
-
-  uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
-  uint32_t newEnd = offset + aInfo->mReplaceLength;
-
-  nsContentUtils::AddScriptRunner(
-    new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition));
-}
-
-void
-nsTextStateManager::NotifyContentAdded(nsINode* aContainer,
-                                       int32_t aStartIndex,
-                                       int32_t aEndIndex)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  uint32_t offset = 0, newOffset = 0;
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    mRootContent, aContainer, aStartIndex, &offset)))
-    return;
-
-  // get offset at the end of the last added node
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    aContainer->GetChildAt(aStartIndex),
-                    aContainer, aEndIndex, &newOffset)))
-    return;
-
-  // fire notification
-  if (newOffset) {
-    nsContentUtils::AddScriptRunner(
-      new TextChangeEvent(this, offset, offset, offset + newOffset,
-                          causedByComposition));
-  }
-}
-
-void
-nsTextStateManager::ContentAppended(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aFirstNewContent,
-                                    int32_t aNewIndexInContainer)
-{
-  NotifyContentAdded(aContainer, aNewIndexInContainer,
-                     aContainer->GetChildCount());
-}
-
-void
-nsTextStateManager::ContentInserted(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aChild,
-                                     int32_t aIndexInContainer)
-{
-  NotifyContentAdded(NODE_FROM(aContainer, aDocument),
-                     aIndexInContainer, aIndexInContainer + 1);
-}
-
-void
-nsTextStateManager::ContentRemoved(nsIDocument* aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aChild,
-                                   int32_t aIndexInContainer,
-                                   nsIContent* aPreviousSibling)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  uint32_t offset = 0, childOffset = 1;
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    mRootContent, NODE_FROM(aContainer, aDocument),
-                    aIndexInContainer, &offset)))
-    return;
-
-  // get offset at the end of the deleted node
-  if (aChild->IsNodeOfType(nsINode::eTEXT))
-    childOffset = aChild->TextLength();
-  else if (0 < aChild->GetChildCount())
-    childOffset = aChild->GetChildCount();
-
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    aChild, aChild, childOffset, &childOffset)))
-    return;
-
-  // fire notification
-  if (childOffset) {
-    nsContentUtils::AddScriptRunner(
-      new TextChangeEvent(this, offset, offset + childOffset, offset,
-                          causedByComposition));
-  }
-}
-
-static nsIContent*
-GetContentBR(mozilla::dom::Element *aElement) {
-  if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
-    return nullptr;
-  }
-  nsIContent *content = static_cast<nsIContent*>(aElement);
-  return content->IsHTML(nsGkAtoms::br) ? content : nullptr;
-}
-
-void
-nsTextStateManager::AttributeWillChange(nsIDocument* aDocument,
-                                        mozilla::dom::Element* aElement,
-                                        int32_t      aNameSpaceID,
-                                        nsIAtom*     aAttribute,
-                                        int32_t      aModType)
-{
-  nsIContent *content = GetContentBR(aElement);
-  mPreAttrChangeLength = content ?
-    nsContentEventHandler::GetNativeTextLength(content) : 0;
-}
-
-void
-nsTextStateManager::AttributeChanged(nsIDocument* aDocument,
-                                     mozilla::dom::Element* aElement,
-                                     int32_t aNameSpaceID,
-                                     nsIAtom* aAttribute,
-                                     int32_t aModType)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  nsIContent *content = GetContentBR(aElement);
-  if (!content) {
-    return;
-  }
-  uint32_t postAttrChangeLength =
-    nsContentEventHandler::GetNativeTextLength(content);
-  if (postAttrChangeLength != mPreAttrChangeLength) {
-    uint32_t start;
-    if (NS_SUCCEEDED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-        mRootContent, content, 0, &start))) {
-      nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start,
-        start + mPreAttrChangeLength, start + postAttrChangeLength,
-        causedByComposition));
-    }
-  }
-}
-
-bool
-nsIMEStateManager::IsEditable(nsINode* node)
-{
-  if (node->IsEditable()) {
-    return true;
-  }
-  // |node| might be readwrite (for example, a text control)
-  if (node->IsElement() && node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
-    return true;
-  }
-  return false;
-}
-
-nsINode*
-nsIMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
-                                       nsIContent* aContent)
-{
-  if (aContent) {
-    nsINode* root = nullptr;
-    nsINode* node = aContent;
-    while (node && IsEditable(node)) {
-      root = node;
-      node = node->GetParentNode();
-    }
-    return root;
-  }
-  if (aPresContext) {
-    nsIDocument* document = aPresContext->Document();
-    if (document && document->IsEditable())
-      return document;
-  }
-  return nullptr;
-}
-
-bool
-nsIMEStateManager::IsEditableIMEState(nsIWidget* aWidget)
-{
-  switch (aWidget->GetInputContext().mIMEState.mEnabled) {
-    case widget::IMEState::ENABLED:
-    case widget::IMEState::PASSWORD:
-      return true;
-    case widget::IMEState::PLUGIN:
-    case widget::IMEState::DISABLED:
-      return false;
-    default:
-      MOZ_CRASH("Unknown IME enable state");
-  }
-}
-
-void
-nsIMEStateManager::DestroyTextStateManager()
-{
-  if (!sTextStateObserver) {
-    return;
-  }
-
-  nsRefPtr<nsTextStateManager> tsm;
-  tsm.swap(sTextStateObserver);
-  tsm->Destroy();
-}
-
-void
-nsIMEStateManager::CreateTextStateManager()
-{
-  if (sTextStateObserver) {
-    NS_WARNING("text state observer has been there already");
-    MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent));
-    return;
-  }
-
-  nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
-  if (!widget) {
-    return; // Sometimes, there are no widgets.
-  }
-
-  // If it's not text ediable, we don't need to create nsTextStateManager.
-  if (!IsEditableIMEState(widget)) {
-    return;
-  }
-
-  static bool sInitializeIsTestingIME = true;
-  if (sInitializeIsTestingIME) {
-    Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false);
-    sInitializeIsTestingIME = false;
-  }
-
-  sTextStateObserver = new nsTextStateManager();
-  NS_ADDREF(sTextStateObserver);
-
-  // nsTextStateManager::Init() might create another nsTextStateManager
-  // instance.  So, sTextStateObserver would be replaced with new one.
-  // We should hold the current instance here.
-  nsRefPtr<nsTextStateManager> kungFuDeathGrip(sTextStateObserver);
-  sTextStateObserver->Init(widget, sPresContext, sContent);
-}
-
-nsresult
-nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel,
-                                            nsIContent** aRoot)
-{
-  if (!sTextStateObserver || !sTextStateObserver->mEditableNode ||
-      !sTextStateObserver->mSel)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
-               "uninitialized text state observer");
-  NS_ADDREF(*aSel = sTextStateObserver->mSel);
-  NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
-  return NS_OK;
-}
-
-// static
-already_AddRefed<TextComposition>
-nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
-{
-  if (!sTextCompositions) {
-    return nullptr;
-  }
-  nsRefPtr<TextComposition> textComposition =
-    sTextCompositions->GetCompositionFor(aWidget);
-  return textComposition.forget();
-}
-
-// static
-already_AddRefed<TextComposition>
-nsIMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent)
-{
-  MOZ_ASSERT(aEvent->AsCompositionEvent() || aEvent->AsTextEvent(),
-             "aEvent has to be WidgetCompositionEvent or WidgetTextEvent");
-  return GetTextCompositionFor(aEvent->widget);
-}
+#endif // mozilla_IMEContentObserver_h_
\ No newline at end of file
--- a/dom/events/moz.build
+++ b/dom/events/moz.build
@@ -72,16 +72,17 @@ UNIFIED_SOURCES += [
     'CompositionEvent.cpp',
     'DataContainerEvent.cpp',
     'DataTransfer.cpp',
     'DeviceMotionEvent.cpp',
     'DragEvent.cpp',
     'Event.cpp',
     'EventTarget.cpp',
     'FocusEvent.cpp',
+    'IMEContentObserver.cpp',
     'KeyboardEvent.cpp',
     'MessageEvent.cpp',
     'MouseEvent.cpp',
     'MouseScrollEvent.cpp',
     'MutationEvent.cpp',
     'NotifyAudioAvailableEvent.cpp',
     'NotifyPaintEvent.cpp',
     'nsAsyncDOMEvent.cpp',
--- a/dom/events/nsIMEStateManager.cpp
+++ b/dom/events/nsIMEStateManager.cpp
@@ -1,118 +1,50 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIMEStateManager.h"
 
+#include "IMEContentObserver.h"
 #include "HTMLInputElement.h"
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 #include "nsISupports.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsContentUtils.h"
 #include "nsINode.h"
-#include "nsIFrame.h"
-#include "nsRange.h"
-#include "nsIDOMRange.h"
 #include "nsISelection.h"
-#include "nsISelectionPrivate.h"
-#include "nsISelectionListener.h"
-#include "nsISelectionController.h"
-#include "nsIMutationObserver.h"
-#include "nsContentEventHandler.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "nsIFormControl.h"
 #include "nsIForm.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TextEvents.h"
 #include "TextComposition.h"
 #include "mozilla/Preferences.h"
-#include "nsAsyncDOMEvent.h"
-#include "nsIDocShell.h"
-#include "nsIReflowObserver.h"
-#include "nsIScrollObserver.h"
-#include "nsWeakReference.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::widget;
 
-// nsTextStateManager notifies widget of any text and selection changes
-//  in the currently focused editor
-// sTextStateObserver points to the currently active nsTextStateManager
-// sTextStateObserver is null if there is no focused editor
-
-class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
-                                     public nsStubMutationObserver,
-                                     public nsIReflowObserver,
-                                     public nsIScrollObserver,
-                                     public nsSupportsWeakReference
-{
-public:
-  nsTextStateManager()
-  {
-  }
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSISELECTIONLISTENER
-  NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
-  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
-  NS_DECL_NSIREFLOWOBSERVER
-
-  // nsIScrollObserver
-  virtual void ScrollPositionChanged() MOZ_OVERRIDE;
-
-  void     Init(nsIWidget* aWidget,
-                nsPresContext* aPresContext,
-                nsIContent* aContent);
-  void     Destroy(void);
-  bool     IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
-  bool     IsEditorHandlingEventForComposition() const;
-  bool     KeepAliveDuringDeactive() const
-  {
-    return mUpdatePreference.WantDuringDeactive();
-  }
-
-  nsCOMPtr<nsIWidget>            mWidget;
-  nsCOMPtr<nsISelection>         mSel;
-  nsCOMPtr<nsIContent>           mRootContent;
-  nsCOMPtr<nsINode>              mEditableNode;
-
-private:
-  void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
-  void ObserveEditableNode();
-
-  nsCOMPtr<nsIDocShell>          mDocShell;
-  nsIMEUpdatePreference mUpdatePreference;
-  uint32_t mPreAttrChangeLength;
-};
-
-/******************************************************************/
-/* nsIMEStateManager                                              */
-/******************************************************************/
-
 nsIContent*    nsIMEStateManager::sContent      = nullptr;
 nsPresContext* nsIMEStateManager::sPresContext  = nullptr;
 bool           nsIMEStateManager::sInstalledMenuKeyboardListener = false;
 bool           nsIMEStateManager::sIsTestingIME = false;
 
-nsTextStateManager* nsIMEStateManager::sTextStateObserver = nullptr;
+// sActiveIMEContentObserver points to the currently active IMEContentObserver.
+// sActiveIMEContentObserver is null if there is no focused editor.
+IMEContentObserver* nsIMEStateManager::sActiveIMEContentObserver = nullptr;
 TextCompositionArray* nsIMEStateManager::sTextCompositions = nullptr;
 
 void
 nsIMEStateManager::Shutdown()
 {
   MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
   delete sTextCompositions;
   sTextCompositions = nullptr;
@@ -235,19 +167,19 @@ nsIMEStateManager::OnChangeFocusInternal
   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);
     }
   }
 
-  if (sTextStateObserver &&
-      (aPresContext || !sTextStateObserver->KeepAliveDuringDeactive()) &&
-      !sTextStateObserver->IsManaging(aPresContext, aContent)) {
+  if (sActiveIMEContentObserver &&
+      (aPresContext || !sActiveIMEContentObserver->KeepAliveDuringDeactive()) &&
+      !sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
     DestroyTextStateManager();
   }
 
   if (!aPresContext) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIWidget> widget =
@@ -285,17 +217,17 @@ nsIMEStateManager::OnChangeFocusInternal
   SetIMEState(newState, aContent, widget, aAction);
 
   sPresContext = aPresContext;
   if (sContent != aContent) {
     NS_IF_RELEASE(sContent);
     NS_IF_ADDREF(sContent = aContent);
   }
 
-  // Don't call CreateTextStateManager() here, it should be called from
+  // Don't call CreateIMEContentObserver() here, it should be called from
   // focus event handler of editor.
 
   return NS_OK;
 }
 
 void
 nsIMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling)
 {
@@ -349,48 +281,48 @@ nsIMEStateManager::OnClickInEditor(nsPre
 void
 nsIMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
                                    nsIContent* aContent)
 {
   if (sPresContext != aPresContext || sContent != aContent) {
     return;
   }
 
-  // If the nsTextStateManager instance isn't managing the editor actually,
+  // If the IMEContentObserver instance isn't managing the editor actually,
   // we need to recreate the instance.
-  if (sTextStateObserver) {
-    if (sTextStateObserver->IsManaging(aPresContext, aContent)) {
+  if (sActiveIMEContentObserver) {
+    if (sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
       return;
     }
     DestroyTextStateManager();
   }
 
-  CreateTextStateManager();
+  CreateIMEContentObserver();
 }
 
 void
 nsIMEStateManager::UpdateIMEState(const IMEState &aNewIMEState,
                                   nsIContent* aContent)
 {
   if (!sPresContext) {
     NS_WARNING("ISM doesn't know which editor has focus");
     return;
   }
   nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (!widget) {
     NS_WARNING("focused widget is not found");
     return;
   }
 
-  // If the nsTextStateManager instance isn't managing the editor's current
+  // If the IMEContentObserver instance isn't managing the editor's current
   // editable root content, the editor frame might be reframed.  We should
   // recreate the instance at that time.
   bool createTextStateManager =
-    (!sTextStateObserver ||
-     !sTextStateObserver->IsManaging(sPresContext, aContent));
+    (!sActiveIMEContentObserver ||
+     !sActiveIMEContentObserver->IsManaging(sPresContext, aContent));
 
   bool updateIMEState =
     (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
 
   if (updateIMEState) {
     // commit current composition before modifying IME state.
     NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget);
   }
@@ -401,17 +333,17 @@ nsIMEStateManager::UpdateIMEState(const 
 
   if (updateIMEState) {
     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                               InputContextAction::FOCUS_NOT_CHANGED);
     SetIMEState(aNewIMEState, aContent, widget, action);
   }
 
   if (createTextStateManager) {
-    CreateTextStateManager();
+    CreateIMEContentObserver();
   }
 }
 
 IMEState
 nsIMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
                                   nsIContent*    aContent)
 {
   // On Printing or Print Preview, we don't need IME.
@@ -694,476 +626,16 @@ nsIMEStateManager::NotifyIME(IMEMessage 
 
   nsIWidget* widget = aPresContext->GetRootWidget();
   if (!widget) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   return NotifyIME(aMessage, widget);
 }
 
-void
-nsTextStateManager::Init(nsIWidget* aWidget,
-                         nsPresContext* aPresContext,
-                         nsIContent* aContent)
-{
-  mWidget = aWidget;
-  mEditableNode =
-    nsIMEStateManager::GetRootEditableNode(aPresContext, aContent);
-  if (!mEditableNode) {
-    return;
-  }
-
-  nsIPresShell* presShell = aPresContext->PresShell();
-
-  // get selection and root content
-  nsCOMPtr<nsISelectionController> selCon;
-  if (mEditableNode->IsNodeOfType(nsINode::eCONTENT)) {
-    nsIFrame* frame =
-      static_cast<nsIContent*>(mEditableNode.get())->GetPrimaryFrame();
-    NS_ENSURE_TRUE_VOID(frame);
-
-    frame->GetSelectionController(aPresContext,
-                                  getter_AddRefs(selCon));
-  } else {
-    // mEditableNode is a document
-    selCon = do_QueryInterface(presShell);
-  }
-  NS_ENSURE_TRUE_VOID(selCon);
-
-  selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
-                       getter_AddRefs(mSel));
-  NS_ENSURE_TRUE_VOID(mSel);
-
-  nsCOMPtr<nsIDOMRange> selDomRange;
-  if (NS_SUCCEEDED(mSel->GetRangeAt(0, getter_AddRefs(selDomRange)))) {
-    nsRange* selRange = static_cast<nsRange*>(selDomRange.get());
-    NS_ENSURE_TRUE_VOID(selRange && selRange->GetStartParent());
-
-    mRootContent = selRange->GetStartParent()->
-                     GetSelectionRootContent(presShell);
-  } else {
-    mRootContent = mEditableNode->GetSelectionRootContent(presShell);
-  }
-  if (!mRootContent && mEditableNode->IsNodeOfType(nsINode::eDOCUMENT)) {
-    // The document node is editable, but there are no contents, this document
-    // is not editable.
-    return;
-  }
-  NS_ENSURE_TRUE_VOID(mRootContent);
-
-  if (nsIMEStateManager::sIsTestingIME) {
-    nsIDocument* doc = aPresContext->Document();
-    (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusIn"),
-                         false, false))->RunDOMEventWhenSafe();
-  }
-
-  aWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS));
-
-  // NOTIFY_IME_OF_FOCUS might cause recreating nsTextStateManager
-  // instance via nsIMEStateManager::UpdateIMEState().  So, this
-  // instance might already have been destroyed, check it.
-  if (!mRootContent) {
-    return;
-  }
-
-  mDocShell = aPresContext->GetDocShell();
-
-  ObserveEditableNode();
-}
-
-void
-nsTextStateManager::ObserveEditableNode()
-{
-  MOZ_ASSERT(mSel);
-  MOZ_ASSERT(mRootContent);
-
-  mUpdatePreference = mWidget->GetIMEUpdatePreference();
-  if (mUpdatePreference.WantSelectionChange()) {
-    // add selection change listener
-    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
-    NS_ENSURE_TRUE_VOID(selPrivate);
-    nsresult rv = selPrivate->AddSelectionListener(this);
-    NS_ENSURE_SUCCESS_VOID(rv);
-  }
-
-  if (mUpdatePreference.WantTextChange()) {
-    // add text change observer
-    mRootContent->AddMutationObserver(this);
-  }
-
-  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
-    // Add scroll position listener and reflow observer to detect position and
-    // size changes
-    mDocShell->AddWeakScrollObserver(this);
-    mDocShell->AddWeakReflowObserver(this);
-  }
-}
-
-void
-nsTextStateManager::Destroy(void)
-{
-  // If CreateTextStateManager failed, mRootContent will be null,
-  // and we should not call NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR))
-  if (mRootContent) {
-    if (nsIMEStateManager::sIsTestingIME && mEditableNode) {
-      nsIDocument* doc = mEditableNode->OwnerDoc();
-      (new nsAsyncDOMEvent(doc, NS_LITERAL_STRING("MozIMEFocusOut"),
-                           false, false))->RunDOMEventWhenSafe();
-    }
-    mWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
-  }
-  // Even if there are some pending notification, it'll never notify the widget.
-  mWidget = nullptr;
-  if (mUpdatePreference.WantSelectionChange() && mSel) {
-    nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
-    if (selPrivate)
-      selPrivate->RemoveSelectionListener(this);
-  }
-  mSel = nullptr;
-  if (mUpdatePreference.WantTextChange() && mRootContent) {
-    mRootContent->RemoveMutationObserver(this);
-  }
-  if (mUpdatePreference.WantPositionChanged() && mDocShell) {
-    mDocShell->RemoveWeakScrollObserver(this);
-    mDocShell->RemoveWeakReflowObserver(this);
-  }
-  mRootContent = nullptr;
-  mEditableNode = nullptr;
-  mDocShell = nullptr;
-  mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
-}
-
-bool
-nsTextStateManager::IsManaging(nsPresContext* aPresContext,
-                               nsIContent* aContent)
-{
-  if (!mSel || !mRootContent || !mEditableNode) {
-    return false; // failed to initialize.
-  }
-  if (!mRootContent->IsInDoc()) {
-    return false; // the focused editor has already been reframed.
-  }
-  return mEditableNode == nsIMEStateManager::GetRootEditableNode(aPresContext,
-                                                                 aContent);
-}
-
-bool
-nsTextStateManager::IsEditorHandlingEventForComposition() const
-{
-  if (!mWidget) {
-    return false;
-  }
-  nsRefPtr<TextComposition> composition =
-    nsIMEStateManager::GetTextCompositionFor(mWidget);
-  if (!composition) {
-    return false;
-  }
-  return composition->IsEditorHandlingEvent();
-}
-
-NS_IMPL_ISUPPORTS5(nsTextStateManager,
-                   nsIMutationObserver,
-                   nsISelectionListener,
-                   nsIReflowObserver,
-                   nsIScrollObserver,
-                   nsISupportsWeakReference)
-
-// Helper class, used for selection change notification
-class SelectionChangeEvent : public nsRunnable {
-public:
-  SelectionChangeEvent(nsTextStateManager *aDispatcher,
-                       bool aCausedByComposition)
-    : mDispatcher(aDispatcher)
-    , mCausedByComposition(aCausedByComposition)
-  {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE);
-      notification.mSelectionChangeData.mCausedByComposition =
-         mCausedByComposition;
-      mDispatcher->mWidget->NotifyIME(notification);
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-  bool mCausedByComposition;
-};
-
-nsresult
-nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc,
-                                           nsISelection* aSel,
-                                           int16_t aReason)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return NS_OK;
-  }
-
-  int32_t count = 0;
-  nsresult rv = aSel->GetRangeCount(&count);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (count > 0 && mWidget) {
-    nsContentUtils::AddScriptRunner(
-      new SelectionChangeEvent(this, causedByComposition));
-  }
-  return NS_OK;
-}
-
-// Helper class, used for text change notification
-class TextChangeEvent : public nsRunnable {
-public:
-  TextChangeEvent(nsTextStateManager* aDispatcher,
-                  uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd,
-                  bool aCausedByComposition)
-    : mDispatcher(aDispatcher)
-    , mStart(aStart)
-    , mOldEnd(aOldEnd)
-    , mNewEnd(aNewEnd)
-    , mCausedByComposition(aCausedByComposition)
-  {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
-      notification.mTextChangeData.mStartOffset = mStart;
-      notification.mTextChangeData.mOldEndOffset = mOldEnd;
-      notification.mTextChangeData.mNewEndOffset = mNewEnd;
-      notification.mTextChangeData.mCausedByComposition = mCausedByComposition;
-      mDispatcher->mWidget->NotifyIME(notification);
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-  uint32_t mStart, mOldEnd, mNewEnd;
-  bool mCausedByComposition;
-};
-
-class PositionChangeEvent MOZ_FINAL : public nsRunnable
-{
-public:
-  PositionChangeEvent(nsTextStateManager* aDispatcher)
-    : mDispatcher(aDispatcher) {
-    MOZ_ASSERT(mDispatcher);
-  }
-
-  NS_IMETHOD Run() {
-    if (mDispatcher->mWidget) {
-      mDispatcher->mWidget->NotifyIME(
-        IMENotification(NOTIFY_IME_OF_POSITION_CHANGE));
-    }
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsTextStateManager> mDispatcher;
-};
-
-void
-nsTextStateManager::ScrollPositionChanged()
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-}
-
-NS_IMETHODIMP
-nsTextStateManager::Reflow(DOMHighResTimeStamp aStart,
-                           DOMHighResTimeStamp aEnd)
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsTextStateManager::ReflowInterruptible(DOMHighResTimeStamp aStart,
-                                        DOMHighResTimeStamp aEnd)
-{
-  if (mWidget) {
-    nsContentUtils::AddScriptRunner(new PositionChangeEvent(this));
-  }
-  return NS_OK;
-}
-
-void
-nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument,
-                                         nsIContent* aContent,
-                                         CharacterDataChangeInfo* aInfo)
-{
-  NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
-               "character data changed for non-text node");
-
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  uint32_t offset = 0;
-  // get offsets of change and fire notification
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    mRootContent, aContent, aInfo->mChangeStart, &offset)))
-    return;
-
-  uint32_t oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
-  uint32_t newEnd = offset + aInfo->mReplaceLength;
-
-  nsContentUtils::AddScriptRunner(
-    new TextChangeEvent(this, offset, oldEnd, newEnd, causedByComposition));
-}
-
-void
-nsTextStateManager::NotifyContentAdded(nsINode* aContainer,
-                                       int32_t aStartIndex,
-                                       int32_t aEndIndex)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  uint32_t offset = 0, newOffset = 0;
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    mRootContent, aContainer, aStartIndex, &offset)))
-    return;
-
-  // get offset at the end of the last added node
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    aContainer->GetChildAt(aStartIndex),
-                    aContainer, aEndIndex, &newOffset)))
-    return;
-
-  // fire notification
-  if (newOffset) {
-    nsContentUtils::AddScriptRunner(
-      new TextChangeEvent(this, offset, offset, offset + newOffset,
-                          causedByComposition));
-  }
-}
-
-void
-nsTextStateManager::ContentAppended(nsIDocument* aDocument,
-                                    nsIContent* aContainer,
-                                    nsIContent* aFirstNewContent,
-                                    int32_t aNewIndexInContainer)
-{
-  NotifyContentAdded(aContainer, aNewIndexInContainer,
-                     aContainer->GetChildCount());
-}
-
-void
-nsTextStateManager::ContentInserted(nsIDocument* aDocument,
-                                     nsIContent* aContainer,
-                                     nsIContent* aChild,
-                                     int32_t aIndexInContainer)
-{
-  NotifyContentAdded(NODE_FROM(aContainer, aDocument),
-                     aIndexInContainer, aIndexInContainer + 1);
-}
-
-void
-nsTextStateManager::ContentRemoved(nsIDocument* aDocument,
-                                   nsIContent* aContainer,
-                                   nsIContent* aChild,
-                                   int32_t aIndexInContainer,
-                                   nsIContent* aPreviousSibling)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  uint32_t offset = 0, childOffset = 1;
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    mRootContent, NODE_FROM(aContainer, aDocument),
-                    aIndexInContainer, &offset)))
-    return;
-
-  // get offset at the end of the deleted node
-  if (aChild->IsNodeOfType(nsINode::eTEXT))
-    childOffset = aChild->TextLength();
-  else if (0 < aChild->GetChildCount())
-    childOffset = aChild->GetChildCount();
-
-  if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-                    aChild, aChild, childOffset, &childOffset)))
-    return;
-
-  // fire notification
-  if (childOffset) {
-    nsContentUtils::AddScriptRunner(
-      new TextChangeEvent(this, offset, offset + childOffset, offset,
-                          causedByComposition));
-  }
-}
-
-static nsIContent*
-GetContentBR(mozilla::dom::Element *aElement) {
-  if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
-    return nullptr;
-  }
-  nsIContent *content = static_cast<nsIContent*>(aElement);
-  return content->IsHTML(nsGkAtoms::br) ? content : nullptr;
-}
-
-void
-nsTextStateManager::AttributeWillChange(nsIDocument* aDocument,
-                                        mozilla::dom::Element* aElement,
-                                        int32_t      aNameSpaceID,
-                                        nsIAtom*     aAttribute,
-                                        int32_t      aModType)
-{
-  nsIContent *content = GetContentBR(aElement);
-  mPreAttrChangeLength = content ?
-    nsContentEventHandler::GetNativeTextLength(content) : 0;
-}
-
-void
-nsTextStateManager::AttributeChanged(nsIDocument* aDocument,
-                                     mozilla::dom::Element* aElement,
-                                     int32_t aNameSpaceID,
-                                     nsIAtom* aAttribute,
-                                     int32_t aModType)
-{
-  bool causedByComposition = IsEditorHandlingEventForComposition();
-  if (causedByComposition &&
-      !mUpdatePreference.WantChangesCausedByComposition()) {
-    return;
-  }
-
-  nsIContent *content = GetContentBR(aElement);
-  if (!content) {
-    return;
-  }
-  uint32_t postAttrChangeLength =
-    nsContentEventHandler::GetNativeTextLength(content);
-  if (postAttrChangeLength != mPreAttrChangeLength) {
-    uint32_t start;
-    if (NS_SUCCEEDED(nsContentEventHandler::GetFlatTextOffsetOfRange(
-        mRootContent, content, 0, &start))) {
-      nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start,
-        start + mPreAttrChangeLength, start + postAttrChangeLength,
-        causedByComposition));
-    }
-  }
-}
-
 bool
 nsIMEStateManager::IsEditable(nsINode* node)
 {
   if (node->IsEditable()) {
     return true;
   }
   // |node| might be readwrite (for example, a text control)
   if (node->IsElement() && node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
@@ -1206,72 +678,74 @@ nsIMEStateManager::IsEditableIMEState(ns
     default:
       MOZ_CRASH("Unknown IME enable state");
   }
 }
 
 void
 nsIMEStateManager::DestroyTextStateManager()
 {
-  if (!sTextStateObserver) {
+  if (!sActiveIMEContentObserver) {
     return;
   }
 
-  nsRefPtr<nsTextStateManager> tsm;
-  tsm.swap(sTextStateObserver);
+  nsRefPtr<IMEContentObserver> tsm;
+  tsm.swap(sActiveIMEContentObserver);
   tsm->Destroy();
 }
 
 void
-nsIMEStateManager::CreateTextStateManager()
+nsIMEStateManager::CreateIMEContentObserver()
 {
-  if (sTextStateObserver) {
+  if (sActiveIMEContentObserver) {
     NS_WARNING("text state observer has been there already");
-    MOZ_ASSERT(sTextStateObserver->IsManaging(sPresContext, sContent));
+    MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
     return;
   }
 
   nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   if (!widget) {
     return; // Sometimes, there are no widgets.
   }
 
-  // If it's not text ediable, we don't need to create nsTextStateManager.
+  // If it's not text ediable, we don't need to create IMEContentObserver.
   if (!IsEditableIMEState(widget)) {
     return;
   }
 
   static bool sInitializeIsTestingIME = true;
   if (sInitializeIsTestingIME) {
     Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false);
     sInitializeIsTestingIME = false;
   }
 
-  sTextStateObserver = new nsTextStateManager();
-  NS_ADDREF(sTextStateObserver);
+  sActiveIMEContentObserver = new IMEContentObserver();
+  NS_ADDREF(sActiveIMEContentObserver);
 
-  // nsTextStateManager::Init() might create another nsTextStateManager
-  // instance.  So, sTextStateObserver would be replaced with new one.
+  // IMEContentObserver::Init() might create another IMEContentObserver
+  // instance.  So, sActiveIMEContentObserver would be replaced with new one.
   // We should hold the current instance here.
-  nsRefPtr<nsTextStateManager> kungFuDeathGrip(sTextStateObserver);
-  sTextStateObserver->Init(widget, sPresContext, sContent);
+  nsRefPtr<IMEContentObserver> kungFuDeathGrip(sActiveIMEContentObserver);
+  sActiveIMEContentObserver->Init(widget, sPresContext, sContent);
 }
 
 nsresult
-nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel,
-                                            nsIContent** aRoot)
+nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection,
+                                            nsIContent** aRootContent)
 {
-  if (!sTextStateObserver || !sTextStateObserver->mEditableNode ||
-      !sTextStateObserver->mSel)
+  if (!sActiveIMEContentObserver || !sActiveIMEContentObserver->mEditableNode ||
+      !sActiveIMEContentObserver->mSel) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
-  NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
-               "uninitialized text state observer");
-  NS_ADDREF(*aSel = sTextStateObserver->mSel);
-  NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
+  NS_ASSERTION(sActiveIMEContentObserver->mSel &&
+                 sActiveIMEContentObserver->mRootContent,
+               "uninitialized content observer");
+  NS_ADDREF(*aSelection = sActiveIMEContentObserver->mSel);
+  NS_ADDREF(*aRootContent = sActiveIMEContentObserver->mRootContent);
   return NS_OK;
 }
 
 // static
 already_AddRefed<TextComposition>
 nsIMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
 {
   if (!sTextCompositions) {
--- a/dom/events/nsIMEStateManager.h
+++ b/dom/events/nsIMEStateManager.h
@@ -10,20 +10,20 @@
 #include "nsIWidget.h"
 
 class nsDispatchingCallback;
 class nsIContent;
 class nsIDOMMouseEvent;
 class nsINode;
 class nsPIDOMWindow;
 class nsPresContext;
-class nsTextStateManager;
 class nsISelection;
 
 namespace mozilla {
+class IMEContentObserver;
 class TextCompositionArray;
 class TextComposition;
 } // namespace mozilla
 
 /*
  * IME state manager
  */
 
@@ -113,43 +113,45 @@ public:
 
   /**
    * Send a notification to IME.  It depends on the IME or platform spec what
    * will occur (or not occur).
    */
   static nsresult NotifyIME(IMEMessage aMessage, nsIWidget* aWidget);
   static nsresult NotifyIME(IMEMessage aMessage, nsPresContext* aPresContext);
 
+  static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
+                                      nsIContent* aContent);
+  static bool IsTestingIME() { return sIsTestingIME; }
+
 protected:
   static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
                                         nsIContent* aContent,
                                         InputContextAction aAction);
   static void SetIMEState(const IMEState &aState,
                           nsIContent* aContent,
                           nsIWidget* aWidget,
                           InputContextAction aAction);
   static IMEState GetNewIMEState(nsPresContext* aPresContext,
                                  nsIContent* aContent);
 
   static void EnsureTextCompositionArray();
-  static void CreateTextStateManager();
+  static void CreateIMEContentObserver();
   static void DestroyTextStateManager();
 
   static bool IsEditable(nsINode* node);
-  static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
-                                      nsIContent* aContent);
 
   static bool IsEditableIMEState(nsIWidget* aWidget);
 
   static nsIContent*    sContent;
   static nsPresContext* sPresContext;
   static bool           sInstalledMenuKeyboardListener;
   static bool           sIsTestingIME;
 
-  static nsTextStateManager* sTextStateObserver;
+  static mozilla::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.
   // The instances in this array can be destroyed automatically if you do
   // something to cause committing or canceling the composition.
   static mozilla::TextCompositionArray* sTextCompositions;
 };