Bug 1052343 part.5 Recreate nsTextStore instance at each focus change r=emk
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 03 Sep 2014 10:38:20 +0900
changeset 203318 06ba93acf94e6ff7c7562ea2bf7d90588a975668
parent 203317 31cd3664ea7d274346f8318939d9c81f0f0c782f
child 203319 b364c512840e980663ddc86f8c99eb63ca61a322
push id27425
push userryanvm@gmail.com
push dateWed, 03 Sep 2014 20:38:59 +0000
treeherdermozilla-central@acbdce59da2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk
bugs1052343
milestone35.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 1052343 part.5 Recreate nsTextStore instance at each focus change r=emk
widget/windows/WinIMEHandler.cpp
widget/windows/nsTextStore.cpp
widget/windows/nsTextStore.h
widget/windows/winrt/MetroWidget.cpp
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -164,20 +164,20 @@ IMEHandler::NotifyIME(nsWindow* aWindow,
   if (IsTSFAvailable()) {
     switch (aIMENotification.mMessage) {
       case NOTIFY_IME_OF_SELECTION_CHANGE:
         return nsTextStore::OnSelectionChange();
       case NOTIFY_IME_OF_TEXT_CHANGE:
         return nsTextStore::OnTextChange(aIMENotification);
       case NOTIFY_IME_OF_FOCUS:
         return nsTextStore::OnFocusChange(true, aWindow,
-                 aWindow->GetInputContext().mIMEState);
+                                          aWindow->GetInputContext());
       case NOTIFY_IME_OF_BLUR:
         return nsTextStore::OnFocusChange(false, aWindow,
-                 aWindow->GetInputContext().mIMEState);
+                                          aWindow->GetInputContext());
       case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
         return nsTextStore::OnMouseButtonEvent(aIMENotification);
       case REQUEST_TO_COMMIT_COMPOSITION:
         if (nsTextStore::IsComposingOn(aWindow)) {
           nsTextStore::CommitComposition(false);
         }
         return NS_OK;
       case REQUEST_TO_CANCEL_COMPOSITION:
@@ -205,17 +205,17 @@ IMEHandler::NotifyIME(nsWindow* aWindow,
       nsIMM32Handler::OnUpdateComposition(aWindow);
       return NS_OK;
 #ifdef NS_ENABLE_TSF
     case NOTIFY_IME_OF_BLUR:
       // If a plugin gets focus while TSF has focus, we need to notify TSF of
       // the blur.
       if (nsTextStore::ThinksHavingFocus()) {
         return nsTextStore::OnFocusChange(false, aWindow,
-                                          aWindow->GetInputContext().mIMEState);
+                                          aWindow->GetInputContext());
       }
       return NS_ERROR_NOT_IMPLEMENTED;
 #endif //NS_ENABLE_TSF
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
--- a/widget/windows/nsTextStore.cpp
+++ b/widget/windows/nsTextStore.cpp
@@ -1192,75 +1192,75 @@ nsTextStore::nsTextStore()
 
 nsTextStore::~nsTextStore()
 {
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
     ("TSF: 0x%p nsTextStore instance is destroyed", this));
 }
 
 bool
-nsTextStore::Create(nsWindowBase* aWidget)
+nsTextStore::Init(nsWindowBase* aWidget)
 {
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
-    ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)",
+    ("TSF: 0x%p nsTextStore::Init(aWidget=0x%p)",
      this, aWidget));
 
   TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard();
 
   if (mDocumentMgr) {
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
-      ("TSF: 0x%p   nsTextStore::Create() FAILED due to already initialized",
+      ("TSF: 0x%p   nsTextStore::Init() FAILED due to already initialized",
        this));
     return false;
   }
 
   // Create document manager
   HRESULT hr = sTsfThreadMgr->CreateDocumentMgr(
                                   getter_AddRefs(mDocumentMgr));
   if (FAILED(hr)) {
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
-      ("TSF: 0x%p   nsTextStore::Create() FAILED to create DocumentMgr "
+      ("TSF: 0x%p   nsTextStore::Init() FAILED to create DocumentMgr "
        "(0x%08X)", this, hr));
     return false;
   }
   mWidget = aWidget;
 
   // Create context and add it to document manager
   hr = mDocumentMgr->CreateContext(sTsfClientId, 0,
                                    static_cast<ITextStoreACP*>(this),
                                    getter_AddRefs(mContext), &mEditCookie);
   if (FAILED(hr)) {
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
-      ("TSF: 0x%p   nsTextStore::Create() FAILED to create the context "
+      ("TSF: 0x%p   nsTextStore::Init() FAILED to create the context "
        "(0x%08X)", this, hr));
     mDocumentMgr = nullptr;
     return false;
   }
 
   hr = mDocumentMgr->Push(mContext);
   if (FAILED(hr)) {
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
-      ("TSF: 0x%p   nsTextStore::Create() FAILED to push the context (0x%08X)",
+      ("TSF: 0x%p   nsTextStore::Init() FAILED to push the context (0x%08X)",
        this, hr));
     // XXX Why don't we use NS_IF_RELEASE() here??
     mContext = nullptr;
     mDocumentMgr = nullptr;
     return false;
   }
 
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
-    ("TSF: 0x%p   nsTextStore::Create() succeeded: "
+    ("TSF: 0x%p   nsTextStore::Init() succeeded: "
      "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
      this, mDocumentMgr.get(), mContext.get(), mEditCookie));
 
   return true;
 }
 
 bool
-nsTextStore::Destroy(void)
+nsTextStore::Destroy()
 {
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
     ("TSF: 0x%p nsTextStore::Destroy(), mComposition.IsComposing()=%s",
      this, GetBoolName(mComposition.IsComposing())));
 
   // If there is composition, TSF keeps the composition even after the text
   // store destroyed.  So, we should clear the composition here.
   if (mComposition.IsComposing()) {
@@ -3905,68 +3905,133 @@ nsTextStore::UnadviseMouseSink(DWORD dwC
          ("TSF: 0x%p   nsTextStore::UnadviseMouseSink(), succeeded", this));
   return S_OK;
 }
 
 // static
 nsresult
 nsTextStore::OnFocusChange(bool aGotFocus,
                            nsWindowBase* aFocusedWidget,
-                           const IMEState& aIMEState)
+                           const InputContext& aContext)
 {
   PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
          ("TSF:   nsTextStore::OnFocusChange(aGotFocus=%s, "
-          "aFocusedWidget=0x%p, aIMEState={ mEnabled=%s }), "
+          "aFocusedWidget=0x%p, aContext={ mIMEState={ mEnabled=%s }, "
+          "mHTMLInputType=\"%s\" }), "
           "sTsfThreadMgr=0x%p, sEnabledTextStore=0x%p",
           GetBoolName(aGotFocus), aFocusedWidget,
-          GetIMEEnabledName(aIMEState.mEnabled),
+          GetIMEEnabledName(aContext.mIMEState.mEnabled),
+          NS_ConvertUTF16toUTF8(aContext.mHTMLInputType).get(),
           sTsfThreadMgr, sEnabledTextStore));
 
-  // no change notifications if TSF is disabled
-  NS_ENSURE_TRUE(IsInTSFMode(), NS_ERROR_NOT_AVAILABLE);
-
-  nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
-  if (aGotFocus && aIMEState.IsEditable()) {
-    bool bRet = sEnabledTextStore->Create(aFocusedWidget);
-    NS_ENSURE_TRUE(bRet, NS_ERROR_FAILURE);
-    NS_ENSURE_TRUE(sEnabledTextStore->mDocumentMgr, NS_ERROR_FAILURE);
-    if (aIMEState.mEnabled == IMEState::PASSWORD) {
-      MarkContextAsKeyboardDisabled(sEnabledTextStore->mContext);
-      nsRefPtr<ITfContext> topContext;
-      sEnabledTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
-      if (topContext && topContext != sEnabledTextStore->mContext) {
-        MarkContextAsKeyboardDisabled(topContext);
-      }
+  if (NS_WARN_IF(!IsInTSFMode())) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // If currently sEnableTextStore has focus, notifies TSF of losing focus.
+  if (ThinksHavingFocus()) {
+    nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
+    DebugOnly<HRESULT> hr =
+      sTsfThreadMgr->AssociateFocus(
+        sEnabledTextStore->mWidget->GetWindowHandle(),
+        nullptr, getter_AddRefs(prevFocusedDocumentMgr));
+    NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
+    NS_ASSERTION(prevFocusedDocumentMgr == sEnabledTextStore->mDocumentMgr,
+                 "different documentMgr has been associated with the window");
+  }
+
+  // If there is sEnabledTextStore, we don't use it in the new focused editor.
+  // Release it now.
+  if (sEnabledTextStore) {
+    sEnabledTextStore->Destroy();
+    NS_RELEASE(sEnabledTextStore);
+  }
+
+  // If this is a notification of blur, move focus to the dummy document
+  // manager.
+  if (!aGotFocus || !aContext.mIMEState.IsEditable()) {
+    HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr);
+    if (NS_WARN_IF(FAILED(hr))) {
+      PR_LOG(sTextStoreLog, PR_LOG_ERROR,
+             ("TSF:   nsTextStore::OnFocusChange() FAILED due to "
+              "ITfThreadMgr::SetFocus() failure"));
+      return NS_ERROR_FAILURE;
     }
-    HRESULT hr = sTsfThreadMgr->SetFocus(sEnabledTextStore->mDocumentMgr);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
-    // Use AssociateFocus() for ensuring that any native focus event
-    // never steal focus from our documentMgr.
-    hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
-                                       sEnabledTextStore->mDocumentMgr,
-                                       getter_AddRefs(prevFocusedDocumentMgr));
-    NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
-  } else {
-    if (ThinksHavingFocus()) {
-      DebugOnly<HRESULT> hr =
-        sTsfThreadMgr->AssociateFocus(
-          sEnabledTextStore->mWidget->GetWindowHandle(),
-          nullptr, getter_AddRefs(prevFocusedDocumentMgr));
-      NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
-      NS_ASSERTION(prevFocusedDocumentMgr == sEnabledTextStore->mDocumentMgr,
-                   "different documentMgr has been associated with the window");
+    return NS_OK;
+  }
+
+  // If an editor is getting focus, create new TextStore and set focus.
+  if (NS_WARN_IF(!CreateAndSetFocus(aFocusedWidget, aContext))) {
+    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
+           ("TSF:   nsTextStore::OnFocusChange() FAILED due to "
+            "ITfThreadMgr::CreateAndSetFocus() failure"));
+    // If setting focus, we should destroy the TextStore completely because
+    // it causes memory leak.
+    if (sEnabledTextStore) {
       sEnabledTextStore->Destroy();
+      NS_RELEASE(sEnabledTextStore);
     }
-    HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr);
-    NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
+    return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 // static
+bool
+nsTextStore::CreateAndSetFocus(nsWindowBase* aFocusedWidget,
+                               const InputContext& aContext)
+{
+  // TSF might do something which causes that we need to access static methods
+  // of nsTextStore.  At that time, sEnabledTextStore may be necessary.
+  // So, we should set sEnabledTextStore directly.
+  NS_ADDREF(sEnabledTextStore = new nsTextStore());
+  if (NS_WARN_IF(!sEnabledTextStore->Init(aFocusedWidget))) {
+    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
+           ("TSF:   nsTextStore::CreateAndSetFocus() FAILED due to "
+            "nsTextStore::Init() failure"));
+    return false;
+  }
+  if (NS_WARN_IF(!sEnabledTextStore->mDocumentMgr)) {
+    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
+           ("TSF:   nsTextStore::CreateAndSetFocus() FAILED due to "
+            "invalid nsTextStore::mDocumentMgr"));
+    return false;
+  }
+  if (aContext.mIMEState.mEnabled == IMEState::PASSWORD) {
+    MarkContextAsKeyboardDisabled(sEnabledTextStore->mContext);
+    nsRefPtr<ITfContext> topContext;
+    sEnabledTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
+    if (topContext && topContext != sEnabledTextStore->mContext) {
+      MarkContextAsKeyboardDisabled(topContext);
+    }
+  }
+  HRESULT hr = sTsfThreadMgr->SetFocus(sEnabledTextStore->mDocumentMgr);
+  if (NS_WARN_IF(FAILED(hr))) {
+    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
+           ("TSF:   nsTextStore::CreateAndSetFocus() FAILED due to "
+            "ITfTheadMgr::SetFocus() failure"));
+    return false;
+  }
+  // Use AssociateFocus() for ensuring that any native focus event
+  // never steal focus from our documentMgr.
+  nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
+  hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
+                                     sEnabledTextStore->mDocumentMgr,
+                                     getter_AddRefs(prevFocusedDocumentMgr));
+  if (NS_WARN_IF(FAILED(hr))) {
+    PR_LOG(sTextStoreLog, PR_LOG_ERROR,
+           ("TSF:   nsTextStore::CreateAndSetFocus() FAILED due to "
+            "ITfTheadMgr::AssociateFocus() failure"));
+    return false;
+  }
+  sEnabledTextStore->SetInputScope(aContext.mHTMLInputType);
+  return true;
+}
+
+// static
 nsIMEUpdatePreference
 nsTextStore::GetIMEUpdatePreference()
 {
   if (sTsfThreadMgr && sEnabledTextStore && sEnabledTextStore->mDocumentMgr) {
     nsRefPtr<ITfDocumentMgr> docMgr;
     sTsfThreadMgr->GetFocus(getter_AddRefs(docMgr));
     if (docMgr == sEnabledTextStore->mDocumentMgr) {
       nsIMEUpdatePreference updatePreference(
@@ -4369,32 +4434,29 @@ nsTextStore::SetInputContext(nsWindowBas
           "aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), "
           "sEnabledTextStore=0x%p, ThinksHavingFocus()=%s",
           aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled),
           GetFocusChangeName(aAction.mFocusChange), sEnabledTextStore,
           GetBoolName(ThinksHavingFocus())));
 
   NS_ENSURE_TRUE_VOID(IsInTSFMode());
 
-  if (!sEnabledTextStore) {
-    return;
-  }
-
-  sEnabledTextStore->SetInputScope(aContext.mHTMLInputType);
-
   if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) {
+    if (sEnabledTextStore) {
+      sEnabledTextStore->SetInputScope(aContext.mHTMLInputType);
+    }
     return;
   }
 
   // If focus isn't actually changed but the enabled state is changed,
   // emulate the focus move.
   if (!ThinksHavingFocus() && aContext.mIMEState.IsEditable()) {
-    OnFocusChange(true, aWidget, aContext.mIMEState);
+    OnFocusChange(true, aWidget, aContext);
   } else if (ThinksHavingFocus() && !aContext.mIMEState.IsEditable()) {
-    OnFocusChange(false, aWidget, aContext.mIMEState);
+    OnFocusChange(false, aWidget, aContext);
   }
 }
 
 // static
 void
 nsTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext)
 {
   VARIANT variant_int4_value1;
@@ -4579,48 +4641,42 @@ nsTextStore::Initialize()
   if (!staticSink->Init(threadMgr, inputProcessorProfiles)) {
     TSFStaticSink::Shutdown();
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
       ("TSF:   nsTextStore::Initialize() FAILED to initialize TSFStaticSink "
        "instance"));
     return;
   }
 
-  PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
-    ("TSF:   nsTextStore::Initialize() is creating "
-     "an nsTextStore instance..."));
-  nsRefPtr<nsTextStore> textStore = new nsTextStore();
-
   inputProcessorProfiles.swap(sInputProcessorProfiles);
   threadMgr.swap(sTsfThreadMgr);
   messagePump.swap(sMessagePump);
   keystrokeMgr.swap(sKeystrokeMgr);
   displayAttributeMgr.swap(sDisplayAttrMgr);
   categoryMgr.swap(sCategoryMgr);
   disabledDocumentMgr.swap(sTsfDisabledDocumentMgr);
   disabledContext.swap(sTsfDisabledContext);
-  sEnabledTextStore = textStore;
 
   sCreateNativeCaretForATOK =
     Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true);
   sDoNotReturnNoLayoutErrorToFreeChangJie =
     Preferences::GetBool(
       "intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error", true);
   sDoNotReturnNoLayoutErrorToEasyChangjei =
     Preferences::GetBool(
       "intl.tsf.hack.easy_changjei.do_not_return_no_layout_error", true);
 
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
     ("TSF:   nsTextStore::Initialize(), sTsfThreadMgr=0x%p, "
-     "sTsfClientId=0x%08X, sEnabledTextStore=0x%p, sDisplayAttrMgr=0x%p, "
+     "sTsfClientId=0x%08X, sDisplayAttrMgr=0x%p, "
      "sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p, "
      "sCreateNativeCaretForATOK=%s, "
      "sDoNotReturnNoLayoutErrorToFreeChangJie=%s, "
      "sDoNotReturnNoLayoutErrorToEasyChangjei=%s",
-     sTsfThreadMgr, sTsfClientId, sEnabledTextStore, sDisplayAttrMgr,
+     sTsfThreadMgr, sTsfClientId, sDisplayAttrMgr,
      sCategoryMgr, sTsfDisabledDocumentMgr, sTsfDisabledContext,
      GetBoolName(sCreateNativeCaretForATOK),
      GetBoolName(sDoNotReturnNoLayoutErrorToFreeChangJie),
      GetBoolName(sDoNotReturnNoLayoutErrorToEasyChangjei)));
 }
 
 // static
 void
--- a/widget/windows/nsTextStore.h
+++ b/widget/windows/nsTextStore.h
@@ -132,17 +132,17 @@ public:
   }
 
   static void SetInputContext(nsWindowBase* aWidget,
                               const InputContext& aContext,
                               const InputContextAction& aAction);
 
   static nsresult OnFocusChange(bool aGotFocus,
                                 nsWindowBase* aFocusedWidget,
-                                const IMEState& aIMEState);
+                                const InputContext& aContext);
   static nsresult OnTextChange(const IMENotification& aIMENotification)
   {
     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
     return sEnabledTextStore ?
       sEnabledTextStore->OnTextChangeInternal(aIMENotification) : NS_OK;
   }
 
   static nsresult OnSelectionChange(void)
@@ -221,21 +221,23 @@ public:
   // Returns true when keyboard layout has IME (TIP).
   static bool     CurrentKeyboardLayoutHasIME();
 #endif // #ifdef DEBUG
 
 protected:
   nsTextStore();
   ~nsTextStore();
 
+  static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget,
+                                const InputContext& aContext);
   static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
   static void MarkContextAsEmpty(ITfContext* aContext);
 
-  bool     Create(nsWindowBase* aWidget);
-  bool     Destroy(void);
+  bool     Init(nsWindowBase* aWidget);
+  bool     Destroy();
 
   bool     IsReadLock(DWORD aLock) const
   {
     return (TS_LF_READ == (aLock & TS_LF_READ));
   }
   bool     IsReadWriteLock(DWORD aLock) const
   {
     return (TS_LF_READWRITE == (aLock & TS_LF_READWRITE));
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -1566,19 +1566,19 @@ MetroWidget::NotifyIME(const IMENotifica
   switch (aIMENotification.mMessage) {
     case REQUEST_TO_COMMIT_COMPOSITION:
       nsTextStore::CommitComposition(false);
       return NS_OK;
     case REQUEST_TO_CANCEL_COMPOSITION:
       nsTextStore::CommitComposition(true);
       return NS_OK;
     case NOTIFY_IME_OF_FOCUS:
-      return nsTextStore::OnFocusChange(true, this, mInputContext.mIMEState);
+      return nsTextStore::OnFocusChange(true, this, mInputContext);
     case NOTIFY_IME_OF_BLUR:
-      return nsTextStore::OnFocusChange(false, this, mInputContext.mIMEState);
+      return nsTextStore::OnFocusChange(false, this, mInputContext);
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       return nsTextStore::OnSelectionChange();
     case NOTIFY_IME_OF_TEXT_CHANGE:
       return nsTextStore::OnTextChange(aIMENotification);
     case NOTIFY_IME_OF_POSITION_CHANGE:
       return nsTextStore::OnLayoutChange();
     case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
       return nsTextStore::OnMouseButtonEvent(aIMENotification);