From 6eed598db5ec327ba3e708d7ada1b41a564fecbb Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 3 Sep 2014 10:38:20 +0900 Subject: [PATCH] Bug 1052343 part.3 Create TSFStaticSink for listening to some TSF events for whole life time of our process r=emk --- widget/windows/nsTextStore.cpp | 899 +++++++++++++++++++-------------- widget/windows/nsTextStore.h | 47 +- 2 files changed, 512 insertions(+), 434 deletions(-) diff --git a/widget/windows/nsTextStore.cpp b/widget/windows/nsTextStore.cpp index 590f32a6b7e..a276f061ed6 100644 --- a/widget/windows/nsTextStore.cpp +++ b/widget/windows/nsTextStore.cpp @@ -52,106 +52,6 @@ static const char* kPrefNameForceEnableTSF = "intl.tsf.force_enable"; */ PRLogModuleInfo* sTextStoreLog = nullptr; -#endif // #ifdef PR_LOGGING - -/******************************************************************/ -/* InputScopeImpl */ -/******************************************************************/ - -class InputScopeImpl MOZ_FINAL : public ITfInputScope -{ - ~InputScopeImpl() {} - -public: - InputScopeImpl(const nsTArray& aList) - : mInputScopes(aList) - { - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p InputScopeImpl()", this)); - } - - NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl) - - STDMETHODIMP QueryInterface(REFIID riid, void** ppv) - { - *ppv=nullptr; - if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) { - *ppv = static_cast(this); - } - if (*ppv) { - AddRef(); - return S_OK; - } - return E_NOINTERFACE; - } - - STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) - { - uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length()); - - InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count); - NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY); - - if (mInputScopes.IsEmpty()) { - *pScope = IS_DEFAULT; - *pcCount = 1; - *pprgInputScopes = pScope; - return S_OK; - } - - *pcCount = 0; - - for (uint32_t idx = 0; idx < count; idx++) { - *(pScope + idx) = mInputScopes[idx]; - (*pcCount)++; - } - - *pprgInputScopes = pScope; - return S_OK; - } - - STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; } - STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; } - STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; } - STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; } - -private: - nsTArray mInputScopes; -}; - -/******************************************************************/ -/* nsTextStore */ -/******************************************************************/ - -ITfThreadMgr* nsTextStore::sTsfThreadMgr = nullptr; -ITfMessagePump* nsTextStore::sMessagePump = nullptr; -ITfKeystrokeMgr* nsTextStore::sKeystrokeMgr = nullptr; -ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = nullptr; -ITfCategoryMgr* nsTextStore::sCategoryMgr = nullptr; -ITfDocumentMgr* nsTextStore::sTsfDisabledDocumentMgr = nullptr; -ITfContext* nsTextStore::sTsfDisabledContext = nullptr; -ITfInputProcessorProfiles* nsTextStore::sInputProcessorProfiles = nullptr; -DWORD nsTextStore::sTsfClientId = 0; -StaticRefPtr nsTextStore::sEnabledTextStore; - -bool nsTextStore::sCreateNativeCaretForATOK = false; -bool nsTextStore::sDoNotReturnNoLayoutErrorToFreeChangJie = false; -bool nsTextStore::sDoNotReturnNoLayoutErrorToEasyChangjei = false; - -#define TIP_NAME_BEGINS_WITH_ATOK \ - (NS_LITERAL_STRING("ATOK ")) -// NOTE: Free ChangJie 2010 missspells its name... -#define TIP_NAME_FREE_CHANG_JIE_2010 \ - (NS_LITERAL_STRING("Free CangJie IME 10")) -#define TIP_NAME_EASY_CHANGJEI \ - (NS_LITERAL_STRING( \ - "\x4E2D\x6587 (\x7E41\x9AD4) - \x6613\x9821\x8F38\x5165\x6CD5")) - -UINT nsTextStore::sFlushTIPInputMessage = 0; - -#define TEXTSTORE_DEFAULT_VIEW (1) - -#ifdef PR_LOGGING static const char* GetBoolName(bool aBool) @@ -729,43 +629,201 @@ GetModifiersName(Modifiers aModifiers) #endif // #ifdef PR_LOGGING -nsTextStore::nsTextStore() - : mLockedContent(mComposition, mSelection) - , mEditCookie(0) - , mIPProfileCookie(TF_INVALID_COOKIE) +/******************************************************************/ +/* InputScopeImpl */ +/******************************************************************/ + +class InputScopeImpl MOZ_FINAL : public ITfInputScope +{ + ~InputScopeImpl() {} + +public: + InputScopeImpl(const nsTArray& aList) + : mInputScopes(aList) + { + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p InputScopeImpl()", this)); + } + + NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl) + + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) + { + *ppv=nullptr; + if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) { + *ppv = static_cast(this); + } + if (*ppv) { + AddRef(); + return S_OK; + } + return E_NOINTERFACE; + } + + STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) + { + uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length()); + + InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count); + NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY); + + if (mInputScopes.IsEmpty()) { + *pScope = IS_DEFAULT; + *pcCount = 1; + *pprgInputScopes = pScope; + return S_OK; + } + + *pcCount = 0; + + for (uint32_t idx = 0; idx < count; idx++) { + *(pScope + idx) = mInputScopes[idx]; + (*pcCount)++; + } + + *pprgInputScopes = pScope; + return S_OK; + } + + STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; } + STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; } + STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; } + STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; } + +private: + nsTArray mInputScopes; +}; + +/******************************************************************/ +/* TSFStaticSink */ +/******************************************************************/ + +class TSFStaticSink MOZ_FINAL : public ITfActiveLanguageProfileNotifySink + , public ITfInputProcessorProfileActivationSink +{ +public: + static TSFStaticSink* GetInstance() + { + if (!sInstance) { + NS_ADDREF(sInstance = new TSFStaticSink()); + } + return sInstance; + } + + static void Shutdown() + { + if (sInstance) { + sInstance->Destroy(); + NS_RELEASE(sInstance); + } + } + + bool Init(ITfThreadMgr* aThreadMgr, + ITfInputProcessorProfiles* aInputProcessorProfiles); + STDMETHODIMP QueryInterface(REFIID riid, void** ppv) + { + *ppv = nullptr; + if (IID_IUnknown == riid || + IID_ITfActiveLanguageProfileNotifySink == riid) { + *ppv = static_cast(this); + } else if (IID_ITfInputProcessorProfileActivationSink == riid) { + *ppv = static_cast(this); + } + if (*ppv) { + AddRef(); + return S_OK; + } + return E_NOINTERFACE; + } + + NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink) + + const nsString& GetActiveTIPKeyboardDescription() const + { + return mActiveTIPKeyboardDescription; + } + + static bool IsIMM_IME() + { + 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(); + +public: // ITfActiveLanguageProfileNotifySink + STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, + BOOL fActivated); + +public: // ITfInputProcessorProfileActivationSink + STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, + HKL, DWORD); + +private: + TSFStaticSink(); + virtual ~TSFStaticSink() {} + + void Destroy(); + + void GetTIPDescription(REFCLSID aTextService, LANGID aLangID, + REFGUID aProfile, nsAString& aDescription); + bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, + REFGUID aProfile); + + // Cookie of installing ITfInputProcessorProfileActivationSink + DWORD mIPProfileCookie; + // Cookie of installing ITfActiveLanguageProfileNotifySink + DWORD mLangProfileCookie; + + // True if current IME is implemented with IMM. + bool mIsIMM_IME; + // True if OnActivated() is already called + bool mOnActivatedCalled; + + nsRefPtr mThreadMgr; + nsRefPtr mInputProcessorProfiles; + + // Active TIP keyboard's description. If active language profile isn't TIP, + // i.e., IMM-IME or just a keyboard layout, this is empty. + nsString mActiveTIPKeyboardDescription; + + static TSFStaticSink* sInstance; +}; + +TSFStaticSink* TSFStaticSink::sInstance = nullptr; + +TSFStaticSink::TSFStaticSink() + : mIPProfileCookie(TF_INVALID_COOKIE) , mLangProfileCookie(TF_INVALID_COOKIE) - , mSinkMask(0) - , mLock(0) - , mLockQueued(0) - , mRequestedAttrValues(false) - , mIsRecordingActionsWithoutLock(false) - , mPendingOnSelectionChange(false) - , mPendingOnLayoutChange(false) - , mNativeCaretIsCreated(false) , mIsIMM_IME(false) , mOnActivatedCalled(false) { - for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) { - mRequestedAttrs[i] = false; - } - - // We hope that 5 or more actions don't occur at once. - mPendingActions.SetCapacity(5); - - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::nsTestStore() SUCCEEDED", this)); } bool -nsTextStore::Init(ITfThreadMgr* aThreadMgr) +TSFStaticSink::Init(ITfThreadMgr* aThreadMgr, + ITfInputProcessorProfiles* aInputProcessorProfiles) { + MOZ_ASSERT(!mThreadMgr && !mInputProcessorProfiles, + "TSFStaticSink::Init() must be called only once"); + + mThreadMgr = aThreadMgr; + mInputProcessorProfiles = aInputProcessorProfiles; + nsRefPtr source; HRESULT hr = - aThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); + mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED to get ITfSource instance " - "(0x%08X)", this, hr)); + ("TSF: 0x%p TSFStaticSink::Init() FAILED to get ITfSource " + "instance (0x%08X)", this, hr)); return false; } @@ -779,7 +837,7 @@ nsTextStore::Init(ITfThreadMgr* aThreadMgr) &mIPProfileCookie); if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED to install " + ("TSF: 0x%p TSFStaticSink::Init() FAILED to install " "ITfInputProcessorProfileActivationSink (0x%08X)", this, hr)); return false; } @@ -789,43 +847,34 @@ nsTextStore::Init(ITfThreadMgr* aThreadMgr) &mLangProfileCookie); if (FAILED(hr) || mLangProfileCookie == TF_INVALID_COOKIE) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED to install " + ("TSF: 0x%p TSFStaticSink::Init() FAILED to install " "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr)); return false; } } PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::Init(), " + ("TSF: 0x%p TSFStaticSink::Init(), " "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X", this, mIPProfileCookie, mLangProfileCookie)); - return true; } -nsTextStore::~nsTextStore() -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore instance is destroyed", this)); -} - void -nsTextStore::Shutdown() +TSFStaticSink::Destroy() { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::Shutdown() " - "mWidget=0x%p, mDocumentMgr=0x%p, mContext=0x%p, mIPProfileCookie=0x%08X, " - "mLangProfileCookie=0x%08X", - this, mWidget.get(), mDocumentMgr.get(), mContext.get(), - mIPProfileCookie, mLangProfileCookie)); + ("TSF: 0x%p TSFStaticSink::Shutdown() " + "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X", + this, mIPProfileCookie, mLangProfileCookie)); if (mIPProfileCookie != TF_INVALID_COOKIE) { nsRefPtr source; HRESULT hr = - sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); + mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Shutdown() FAILED to get " + ("TSF: 0x%p TSFStaticSink::Shutdown() FAILED to get " "ITfSource instance (0x%08X)", this, hr)); } else { hr = source->UnadviseSink(mIPProfileCookie); @@ -841,21 +890,312 @@ nsTextStore::Shutdown() if (mLangProfileCookie != TF_INVALID_COOKIE) { nsRefPtr source; HRESULT hr = - sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); + mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Shutdown() FAILED to get " + ("TSF: 0x%p TSFStaticSink::Shutdown() FAILED to get " "ITfSource instance (0x%08X)", this, hr)); } else { hr = source->UnadviseSink(mLangProfileCookie); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Shutdown() FAILED to uninstall " + ("TSF: 0x%p TSFStaticSink::Shutdown() FAILED to uninstall " "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr)); } } } + + mThreadMgr = nullptr; + mInputProcessorProfiles = nullptr; +} + +STDMETHODIMP +TSFStaticSink::OnActivated(REFCLSID clsid, REFGUID guidProfile, + BOOL fActivated) +{ + // NOTE: This is installed only on XP or Server 2003. + if (fActivated) { + // TODO: We should check if the profile's category is keyboard or not. + mOnActivatedCalled = true; + mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0)); + + LANGID langID; + HRESULT hr = mInputProcessorProfiles->GetCurrentLanguage(&langID); + if (FAILED(hr)) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: TSFStaticSink::OnActivated() FAILED due to " + "GetCurrentLanguage() failure, hr=0x%08X", hr)); + } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) { + GetTIPDescription(clsid, langID, guidProfile, + mActiveTIPKeyboardDescription); + } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) { + // Perhaps, this case is that keyboard layout without TIP is activated. + mActiveTIPKeyboardDescription.Truncate(); + } + } + + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p TSFStaticSink::OnActivated(rclsid=%s, guidProfile=%s, " + "fActivated=%s), mIsIMM_IME=%s, mActiveTIPDescription=\"%s\"", + this, GetCLSIDNameStr(clsid).get(), + GetGUIDNameStr(guidProfile).get(), GetBoolName(fActivated), + GetBoolName(mIsIMM_IME), + NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get())); + return S_OK; +} + +STDMETHODIMP +TSFStaticSink::OnActivated(DWORD dwProfileType, + LANGID langid, + REFCLSID rclsid, + REFGUID catid, + REFGUID guidProfile, + HKL hkl, + DWORD dwFlags) +{ + // NOTE: This is installed only on Vista or later. However, this may be + // called by EnsureInitActiveLanguageProfile() even on XP or Server + // 2003. + if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) && + (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT || + catid == GUID_TFCAT_TIP_KEYBOARD)) { + mOnActivatedCalled = true; + mIsIMM_IME = IsIMM_IME(hkl); + GetTIPDescription(rclsid, langid, guidProfile, + mActiveTIPKeyboardDescription); + } + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), " + "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, " + "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, " + "mActiveTIPDescription=\"%s\"", + this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ? + "TF_PROFILETYPE_INPUTPROCESSOR" : + dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ? + "TF_PROFILETYPE_KEYBOARDLAYOUT" : "Unknown", dwProfileType, + langid, GetCLSIDNameStr(rclsid).get(), GetGUIDNameStr(catid).get(), + GetGUIDNameStr(guidProfile).get(), hkl, dwFlags, + GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE), + GetBoolName(mIsIMM_IME), + NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get())); + return S_OK; +} + +bool +TSFStaticSink::EnsureInitActiveTIPKeyboard() +{ + if (mOnActivatedCalled) { + return true; + } + + if (IsVistaOrLater()) { + nsRefPtr profileMgr; + HRESULT hr = + mInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr, + getter_AddRefs(profileMgr)); + if (FAILED(hr) || !profileMgr) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " + "to get input processor profile manager, hr=0x%08X", this, hr)); + return false; + } + + TF_INPUTPROCESSORPROFILE profile; + hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); + if (hr == S_FALSE) { + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " + "to get active keyboard layout profile due to no active profile, " + "hr=0x%08X", this, hr)); + // XXX Should we call OnActivated() with arguments like non-TIP in this + // case? + return false; + } + if (FAILED(hr)) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " + "to get active TIP keyboard, hr=0x%08X", this, hr)); + return false; + } + + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), " + "calling OnActivated() manually...", this)); + OnActivated(profile.dwProfileType, profile.langid, profile.clsid, + profile.catid, profile.guidProfile, ::GetKeyboardLayout(0), + TF_IPSINK_FLAG_ACTIVE); + return true; + } + + LANGID langID; + HRESULT hr = mInputProcessorProfiles->GetCurrentLanguage(&langID); + if (FAILED(hr)) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " + "to get current language ID, hr=0x%08X", this, hr)); + return false; + } + + nsRefPtr enumLangProfiles; + hr = mInputProcessorProfiles->EnumLanguageProfiles(langID, + getter_AddRefs(enumLangProfiles)); + if (FAILED(hr) || !enumLangProfiles) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " + "to get language profiles enumerator, hr=0x%08X", this, hr)); + return false; + } + + TF_LANGUAGEPROFILE profile; + ULONG fetch = 0; + while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) { + if (!profile.fActive || profile.catid != GUID_TFCAT_TIP_KEYBOARD) { + continue; + } + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), " + "calling OnActivated() manually...", this)); + bool isTIP = profile.guidProfile != GUID_NULL; + OnActivated(isTIP ? TF_PROFILETYPE_INPUTPROCESSOR : + TF_PROFILETYPE_KEYBOARDLAYOUT, + profile.langid, profile.clsid, profile.catid, + profile.guidProfile, ::GetKeyboardLayout(0), + TF_IPSINK_FLAG_ACTIVE); + return true; + } + + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), " + "calling OnActivated() without active TIP manually...", this)); + OnActivated(TF_PROFILETYPE_KEYBOARDLAYOUT, + langID, CLSID_NULL, GUID_TFCAT_TIP_KEYBOARD, + GUID_NULL, ::GetKeyboardLayout(0), + TF_IPSINK_FLAG_ACTIVE); + return true; +} + +void +TSFStaticSink::GetTIPDescription(REFCLSID aTextService, LANGID aLangID, + REFGUID aProfile, nsAString& aDescription) +{ + aDescription.Truncate(); + + if (aTextService == CLSID_NULL || aProfile == GUID_NULL) { + return; + } + + BSTR description = nullptr; + HRESULT hr = + mInputProcessorProfiles->GetLanguageProfileDescription(aTextService, + aLangID, + aProfile, + &description); + if (FAILED(hr)) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: 0x%p TSFStaticSink::InitActiveTIPDescription() FAILED " + "due to GetLanguageProfileDescription() failure, hr=0x%08X", + this, hr)); + return; + } + + if (description && description[0]) { + aDescription.Assign(description); + } + ::SysFreeString(description); +} + +bool +TSFStaticSink::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, + REFGUID aProfile) +{ + if (aTextService == CLSID_NULL || aProfile == GUID_NULL) { + return false; + } + + nsRefPtr enumLangProfiles; + HRESULT hr = + mInputProcessorProfiles->EnumLanguageProfiles(aLangID, + getter_AddRefs(enumLangProfiles)); + if (FAILED(hr) || !enumLangProfiles) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: 0x%p TSFStaticSink::IsTIPCategoryKeyboard(), FAILED " + "to get language profiles enumerator, hr=0x%08X", this, hr)); + return false; + } + + TF_LANGUAGEPROFILE profile; + ULONG fetch = 0; + while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) { + // XXX We're not sure a profile is registered with two or more categories. + if (profile.clsid == aTextService && + profile.guidProfile == aProfile && + profile.catid == GUID_TFCAT_TIP_KEYBOARD) { + return true; + } + } + return false; +} + +/******************************************************************/ +/* nsTextStore */ +/******************************************************************/ + +ITfThreadMgr* nsTextStore::sTsfThreadMgr = nullptr; +ITfMessagePump* nsTextStore::sMessagePump = nullptr; +ITfKeystrokeMgr* nsTextStore::sKeystrokeMgr = nullptr; +ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = nullptr; +ITfCategoryMgr* nsTextStore::sCategoryMgr = nullptr; +ITfDocumentMgr* nsTextStore::sTsfDisabledDocumentMgr = nullptr; +ITfContext* nsTextStore::sTsfDisabledContext = nullptr; +ITfInputProcessorProfiles* nsTextStore::sInputProcessorProfiles = nullptr; +DWORD nsTextStore::sTsfClientId = 0; +StaticRefPtr nsTextStore::sEnabledTextStore; + +bool nsTextStore::sCreateNativeCaretForATOK = false; +bool nsTextStore::sDoNotReturnNoLayoutErrorToFreeChangJie = false; +bool nsTextStore::sDoNotReturnNoLayoutErrorToEasyChangjei = false; + +#define TIP_NAME_BEGINS_WITH_ATOK \ + (NS_LITERAL_STRING("ATOK ")) +// NOTE: Free ChangJie 2010 missspells its name... +#define TIP_NAME_FREE_CHANG_JIE_2010 \ + (NS_LITERAL_STRING("Free CangJie IME 10")) +#define TIP_NAME_EASY_CHANGJEI \ + (NS_LITERAL_STRING( \ + "\x4E2D\x6587 (\x7E41\x9AD4) - \x6613\x9821\x8F38\x5165\x6CD5")) + +UINT nsTextStore::sFlushTIPInputMessage = 0; + +#define TEXTSTORE_DEFAULT_VIEW (1) + +nsTextStore::nsTextStore() + : mLockedContent(mComposition, mSelection) + , mEditCookie(0) + , mSinkMask(0) + , mLock(0) + , mLockQueued(0) + , mRequestedAttrValues(false) + , mIsRecordingActionsWithoutLock(false) + , mPendingOnSelectionChange(false) + , mPendingOnLayoutChange(false) + , mNativeCaretIsCreated(false) +{ + for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) { + mRequestedAttrs[i] = false; + } + + // We hope that 5 or more actions don't occur at once. + mPendingActions.SetCapacity(5); + + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p nsTextStore::nsTestStore() SUCCEEDED", this)); +} + +nsTextStore::~nsTextStore() +{ + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: 0x%p nsTextStore instance is destroyed", this)); } bool @@ -865,7 +1205,7 @@ nsTextStore::Create(nsWindowBase* aWidget) ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)", this, aWidget)); - EnsureInitActiveTIPKeyboard(); + TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard(); if (mDocumentMgr) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, @@ -974,10 +1314,6 @@ nsTextStore::QueryInterface(REFIID riid, *ppv = static_cast(this); } else if (IID_ITfContextOwnerCompositionSink == riid) { *ppv = static_cast(this); - } else if (IID_ITfActiveLanguageProfileNotifySink == riid) { - *ppv = static_cast(this); - } else if (IID_ITfInputProcessorProfileActivationSink == riid) { - *ppv = static_cast(this); } else if (IID_ITfMouseTrackerACP == riid) { *ppv = static_cast(this); } @@ -2818,10 +3154,12 @@ nsTextStore::GetTextExt(TsViewCookie vcView, // 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. // TODO: On Win 9, we need to check this hack is still necessary. + const nsString& activeTIPKeyboardDescription = + TSFStaticSink::GetInstance()->GetActiveTIPKeyboardDescription(); if ((sDoNotReturnNoLayoutErrorToFreeChangJie && - mActiveTIPKeyboardDescription.Equals(TIP_NAME_FREE_CHANG_JIE_2010)) || + activeTIPKeyboardDescription.Equals(TIP_NAME_FREE_CHANG_JIE_2010)) || (sDoNotReturnNoLayoutErrorToEasyChangjei && - mActiveTIPKeyboardDescription.Equals(TIP_NAME_EASY_CHANGJEI)) && + activeTIPKeyboardDescription.Equals(TIP_NAME_EASY_CHANGJEI)) && mComposition.IsComposing() && mLockedContent.IsLayoutChangedAfter(acpEnd) && mComposition.mStart < acpEnd) { @@ -2897,7 +3235,7 @@ nsTextStore::GetTextExt(TsViewCookie vcView, // for hacking the bug. if (sCreateNativeCaretForATOK && StringBeginsWith( - mActiveTIPKeyboardDescription, TIP_NAME_BEGINS_WITH_ATOK) && + activeTIPKeyboardDescription, TIP_NAME_BEGINS_WITH_ATOK) && mComposition.IsComposing() && mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart && mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) { @@ -3484,78 +3822,6 @@ nsTextStore::OnEndComposition(ITfCompositionView* pComposition) return S_OK; } -STDMETHODIMP -nsTextStore::OnActivated(REFCLSID clsid, REFGUID guidProfile, - BOOL fActivated) -{ - // NOTE: This is installed only on XP or Server 2003. - if (fActivated) { - // TODO: We should check if the profile's category is keyboard or not. - mOnActivatedCalled = true; - mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0)); - - LANGID langID; - HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID); - if (FAILED(hr)) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: nsTextStore::OnActivated() FAILED due to " - "GetCurrentLanguage() failure, hr=0x%08X", hr)); - } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) { - GetTIPDescription(clsid, langID, guidProfile, - mActiveTIPKeyboardDescription); - } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) { - // Perhaps, this case is that keyboard layout without TIP is activated. - mActiveTIPKeyboardDescription.Truncate(); - } - } - - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::OnActivated(rclsid=%s, guidProfile=%s, " - "fActivated=%s), mIsIMM_IME=%s, mActiveTIPDescription=\"%s\"", - this, GetCLSIDNameStr(clsid).get(), - GetGUIDNameStr(guidProfile).get(), GetBoolName(fActivated), - GetBoolName(mIsIMM_IME), - NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get())); - return S_OK; -} - -STDMETHODIMP -nsTextStore::OnActivated(DWORD dwProfileType, - LANGID langid, - REFCLSID rclsid, - REFGUID catid, - REFGUID guidProfile, - HKL hkl, - DWORD dwFlags) -{ - // NOTE: This is installed only on Vista or later. However, this may be - // called by EnsureInitActiveLanguageProfile() even on XP or Server - // 2003. - if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) && - (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT || - catid == GUID_TFCAT_TIP_KEYBOARD)) { - mOnActivatedCalled = true; - mIsIMM_IME = IsIMM_IME(hkl); - GetTIPDescription(rclsid, langid, guidProfile, - mActiveTIPKeyboardDescription); - } - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::OnActivated(dwProfileType=%s (0x%08X), " - "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, " - "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, " - "mActiveTIPDescription=\"%s\"", - this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ? - "TF_PROFILETYPE_INPUTPROCESSOR" : - dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ? - "TF_PROFILETYPE_KEYBOARDLAYOUT" : "Unknown", dwProfileType, - langid, GetCLSIDNameStr(rclsid).get(), GetGUIDNameStr(catid).get(), - GetGUIDNameStr(guidProfile).get(), hkl, dwFlags, - GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE), - GetBoolName(mIsIMM_IME), - NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get())); - return S_OK; -} - STDMETHODIMP nsTextStore::AdviseMouseSink(ITfRangeACP* range, ITfMouseSink* pSink, @@ -3994,99 +4260,6 @@ nsTextStore::CreateNativeCaret() ::SetCaretPos(caretRect.x, caretRect.y); } -bool -nsTextStore::EnsureInitActiveTIPKeyboard() -{ - if (mOnActivatedCalled) { - return true; - } - - if (IsVistaOrLater()) { - nsRefPtr profileMgr; - HRESULT hr = - sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr, - getter_AddRefs(profileMgr)); - if (FAILED(hr) || !profileMgr) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " - "to get input processor profile manager, hr=0x%08X", this, hr)); - return false; - } - - TF_INPUTPROCESSORPROFILE profile; - hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); - if (hr == S_FALSE) { - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " - "to get active keyboard layout profile due to no active profile, " - "hr=0x%08X", this, hr)); - // XXX Should we call OnActivated() with arguments like non-TIP in this - // case? - return false; - } - if (FAILED(hr)) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " - "to get active TIP keyboard, hr=0x%08X", this, hr)); - return false; - } - - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), " - "calling OnActivated() manually...", this)); - OnActivated(profile.dwProfileType, profile.langid, profile.clsid, - profile.catid, profile.guidProfile, ::GetKeyboardLayout(0), - TF_IPSINK_FLAG_ACTIVE); - return true; - } - - LANGID langID; - HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID); - if (FAILED(hr)) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " - "to get current language ID, hr=0x%08X", this, hr)); - return false; - } - - nsRefPtr enumLangProfiles; - hr = sInputProcessorProfiles->EnumLanguageProfiles(langID, - getter_AddRefs(enumLangProfiles)); - if (FAILED(hr) || !enumLangProfiles) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " - "to get language profiles enumerator, hr=0x%08X", this, hr)); - return false; - } - - TF_LANGUAGEPROFILE profile; - ULONG fetch = 0; - while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) { - if (!profile.fActive || profile.catid != GUID_TFCAT_TIP_KEYBOARD) { - continue; - } - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), " - "calling OnActivated() manually...", this)); - bool isTIP = profile.guidProfile != GUID_NULL; - OnActivated(isTIP ? TF_PROFILETYPE_INPUTPROCESSOR : - TF_PROFILETYPE_KEYBOARDLAYOUT, - profile.langid, profile.clsid, profile.catid, - profile.guidProfile, ::GetKeyboardLayout(0), - TF_IPSINK_FLAG_ACTIVE); - return true; - } - - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), " - "calling OnActivated() without active TIP manually...", this)); - OnActivated(TF_PROFILETYPE_KEYBOARDLAYOUT, - langID, CLSID_NULL, GUID_TFCAT_TIP_KEYBOARD, - GUID_NULL, ::GetKeyboardLayout(0), - TF_IPSINK_FLAG_ACTIVE); - return true; -} - void nsTextStore::CommitCompositionInternal(bool aDiscard) { @@ -4283,69 +4456,6 @@ nsTextStore::MarkContextAsEmpty(ITfContext* aContext) comp->SetValue(sTsfClientId, &variant_int4_value1); } -// static -bool -nsTextStore::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, - REFGUID aProfile) -{ - if (aTextService == CLSID_NULL || aProfile == GUID_NULL) { - return false; - } - - nsRefPtr enumLangProfiles; - HRESULT hr = - sInputProcessorProfiles->EnumLanguageProfiles(aLangID, - getter_AddRefs(enumLangProfiles)); - if (FAILED(hr) || !enumLangProfiles) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: nsTextStore::IsTIPCategoryKeyboard(), FAILED " - "to get language profiles enumerator, hr=0x%08X", hr)); - return false; - } - - TF_LANGUAGEPROFILE profile; - ULONG fetch = 0; - while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) { - // XXX We're not sure a profile is registered with two or more categories. - if (profile.clsid == aTextService && - profile.guidProfile == aProfile && - profile.catid == GUID_TFCAT_TIP_KEYBOARD) { - return true; - } - } - return false; -} - -// static -void -nsTextStore::GetTIPDescription(REFCLSID aTextService, LANGID aLangID, - REFGUID aProfile, nsAString& aDescription) -{ - aDescription.Truncate(); - - if (aTextService == CLSID_NULL || aProfile == GUID_NULL) { - return; - } - - BSTR description = nullptr; - HRESULT hr = - sInputProcessorProfiles->GetLanguageProfileDescription(aTextService, - aLangID, - aProfile, - &description); - if (FAILED(hr)) { - PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: nsTextStore::InitActiveTIPDescription() FAILED due to " - "GetLanguageProfileDescription() failure, hr=0x%08X", hr)); - return; - } - - if (description && description[0]) { - aDescription.Assign(description); - } - ::SysFreeString(description); -} - // static void nsTextStore::Initialize() @@ -4478,15 +4588,21 @@ nsTextStore::Initialize() PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Initialize() is creating " - "an nsTextStore instance...")); - nsRefPtr textStore = new nsTextStore(); - if (!textStore->Init(threadMgr)) { + "a TSFStaticSink instance...")); + TSFStaticSink* staticSink = TSFStaticSink::GetInstance(); + if (!staticSink->Init(threadMgr, inputProcessorProfiles)) { + TSFStaticSink::Shutdown(); PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: nsTextStore::Initialize() FAILED to initialize nsTextStore " + ("TSF: nsTextStore::Initialize() FAILED to initialize TSFStaticSink " "instance")); return; } + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, + ("TSF: nsTextStore::Initialize() is creating " + "an nsTextStore instance...")); + nsRefPtr textStore = new nsTextStore(); + inputProcessorProfiles.swap(sInputProcessorProfiles); threadMgr.swap(sTsfThreadMgr); messagePump.swap(sMessagePump); @@ -4529,9 +4645,7 @@ nsTextStore::Terminate(void) { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Terminate()")); - if (sEnabledTextStore) { - sEnabledTextStore->Shutdown(); - } + TSFStaticSink::Shutdown(); NS_IF_RELEASE(sDisplayAttrMgr); NS_IF_RELEASE(sCategoryMgr); @@ -4605,6 +4719,13 @@ nsTextStore::ProcessMessage(nsWindowBase* aWindow, UINT aMessage, } } +// static +bool +nsTextStore::IsIMM_IME() +{ + return TSFStaticSink::IsIMM_IME(); +} + /******************************************************************/ /* nsTextStore::Composition */ /******************************************************************/ diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index a57b95e6ee8..1739b942376 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -41,6 +41,7 @@ class nsWindow; #ifdef MOZ_METRO class MetroWidget; #endif +class TSFStaticSink; namespace mozilla { namespace widget { @@ -54,8 +55,6 @@ struct MSGResult; class nsTextStore MOZ_FINAL : public ITextStoreACP , public ITfContextOwnerCompositionSink - , public ITfActiveLanguageProfileNotifySink - , public ITfInputProcessorProfileActivationSink , public ITfMouseTrackerACP { public: /*IUnknown*/ @@ -101,14 +100,6 @@ public: /*ITfContextOwnerCompositionSink*/ STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*); STDMETHODIMP OnEndComposition(ITfCompositionView*); -public: /*ITfActiveLanguageProfileNotifySink*/ - STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, - BOOL fActivated); - -public: /*ITfInputProcessorProfileActivationSink*/ - STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, - HKL, DWORD); - public: /*ITfMouseTrackerACP*/ STDMETHODIMP AdviseMouseSink(ITfRangeACP*, ITfMouseSink*, DWORD*); STDMETHODIMP UnadviseMouseSink(DWORD); @@ -224,19 +215,7 @@ public: return (IsComposing() && sEnabledTextStore->mWidget == aWidget); } - static bool IsIMM_IME() - { - if (!sEnabledTextStore || - !sEnabledTextStore->EnsureInitActiveTIPKeyboard()) { - return IsIMM_IME(::GetKeyboardLayout(0)); - } - return sEnabledTextStore->mIsIMM_IME; - } - - static bool IsIMM_IME(HKL aHKL) - { - return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0); - } + static bool IsIMM_IME(); #ifdef DEBUG // Returns true when keyboard layout has IME (TIP). @@ -247,17 +226,9 @@ protected: nsTextStore(); ~nsTextStore(); - bool Init(ITfThreadMgr* aThreadMgr); - void Shutdown(); - static void MarkContextAsKeyboardDisabled(ITfContext* aContext); static void MarkContextAsEmpty(ITfContext* aContext); - static bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, - REFGUID aProfile); - static void GetTIPDescription(REFCLSID aTextService, LANGID aLangID, - REFGUID aProfile, nsAString& aDescription); - bool Create(nsWindowBase* aWidget); bool Destroy(void); @@ -318,18 +289,12 @@ protected: // application. Otherwise, this does nothing. void CreateNativeCaret(); - bool EnsureInitActiveTIPKeyboard(); - // Holds the pointer to our current win32 or metro widget nsRefPtr mWidget; // Document manager for the currently focused editor nsRefPtr mDocumentMgr; // Edit cookie associated with the current editing context DWORD mEditCookie; - // Cookie of installing ITfInputProcessorProfileActivationSink - DWORD mIPProfileCookie; - // Cookie of installing ITfActiveLanguageProfileNotifySink - DWORD mLangProfileCookie; // Editing context at the bottom of mDocumentMgr's context stack nsRefPtr mContext; // Currently installed notification sink @@ -340,9 +305,6 @@ protected: DWORD mLock; // 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock DWORD mLockQueued; - // Active TIP keyboard's description. If active language profile isn't TIP, - // i.e., IMM-IME or just a keyboard layout, this is empty. - nsString mActiveTIPKeyboardDescription; class Composition MOZ_FINAL { @@ -771,11 +733,6 @@ protected: // While there is native caret, this is true. Otherwise, false. bool mNativeCaretIsCreated; - // True if current IME is implemented with IMM. - bool mIsIMM_IME; - // True if OnActivated() is already called - bool mOnActivatedCalled; - // TSF thread manager object for the current application static ITfThreadMgr* sTsfThreadMgr; // sMessagePump is QI'ed from sTsfThreadMgr