Bug 1369695 Creating ITfInputProcessorProfiles and TSFStaticSink when they are used at first time r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 08 Jun 2017 23:55:57 +0900
changeset 591041 82bf7d8cd01e3ab9b34552540e5d91a06a074881
parent 590941 f8357367411e867a5c6880173dd9e9539a0a8cf3
child 632405 f2f85f1560e83921ebcd15bc8fb4e21108e5869f
push id62934
push usermasayuki@d-toybox.com
push dateThu, 08 Jun 2017 14:57:29 +0000
reviewersm_kato
bugs1369695
milestone55.0a1
Bug 1369695 Creating ITfInputProcessorProfiles and TSFStaticSink when they are used at first time r?m_kato ITfProcessorProfiles are used by a debug method TSFTextStore::CurrentKeyboardLayoutHasIME() and TSFStaticSink (when it's initialized). However, TSFStaticSink isn't necessary until when TSFTextStore needs to hack something for specific IME or notifying IMEHandler of active TIP change. So, we can put off to create the instance of ITfInputProcessorProfiles and TSFStaticSink. MozReview-Commit-ID: KcrqUbqz1do
widget/windows/TSFTextStore.cpp
widget/windows/TSFTextStore.h
widget/windows/WinIMEHandler.cpp
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -996,17 +996,40 @@ private:
 /******************************************************************/
 
 class TSFStaticSink final : public ITfInputProcessorProfileActivationSink
 {
 public:
   static TSFStaticSink* GetInstance()
   {
     if (!sInstance) {
-      sInstance = new TSFStaticSink();
+      RefPtr<ITfThreadMgr> threadMgr = TSFTextStore::GetThreadMgr();
+      if (NS_WARN_IF(!threadMgr)) {
+        MOZ_LOG(sTextStoreLog, LogLevel::Error,
+          ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
+           "instance due to no ThreadMgr instance"));
+        return nullptr;
+      }
+      RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
+        TSFTextStore::GetInputProcessorProfiles();
+      if (NS_WARN_IF(!inputProcessorProfiles)) {
+        MOZ_LOG(sTextStoreLog, LogLevel::Error,
+          ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
+           "instance due to no InputProcessorProfiles instance"));
+        return nullptr;
+      }
+      RefPtr<TSFStaticSink> staticSink = new TSFStaticSink();
+      if (NS_WARN_IF(!staticSink->Init(threadMgr, inputProcessorProfiles))) {
+        staticSink->Destroy();
+        MOZ_LOG(sTextStoreLog, LogLevel::Error,
+          ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
+           "instance"));
+        return nullptr;
+      }
+      sInstance = staticSink.forget();
     }
     return sInstance;
   }
 
   static void Shutdown()
   {
     if (sInstance) {
       sInstance->Destroy();
@@ -1034,203 +1057,245 @@ public:
 
   const nsString& GetActiveTIPKeyboardDescription() const
   {
     return mActiveTIPKeyboardDescription;
   }
 
   static bool IsIMM_IMEActive()
   {
+    // Use IMM API until TSFStaticSink starts to work.
     if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
       return IsIMM_IME(::GetKeyboardLayout(0));
     }
     return sInstance->mIsIMM_IME;
   }
 
   static bool IsIMM_IME(HKL aHKL)
   {
      return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0);
   }
 
-  bool EnsureInitActiveTIPKeyboard();
-
+#define DECL_AND_IMPL_IS_TIP_ACTIVE(aMethod)                                   \
+  static bool aMethod()                                                        \
+  {                                                                            \
+    RefPtr<TSFStaticSink> staticSink = GetInstance();                          \
+    if (NS_WARN_IF(!staticSink) ||                                             \
+        NS_WARN_IF(!staticSink->EnsureInitActiveTIPKeyboard())) {              \
+      return false;                                                            \
+    }                                                                          \
+    return staticSink->aMethod ## Internal();                                  \
+  }
+
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSJapaneseIMEActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSOfficeJapaneseIME2010Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOKActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2011Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2012Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2013Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2014Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2015Active)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsATOK2016Active)
+
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSChangJieActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSQuickQuickActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsFreeChangJieActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsEasyChangjeiActive)
+
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSPinyinActive)
+  DECL_AND_IMPL_IS_TIP_ACTIVE(IsMSWubiActive)
+
+#undef DECL_AND_IMPL_IS_TIP_ACTIVE
+
+  // Note that ATOK 2011 - 2016 refers native caret position for deciding its
+  // popup window position.
+  static bool IsATOKReferringNativeCaretActive()
+  {
+    RefPtr<TSFStaticSink> staticSink = GetInstance();
+    if (NS_WARN_IF(!staticSink) ||
+        NS_WARN_IF(!staticSink->EnsureInitActiveTIPKeyboard())) {
+      return false;
+    }
+    return staticSink->IsATOKActiveInternal() &&
+           (staticSink->IsATOK2011ActiveInternal() ||
+            staticSink->IsATOK2012ActiveInternal() ||
+            staticSink->IsATOK2013ActiveInternal() ||
+            staticSink->IsATOK2014ActiveInternal() ||
+            staticSink->IsATOK2015ActiveInternal() ||
+            staticSink->IsATOK2016ActiveInternal());
+  }
+
+private:
   /****************************************************************************
    * Japanese TIP
    ****************************************************************************/
 
   // Note that TIP name may depend on the language of the environment.
   // For example, some TIP may use localized name for its target language
   // environment but English name for the others.
 
-  bool IsMSJapaneseIMEActive() const
+  bool IsMSJapaneseIMEActiveInternal() const
   {
     // FYI: Name of MS-IME for Japanese is same as MS-IME for Korean.
     //      Therefore, we need to check the langid too.
     return mLangID == 0x411 &&
       (mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft IME") ||
        mActiveTIPKeyboardDescription.Equals(
          NS_LITERAL_STRING(u"Microsoft \xC785\xB825\xAE30")) ||
        mActiveTIPKeyboardDescription.Equals(
          NS_LITERAL_STRING(u"\x5FAE\x8F6F\x8F93\x5165\x6CD5")) ||
        mActiveTIPKeyboardDescription.Equals(
          NS_LITERAL_STRING(u"\x5FAE\x8EDF\x8F38\x5165\x6CD5")));
   }
 
-  bool IsMSOfficeJapaneseIME2010Active() const
+  bool IsMSOfficeJapaneseIME2010ActiveInternal() const
   {
     // {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
     static const GUID kGUID = {
       0x54EDCC94, 0x1524, 0x4BB1,
         { 0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOKActive() const
+  bool IsATOKActiveInternal() const
   {
     // FYI: Name of ATOK includes the release year like "ATOK 2015".
     return StringBeginsWith(mActiveTIPKeyboardDescription,
                             NS_LITERAL_STRING("ATOK "));
   }
 
-  bool IsATOK2011Active() const
+  bool IsATOK2011ActiveInternal() const
   {
     // {F9C24A5C-8A53-499D-9572-93B2FF582115}
     static const GUID kGUID = {
       0xF9C24A5C, 0x8A53, 0x499D,
         { 0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2012Active() const
+  bool IsATOK2012ActiveInternal() const
   {
     // {1DE01562-F445-401B-B6C3-E5B18DB79461}
     static const GUID kGUID = {
       0x1DE01562, 0xF445, 0x401B,
         { 0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2013Active() const
+  bool IsATOK2013ActiveInternal() const
   {
     // {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
     static const GUID kGUID = {
       0x3C4DB511, 0x189A, 0x4168,
         { 0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15 }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2014Active() const
+  bool IsATOK2014ActiveInternal() const
   {
     // {4EF33B79-6AA9-4271-B4BF-9321C279381B}
     static const GUID kGUID = {
       0x4EF33B79, 0x6AA9, 0x4271,
         { 0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2015Active() const
+  bool IsATOK2015ActiveInternal() const
   {
     // {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
     static const GUID kGUID = {
       0xEAB4DC00, 0xCE2E, 0x483D,
         { 0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  bool IsATOK2016Active() const
+  bool IsATOK2016ActiveInternal() const
   {
     // {0B557B4C-5740-4110-A60A-1493FA10BF2B}
     static const GUID kGUID = {
       0x0B557B4C, 0x5740, 0x4110,
         { 0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B }
     };
     return mActiveTIPGUID == kGUID;
   }
 
-  // Note that ATOK 2011 - 2016 refers native caret position for deciding its
-  // popup window position.
-  bool IsATOKReferringNativeCaretActive() const
-  {
-    return IsATOKActive() &&
-           (IsATOK2011Active() || IsATOK2012Active() || IsATOK2013Active() ||
-            IsATOK2014Active() || IsATOK2015Active() || IsATOK2016Active());
-  }
-
   /****************************************************************************
    * Traditional Chinese TIP
    ****************************************************************************/
 
-  bool IsMSChangJieActive() const
+  bool IsMSChangJieActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft ChangJie") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x4ED3\x9889")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x5009\x9821"));
   }
 
-  bool IsMSQuickQuickActive() const
+  bool IsMSQuickQuickActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Quick") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x901F\x6210")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x901F\x6210"));
   }
 
-  bool IsFreeChangJieActive() const
+  bool IsFreeChangJieActiveInternal() const
   {
     // FYI: The TIP name is misspelled...
     return mActiveTIPKeyboardDescription.EqualsLiteral("Free CangJie IME 10");
   }
 
-  bool IsEasyChangjeiActive() const
+  bool IsEasyChangjeiActiveInternal() const
   {
     return
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(
           u"\x4E2D\x6587 (\x7E41\x9AD4) - \x6613\x9821\x8F38\x5165\x6CD5"));
   }
 
   /****************************************************************************
    * Simplified Chinese TIP
    ****************************************************************************/
 
-  bool IsMSPinyinActive() const
+  bool IsMSPinyinActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Pinyin") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x62FC\x97F3")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x62FC\x97F3"));
   }
 
-  bool IsMSWubiActive() const
+  bool IsMSWubiActiveInternal() const
   {
     return mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft Wubi") ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8F6F\x4E94\x7B14")) ||
       mActiveTIPKeyboardDescription.Equals(
         NS_LITERAL_STRING(u"\x5FAE\x8EDF\x4E94\x7B46"));
   }
 
 public: // ITfInputProcessorProfileActivationSink
   STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID,
                            HKL, DWORD);
 
 private:
   TSFStaticSink();
   virtual ~TSFStaticSink() {}
 
+  bool EnsureInitActiveTIPKeyboard();
+
   void Destroy();
 
   void GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
                          REFGUID aProfile, nsAString& aDescription);
   bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
                              REFGUID aProfile);
 
   // Cookie of installing ITfInputProcessorProfileActivationSink
@@ -1558,18 +1623,16 @@ TSFTextStore::Init(nsWindowBase* aWidget
   if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED due to being initialized with "
        "destroyed widget",
        this));
     return false;
   }
 
-  TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard();
-
   if (mDocumentMgr) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::Init() FAILED due to already initialized",
        this));
     return false;
   }
 
   mWidget = aWidget;
@@ -2288,22 +2351,23 @@ TSFTextStore::QueryInsert(LONG acpTestSt
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::QueryInsert() FAILED due to "
        "wrong argument", this));
     return E_INVALIDARG;
   }
 
   // XXX need to adjust to cluster boundary
   // Assume we are given good offsets for now
-  const TSFStaticSink* kSink = TSFStaticSink::GetInstance();
   if (IsWin8OrLater() && !mComposition.IsComposing() &&
       ((sHackQueryInsertForMSTraditionalTIP &&
-         (kSink->IsMSChangJieActive() || kSink->IsMSQuickQuickActive())) ||
+         (TSFStaticSink::IsMSChangJieActive() ||
+          TSFStaticSink::IsMSQuickQuickActive())) ||
        (sHackQueryInsertForMSSimplifiedTIP &&
-         (kSink->IsMSPinyinActive() || kSink->IsMSWubiActive())))) {
+         (TSFStaticSink::IsMSPinyinActive() ||
+          TSFStaticSink::IsMSWubiActive())))) {
     MOZ_LOG(sTextStoreLog, LogLevel::Warning,
       ("0x%p   TSFTextStore::QueryInsert() WARNING using different "
        "result for the TIP", this));
     // Chinese TIPs of Microsoft assume that QueryInsert() returns selected
     // range which should be removed.
     *pacpResultStart = acpTestStart;
     *pacpResultEnd = acpTestEnd;
   } else {
@@ -3972,35 +4036,34 @@ TSFTextStore::GetTextExt(TsViewCookie vc
   mWaitingQueryLayout = false;
 
   // NOTE: TSF (at least on Win 8.1) doesn't return TS_E_NOLAYOUT to the
   // caller even if we return it.  It's converted to just E_FAIL.
   // However, this is fixed on Win 10.
 
   bool dontReturnNoLayoutError = false;
 
-  const TSFStaticSink* kSink = TSFStaticSink::GetInstance();
   if (mComposition.IsComposing() && mComposition.mStart < acpEnd &&
       mContentForTSF.IsLayoutChangedAt(acpEnd)) {
     const Selection& selectionForTSF = SelectionForTSFRef();
     // The bug of Microsoft Office IME 2010 for Japanese is similar to
     // MS-IME for Win 8.1 and Win 10.  Newer version of MS Office IME is not
     // released yet.  So, we can hack it without prefs  because there must be
     // no developers who want to disable this hack for tests.
     const bool kIsMSOfficeJapaneseIME2010 =
-      kSink->IsMSOfficeJapaneseIME2010Active();
+      TSFStaticSink::IsMSOfficeJapaneseIME2010Active();
     // MS IME for Japanese doesn't support asynchronous handling at deciding
     // its suggest list window position.  The feature was implemented
     // starting from Windows 8.  And also we may meet same trouble in e10s
     // mode on Win7.  So, we should never return TS_E_NOLAYOUT to MS IME for
     // Japanese.
     if (kIsMSOfficeJapaneseIME2010 ||
         ((sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar ||
           sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret) &&
-         kSink->IsMSJapaneseIMEActive())) {
+         TSFStaticSink::IsMSJapaneseIMEActive())) {
       // Basically, MS-IME tries to retrieve whole composition string rect
       // at deciding suggest window immediately after unlocking the document.
       // However, in e10s mode, the content hasn't updated yet in most cases.
       // Therefore, if the first character at the retrieving range rect is
       // available, we should use it as the result.
       if ((kIsMSOfficeJapaneseIME2010 ||
            sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar) &&
           acpStart < acpEnd) {
@@ -4034,44 +4097,44 @@ TSFTextStore::GetTextExt(TsViewCookie vc
     // suggest window.  In such case, ATOK tries to query rect of whole
     // composition string.
     // XXX For testing with legacy ATOK, we should hack it even if current ATOK
     //     refers native caret rect on windows whose window class is one of
     //     Mozilla window classes and we stop creating native caret for ATOK
     //     because creating native caret causes ATOK refers caret position
     //     when GetTextExt() returns TS_E_NOLAYOUT.
     else if (sDoNotReturnNoLayoutErrorToATOKOfCompositionString &&
-             kSink->IsATOKActive() &&
-             (!kSink->IsATOKReferringNativeCaretActive() ||
+             TSFStaticSink::IsATOKActive() &&
+             (!TSFStaticSink::IsATOKReferringNativeCaretActive() ||
               !sCreateNativeCaretForLegacyATOK) &&
              mComposition.mStart == acpStart &&
              mComposition.EndOffset() == acpEnd) {
       dontReturnNoLayoutError = true;
     }
     // Free ChangJie 2010 and Easy Changjei 1.0.12.0 doesn't handle
     // ITfContextView::GetTextExt() properly.  Prehaps, it's due to the bug of
     // TSF.  We need to check if this is necessary on Windows 10 before
     // disabling this on Windows 10.
     else if ((sDoNotReturnNoLayoutErrorToFreeChangJie &&
-              kSink->IsFreeChangJieActive()) ||
+              TSFStaticSink::IsFreeChangJieActive()) ||
              (sDoNotReturnNoLayoutErrorToEasyChangjei &&
-              kSink->IsEasyChangjeiActive())) {
+              TSFStaticSink::IsEasyChangjeiActive())) {
       acpEnd = mComposition.mStart;
       acpStart = std::min(acpStart, acpEnd);
       dontReturnNoLayoutError = true;
     }
     // Some Chinese TIPs of Microsoft doesn't show candidate window in e10s
     // mode on Win8 or later.
     else if (IsWin8OrLater() &&
              ((sDoNotReturnNoLayoutErrorToMSTraditionalTIP &&
-               (kSink->IsMSChangJieActive() ||
-                kSink->IsMSQuickQuickActive())) ||
+               (TSFStaticSink::IsMSChangJieActive() ||
+                TSFStaticSink::IsMSQuickQuickActive())) ||
               (sDoNotReturnNoLayoutErrorToMSSimplifiedTIP &&
-                (kSink->IsMSPinyinActive() ||
-                 kSink->IsMSWubiActive())))) {
+                (TSFStaticSink::IsMSPinyinActive() ||
+                 TSFStaticSink::IsMSWubiActive())))) {
       acpEnd = mComposition.mStart;
       acpStart = std::min(acpStart, acpEnd);
       dontReturnNoLayoutError = true;
     }
 
     // If we hack the queried range for active TIP, that means we should not
     // return TS_E_NOLAYOUT even if hacked offset is still modified.  So, as
     // far as possible, we should adjust the offset.
@@ -4211,17 +4274,17 @@ TSFTextStore::GetTextExt(TsViewCookie vc
   // not equal if text rect was clipped
   *pfClipped = !::EqualRect(prc, &textRect);
 
   // ATOK 2011 - 2016 refers native caret position and size on windows whose
   // class name is one of Mozilla's windows for deciding candidate window
   // position.  Therefore, we need to create native caret only when ATOK 2011 -
   // 2016 is active.
   if (sCreateNativeCaretForLegacyATOK &&
-      kSink->IsATOKReferringNativeCaretActive() &&
+      TSFStaticSink::IsATOKReferringNativeCaretActive() &&
       mComposition.IsComposing() &&
       mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart &&
       mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) {
     CreateNativeCaret();
   }
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p   TSFTextStore::GetTextExt() succeeded: "
@@ -6008,37 +6071,20 @@ TSFTextStore::Initialize()
   bool enableTsf = Preferences::GetBool(kPrefNameEnableTSF, false);
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("  TSFTextStore::Initialize(), TSF is %s",
      enableTsf ? "enabled" : "disabled"));
   if (!enableTsf) {
     return;
   }
 
-  // XXX MSDN documents that ITfInputProcessorProfiles is available only on
-  //     desktop apps.  However, there is no known way to obtain
-  //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
-  //     instance.
-  RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
-  HRESULT hr =
-    ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
-                       CLSCTX_INPROC_SERVER,
-                       IID_ITfInputProcessorProfiles,
-                       getter_AddRefs(inputProcessorProfiles));
-  if (FAILED(hr) || !inputProcessorProfiles) {
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("  TSFTextStore::Initialize() FAILED to create input processor "
-       "profiles, hr=0x%08X", hr));
-    return;
-  }
-
   RefPtr<ITfThreadMgr> threadMgr;
-  hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
-                          CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
-                          getter_AddRefs(threadMgr));
+  HRESULT hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
+                                  CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
+                                  getter_AddRefs(threadMgr));
   if (FAILED(hr) || !threadMgr) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::Initialize() FAILED to "
        "create the thread manager, hr=0x%08X", hr));
     return;
   }
 
   RefPtr<ITfMessagePump> messagePump;
@@ -6087,29 +6133,16 @@ TSFTextStore::Initialize()
       ("  TSFTextStore::Initialize() FAILED to create "
        "a context for disabled mode, hr=0x%08X", hr));
     return;
   }
 
   MarkContextAsKeyboardDisabled(disabledContext);
   MarkContextAsEmpty(disabledContext);
 
-  MOZ_LOG(sTextStoreLog, LogLevel::Info,
-    ("  TSFTextStore::Initialize() is creating "
-     "a TSFStaticSink instance..."));
-  TSFStaticSink* staticSink = TSFStaticSink::GetInstance();
-  if (!staticSink->Init(threadMgr, inputProcessorProfiles)) {
-    TSFStaticSink::Shutdown();
-    MOZ_LOG(sTextStoreLog, LogLevel::Error,
-      ("  TSFTextStore::Initialize() FAILED to initialize TSFStaticSink "
-       "instance"));
-    return;
-  }
-
-  sInputProcessorProfiles = inputProcessorProfiles;
   sThreadMgr = threadMgr;
   sMessagePump = messagePump;
   sKeystrokeMgr = keystrokeMgr;
   sDisabledDocumentMgr = disabledDocumentMgr;
   sDisabledContext = disabledContext;
 
   sCreateNativeCaretForLegacyATOK =
     Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true);
@@ -6161,16 +6194,24 @@ TSFTextStore::Initialize()
      GetBoolName(sDoNotReturnNoLayoutErrorToATOKOfCompositionString),
      GetBoolName(sDoNotReturnNoLayoutErrorToFreeChangJie),
      GetBoolName(sDoNotReturnNoLayoutErrorToEasyChangjei),
      GetBoolName(sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar),
      GetBoolName(sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret)));
 }
 
 // static
+already_AddRefed<ITfThreadMgr>
+TSFTextStore::GetThreadMgr()
+{
+  RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
+  return threadMgr.forget();
+}
+
+// static
 already_AddRefed<ITfDisplayAttributeMgr>
 TSFTextStore::GetDisplayAttributeMgr()
 {
   RefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
   if (sDisplayAttrMgr) {
     displayAttributeMgr = sDisplayAttrMgr;
     return displayAttributeMgr.forget();
   }
@@ -6208,16 +6249,44 @@ TSFTextStore::GetCategoryMgr()
        "a category manager instance, hr=0x%08X", hr));
     return nullptr;
   }
   sCategoryMgr = categoryMgr;
   return categoryMgr.forget();
 }
 
 // static
+already_AddRefed<ITfInputProcessorProfiles>
+TSFTextStore::GetInputProcessorProfiles()
+{
+  RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
+  if (sInputProcessorProfiles) {
+    inputProcessorProfiles = sInputProcessorProfiles;
+    return inputProcessorProfiles.forget();
+  }
+  // XXX MSDN documents that ITfInputProcessorProfiles is available only on
+  //     desktop apps.  However, there is no known way to obtain
+  //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
+  //     instance.
+  HRESULT hr =
+    ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
+                       CLSCTX_INPROC_SERVER,
+                       IID_ITfInputProcessorProfiles,
+                       getter_AddRefs(inputProcessorProfiles));
+  if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!inputProcessorProfiles)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+      ("TSFTextStore::GetInputProcessorProfiles() FAILED to create input "
+       "processor profiles, hr=0x%08X", hr));
+    return nullptr;
+  }
+  sInputProcessorProfiles = inputProcessorProfiles;
+  return inputProcessorProfiles.forget();
+}
+
+// static
 void
 TSFTextStore::Terminate()
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Info, ("TSFTextStore::Terminate()"));
 
   TSFStaticSink::Shutdown();
 
   sDisplayAttrMgr = nullptr;
@@ -6324,17 +6393,17 @@ TSFTextStore::IsIMM_IMEActive()
 {
   return TSFStaticSink::IsIMM_IMEActive();
 }
 
 // static
 bool
 TSFTextStore::IsMSJapaneseIMEActive()
 {
-  return TSFStaticSink::GetInstance()->IsMSJapaneseIMEActive();
+  return TSFStaticSink::IsMSJapaneseIMEActive();
 }
 
 /******************************************************************/
 /* TSFTextStore::Composition                                       */
 /******************************************************************/
 
 void
 TSFTextStore::Composition::Start(ITfCompositionView* aCompositionView,
@@ -6643,26 +6712,28 @@ TSFTextStore::MouseTracker::OnMouseButto
   return SUCCEEDED(hr) && eaten;
 }
 
 #ifdef DEBUG
 // static
 bool
 TSFTextStore::CurrentKeyboardLayoutHasIME()
 {
-  if (!sInputProcessorProfiles) {
+  RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
+    TSFTextStore::GetInputProcessorProfiles();
+  if (!inputProcessorProfiles) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED due to "
        "there is no input processor profiles instance"));
     return false;
   }
   RefPtr<ITfInputProcessorProfileMgr> profileMgr;
   HRESULT hr =
-    sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
-                                            getter_AddRefs(profileMgr));
+    inputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
+                                           getter_AddRefs(profileMgr));
   if (FAILED(hr) || !profileMgr) {
     // On Windows Vista or later, ImmIsIME() API always returns true.
     // If we failed to obtain the profile manager, we cannot know if current
     // keyboard layout has IME.
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("  TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
        "ITfInputProcessorProfileMgr"));
     return false;
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -41,26 +41,28 @@ struct ITfThreadMgr;
 struct ITfDocumentMgr;
 struct ITfDisplayAttributeMgr;
 struct ITfCategoryMgr;
 class nsWindow;
 
 namespace mozilla {
 namespace widget {
 
+class TSFStaticSink;
 struct MSGResult;
 
 /*
  * Text Services Framework text store
  */
 
 class TSFTextStore final : public ITextStoreACP
                          , public ITfContextOwnerCompositionSink
                          , public ITfMouseTrackerACP
 {
+  friend class TSFStaticSink;
 private:
   typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
   typedef IMENotification::SelectionChangeData SelectionChangeData;
   typedef IMENotification::TextChangeDataBase TextChangeDataBase;
   typedef IMENotification::TextChangeData TextChangeData;
 
 public: /*IUnknown*/
   STDMETHODIMP          QueryInterface(REFIID, void**);
@@ -997,16 +999,17 @@ protected:
   bool                         mDestroyed;
   // While the instance is being destroyed, this is set to true for avoiding
   // recursive Destroy() calls.
   bool                         mBeingDestroyed;
 
 
   // TSF thread manager object for the current application
   static StaticRefPtr<ITfThreadMgr> sThreadMgr;
+  static already_AddRefed<ITfThreadMgr> GetThreadMgr();
   // sMessagePump is QI'ed from sThreadMgr
   static StaticRefPtr<ITfMessagePump> sMessagePump;
   // sKeystrokeMgr is QI'ed from sThreadMgr
   static StaticRefPtr<ITfKeystrokeMgr> sKeystrokeMgr;
   // TSF display attribute manager
   static StaticRefPtr<ITfDisplayAttributeMgr> sDisplayAttrMgr;
   static already_AddRefed<ITfDisplayAttributeMgr> GetDisplayAttributeMgr();
   // TSF category manager
@@ -1019,16 +1022,18 @@ protected:
   // when the focused editor is blurred.
   static StaticRefPtr<TSFTextStore> sEnabledTextStore;
 
   // For IME (keyboard) disabled state:
   static StaticRefPtr<ITfDocumentMgr> sDisabledDocumentMgr;
   static StaticRefPtr<ITfContext> sDisabledContext;
 
   static StaticRefPtr<ITfInputProcessorProfiles> sInputProcessorProfiles;
+  static already_AddRefed<ITfInputProcessorProfiles>
+           GetInputProcessorProfiles();
 
   // TSF client ID for the current application
   static DWORD sClientId;
 
   // Enables/Disables hack for specific TIP.
   static bool sCreateNativeCaretForLegacyATOK;
   static bool sDoNotReturnNoLayoutErrorToATOKOfCompositionString;
   static bool sDoNotReturnNoLayoutErrorToMSSimplifiedTIP;
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -174,17 +174,17 @@ IMEHandler::ProcessMessage(nsWindow* aWi
       return true;
     }
     // If we don't support IMM in TSF mode, we don't use IMMHandler.
     if (!sIsIMMEnabled) {
       return false;
     }
     // IME isn't implemented with IMM, IMMHandler shouldn't handle any
     // messages.
-    if (!TSFTextStore::IsIMM_IMEActive()) {
+    if (!IsIMMActive()) {
       return false;
     }
   }
 #endif // #ifdef NS_ENABLE_TSF
 
   return IMMHandler::ProcessMessage(aWindow, aMessage, aWParam, aLParam,
                                     aResult);
 }
@@ -416,17 +416,17 @@ IMEHandler::OnDestroyWindow(nsWindow* aW
 }
 
 #ifdef NS_ENABLE_TSF
 // static
 bool
 IMEHandler::NeedsToAssociateIMC()
 {
   if (sAssociateIMCOnlyWhenIMM_IMEActive) {
-    return TSFTextStore::IsIMM_IMEActive();
+    return IsIMMActive();
   }
 
   // Even if IMC should be associated with focused widget with non-IMM-IME,
   // we need to avoid crash bug of MS-IME for Japanese on Win10.  It crashes
   // while we're associating default IME to a window when it's active.
   static const bool sDoNotAssociateIMCWhenMSJapaneseIMEActiveOnWin10 =
     IsWin10OrLater() &&
     Preferences::GetBool(
@@ -551,16 +551,21 @@ IMEHandler::CurrentKeyboardLayoutHasIME(
   return IMMHandler::IsIMEAvailable();
 }
 #endif // #ifdef DEBUG
 
 // static
 void
 IMEHandler::OnKeyboardLayoutChanged()
 {
+  // Be aware, this method won't be called until TSFStaticSink starts to
+  // observe active TIP change.  If you need to be notified of this, you
+  // need to create TSFStaticSink::Observe() or something and call it
+  // TSFStaticSink::EnsureInitActiveTIPKeyboard() forcibly.
+
   if (!sIsIMMEnabled || !IsTSFAvailable()) {
     return;
   }
 
   // If there is no TSFTextStore which has focus, i.e., no editor has focus,
   // nothing to do here.
   nsWindowBase* windowBase = TSFTextStore::GetEnabledWindowBase();
   if (!windowBase) {