From a98cdf9c4deb6ddea8127ddc165ef84c3203036a Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 2 Sep 2014 09:41:48 +0900 Subject: [PATCH] Backout bug 1052343 because 7b7c24a989fa hasn't been reviewed yet --- widget/windows/WinIMEHandler.cpp | 10 +- widget/windows/nsTextStore.cpp | 1104 +++++++++++--------------- widget/windows/nsTextStore.h | 102 ++- widget/windows/winrt/MetroWidget.cpp | 5 +- 4 files changed, 539 insertions(+), 682 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 6c489e7064a..40b7ad5e3b1 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -169,10 +169,10 @@ IMEHandler::NotifyIME(nsWindow* aWindow, return nsTextStore::OnTextChange(aIMENotification); case NOTIFY_IME_OF_FOCUS: return nsTextStore::OnFocusChange(true, aWindow, - aWindow->GetInputContext()); + aWindow->GetInputContext().mIMEState); case NOTIFY_IME_OF_BLUR: return nsTextStore::OnFocusChange(false, aWindow, - aWindow->GetInputContext()); + aWindow->GetInputContext().mIMEState); case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: return nsTextStore::OnMouseButtonEvent(aIMENotification); case REQUEST_TO_COMMIT_COMPOSITION: @@ -210,7 +210,7 @@ IMEHandler::NotifyIME(nsWindow* aWindow, // the blur. if (nsTextStore::ThinksHavingFocus()) { return nsTextStore::OnFocusChange(false, aWindow, - aWindow->GetInputContext()); + aWindow->GetInputContext().mIMEState); } return NS_ERROR_NOT_IMPLEMENTED; #endif //NS_ENABLE_TSF @@ -289,7 +289,7 @@ IMEHandler::SetInputContext(nsWindow* aWindow, if (sIsInTSFMode) { nsTextStore::SetInputContext(aWindow, aInputContext, aAction); if (IsTSFAvailable()) { - aInputContext.mNativeIMEContext = nsTextStore::GetThreadManager(); + aInputContext.mNativeIMEContext = nsTextStore::GetTextStore(); if (sIsIMMEnabled) { // Associate IME context for IMM-IMEs. AssociateIMEContext(aWindow, enable); @@ -354,7 +354,7 @@ IMEHandler::InitInputContext(nsWindow* aWindow, InputContext& aInputContext) nsTextStore::SetInputContext(aWindow, aInputContext, InputContextAction(InputContextAction::CAUSE_UNKNOWN, InputContextAction::GOT_FOCUS)); - aInputContext.mNativeIMEContext = nsTextStore::GetThreadManager(); + aInputContext.mNativeIMEContext = nsTextStore::GetTextStore(); MOZ_ASSERT(aInputContext.mNativeIMEContext); // IME context isn't necessary in pure TSF mode. if (!sIsIMMEnabled) { diff --git a/widget/windows/nsTextStore.cpp b/widget/windows/nsTextStore.cpp index 3a9af420cb4..3902b1619e3 100644 --- a/widget/windows/nsTextStore.cpp +++ b/widget/windows/nsTextStore.cpp @@ -52,6 +52,106 @@ 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) @@ -629,201 +729,43 @@ GetModifiersName(Modifiers aModifiers) #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; -}; - -/******************************************************************/ -/* 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) +nsTextStore::nsTextStore() + : mLockedContent(mComposition, mSelection) + , mEditCookie(0) + , 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 -TSFStaticSink::Init(ITfThreadMgr* aThreadMgr, - ITfInputProcessorProfiles* aInputProcessorProfiles) +nsTextStore::Init(ITfThreadMgr* aThreadMgr) { - MOZ_ASSERT(!mThreadMgr && !mInputProcessorProfiles, - "TSFStaticSink::Init() must be called only once"); - - mThreadMgr = aThreadMgr; - mInputProcessorProfiles = aInputProcessorProfiles; - nsRefPtr source; HRESULT hr = - mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); + aThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p TSFStaticSink::Init() FAILED to get ITfSource " - "instance (0x%08X)", this, hr)); + ("TSF: 0x%p nsTextStore::Init() FAILED to get ITfSource instance " + "(0x%08X)", this, hr)); return false; } @@ -837,7 +779,7 @@ TSFStaticSink::Init(ITfThreadMgr* aThreadMgr, &mIPProfileCookie); if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p TSFStaticSink::Init() FAILED to install " + ("TSF: 0x%p nsTextStore::Init() FAILED to install " "ITfInputProcessorProfileActivationSink (0x%08X)", this, hr)); return false; } @@ -847,34 +789,43 @@ TSFStaticSink::Init(ITfThreadMgr* aThreadMgr, &mLangProfileCookie); if (FAILED(hr) || mLangProfileCookie == TF_INVALID_COOKIE) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p TSFStaticSink::Init() FAILED to install " + ("TSF: 0x%p nsTextStore::Init() FAILED to install " "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr)); return false; } } PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p TSFStaticSink::Init(), " + ("TSF: 0x%p nsTextStore::Init(), " "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X", this, mIPProfileCookie, mLangProfileCookie)); + return true; } -void -TSFStaticSink::Destroy() +nsTextStore::~nsTextStore() { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p TSFStaticSink::Shutdown() " - "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X", - this, mIPProfileCookie, mLangProfileCookie)); + ("TSF: 0x%p nsTextStore instance is destroyed", this)); +} + +void +nsTextStore::Shutdown() +{ + 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)); if (mIPProfileCookie != TF_INVALID_COOKIE) { nsRefPtr source; HRESULT hr = - mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); + sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p TSFStaticSink::Shutdown() FAILED to get " + ("TSF: 0x%p nsTextStore::Shutdown() FAILED to get " "ITfSource instance (0x%08X)", this, hr)); } else { hr = source->UnadviseSink(mIPProfileCookie); @@ -890,325 +841,35 @@ TSFStaticSink::Destroy() if (mLangProfileCookie != TF_INVALID_COOKIE) { nsRefPtr source; HRESULT hr = - mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); + sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p TSFStaticSink::Shutdown() FAILED to get " + ("TSF: 0x%p nsTextStore::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 TSFStaticSink::Shutdown() FAILED to uninstall " + ("TSF: 0x%p nsTextStore::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")) - -#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) - , mPendingDestroy(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() +nsTextStore::Create(nsWindowBase* aWidget) { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore instance is destroyed", this)); -} - -bool -nsTextStore::Init(nsWindowBase* aWidget) -{ - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::Init(aWidget=0x%p)", + ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)", this, aWidget)); - TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard(); + EnsureInitActiveTIPKeyboard(); if (mDocumentMgr) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED due to already initialized", + ("TSF: 0x%p nsTextStore::Create() FAILED due to already initialized", this)); return false; } @@ -1218,7 +879,7 @@ nsTextStore::Init(nsWindowBase* aWidget) getter_AddRefs(mDocumentMgr)); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED to create DocumentMgr " + ("TSF: 0x%p nsTextStore::Create() FAILED to create DocumentMgr " "(0x%08X)", this, hr)); return false; } @@ -1230,7 +891,7 @@ nsTextStore::Init(nsWindowBase* aWidget) getter_AddRefs(mContext), &mEditCookie); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED to create the context " + ("TSF: 0x%p nsTextStore::Create() FAILED to create the context " "(0x%08X)", this, hr)); mDocumentMgr = nullptr; return false; @@ -1239,7 +900,7 @@ nsTextStore::Init(nsWindowBase* aWidget) hr = mDocumentMgr->Push(mContext); if (FAILED(hr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: 0x%p nsTextStore::Init() FAILED to push the context (0x%08X)", + ("TSF: 0x%p nsTextStore::Create() FAILED to push the context (0x%08X)", this, hr)); // XXX Why don't we use NS_IF_RELEASE() here?? mContext = nullptr; @@ -1248,7 +909,7 @@ nsTextStore::Init(nsWindowBase* aWidget) } PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::Init() succeeded: " + ("TSF: 0x%p nsTextStore::Create() succeeded: " "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X", this, mDocumentMgr.get(), mContext.get(), mEditCookie)); @@ -1256,18 +917,11 @@ nsTextStore::Init(nsWindowBase* aWidget) } bool -nsTextStore::Destroy() +nsTextStore::Destroy(void) { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, - ("TSF: 0x%p nsTextStore::Destroy(), mLock=%s, " - "mComposition.IsComposing()=%s", - this, GetLockFlagNameStr(mLock).get(), - GetBoolName(mComposition.IsComposing()))); - - if (mLock) { - mPendingDestroy = true; - return true; - } + ("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. @@ -1279,6 +933,18 @@ nsTextStore::Destroy() mLockedContent.Clear(); mSelection.MarkDirty(); + if (mWidget) { + // When blurred, Tablet Input Panel posts "blur" messages + // and try to insert text when the message is retrieved later. + // But by that time the text store is already destroyed, + // so try to get the message early + MSG msg; + if (WinUtils::PeekMessage(&msg, mWidget->GetWindowHandle(), + sFlushTIPInputMessage, sFlushTIPInputMessage, + PM_REMOVE)) { + ::DispatchMessageW(&msg); + } + } mContext = nullptr; if (mDocumentMgr) { mDocumentMgr->Pop(TF_POPF_ALL); @@ -1308,6 +974,10 @@ 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); } @@ -1438,9 +1108,6 @@ nsTextStore::RequestLock(DWORD dwLockFlags, ("TSF: 0x%p Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", this, GetLockFlagNameStr(mLock).get())); - // Don't release this instance during this lock because this is called by - // TSF but they don't grab us during this call. - nsRefPtr kungFuDeathGrip(this); *phrSession = mSink->OnLockGranted(mLock); PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: 0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" @@ -1465,7 +1132,7 @@ nsTextStore::RequestLock(DWORD dwLockFlags, // The document is now completely unlocked. mLock = 0; - if (!mPendingDestroy && mPendingOnLayoutChange) { + if (mPendingOnLayoutChange) { mPendingOnLayoutChange = false; if (mSink) { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, @@ -1489,7 +1156,7 @@ nsTextStore::RequestLock(DWORD dwLockFlags, } } - if (!mPendingDestroy && mPendingOnSelectionChange) { + if (mPendingOnSelectionChange) { mPendingOnSelectionChange = false; if (mSink) { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, @@ -1499,10 +1166,6 @@ nsTextStore::RequestLock(DWORD dwLockFlags, } } - if (mPendingDestroy) { - Destroy(); - } - PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: 0x%p nsTextStore::RequestLock() succeeded: *phrSession=%s", this, GetTextStoreReturnValueName(*phrSession))); @@ -3155,12 +2818,10 @@ 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 && - activeTIPKeyboardDescription.Equals(TIP_NAME_FREE_CHANG_JIE_2010)) || + mActiveTIPKeyboardDescription.Equals(TIP_NAME_FREE_CHANG_JIE_2010)) || (sDoNotReturnNoLayoutErrorToEasyChangjei && - activeTIPKeyboardDescription.Equals(TIP_NAME_EASY_CHANGJEI)) && + mActiveTIPKeyboardDescription.Equals(TIP_NAME_EASY_CHANGJEI)) && mComposition.IsComposing() && mLockedContent.IsLayoutChangedAfter(acpEnd) && mComposition.mStart < acpEnd) { @@ -3236,7 +2897,7 @@ nsTextStore::GetTextExt(TsViewCookie vcView, // for hacking the bug. if (sCreateNativeCaretForATOK && StringBeginsWith( - activeTIPKeyboardDescription, TIP_NAME_BEGINS_WITH_ATOK) && + mActiveTIPKeyboardDescription, TIP_NAME_BEGINS_WITH_ATOK) && mComposition.IsComposing() && mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart && mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) { @@ -3823,6 +3484,78 @@ 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, @@ -3925,122 +3658,57 @@ nsTextStore::UnadviseMouseSink(DWORD dwCookie) nsresult nsTextStore::OnFocusChange(bool aGotFocus, nsWindowBase* aFocusedWidget, - const InputContext& aContext) + const IMEState& aIMEState) { PR_LOG(sTextStoreLog, PR_LOG_DEBUG, ("TSF: nsTextStore::OnFocusChange(aGotFocus=%s, " - "aFocusedWidget=0x%p, aContext={ mIMEState={ mEnabled=%s }, " - "mHTMLInputType=\"%s\" }), " + "aFocusedWidget=0x%p, aIMEState={ mEnabled=%s }), " "sTsfThreadMgr=0x%p, sEnabledTextStore=0x%p", GetBoolName(aGotFocus), aFocusedWidget, - GetIMEEnabledName(aContext.mIMEState.mEnabled), - NS_ConvertUTF16toUTF8(aContext.mHTMLInputType).get(), + GetIMEEnabledName(aIMEState.mEnabled), sTsfThreadMgr, sEnabledTextStore)); - if (NS_WARN_IF(!IsInTSFMode())) { - return NS_ERROR_NOT_AVAILABLE; - } + // no change notifications if TSF is disabled + NS_ENSURE_TRUE(sTsfThreadMgr && sEnabledTextStore, NS_ERROR_NOT_AVAILABLE); - // If currently sEnableTextStore has focus, notifies TSF of losing focus. - if (ThinksHavingFocus()) { - nsRefPtr prevFocusedDocumentMgr; - DebugOnly 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; + nsRefPtr 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 topContext; + sEnabledTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext)); + if (topContext && topContext != sEnabledTextStore->mContext) { + MarkContextAsKeyboardDisabled(topContext); + } } - 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) { + 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 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"); sEnabledTextStore->Destroy(); - NS_RELEASE(sEnabledTextStore); } - return NS_ERROR_FAILURE; + HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr); + NS_ENSURE_TRUE(SUCCEEDED(hr), 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 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 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() @@ -4326,6 +3994,99 @@ 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) { @@ -4447,26 +4208,24 @@ nsTextStore::SetInputContext(nsWindowBase* aWidget, PR_LOG(sTextStoreLog, PR_LOG_DEBUG, ("TSF: nsTextStore::SetInputContext(aWidget=%p, " "aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), " - "sEnabledTextStore=0x%p, ThinksHavingFocus()=%s", + "ThinksHavingFocus()=%s", aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled), - GetFocusChangeName(aAction.mFocusChange), sEnabledTextStore, + GetFocusChangeName(aAction.mFocusChange), GetBoolName(ThinksHavingFocus()))); - NS_ENSURE_TRUE_VOID(IsInTSFMode()); + NS_ENSURE_TRUE_VOID(sEnabledTextStore); + 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); + OnFocusChange(true, aWidget, aContext.mIMEState); } else if (ThinksHavingFocus() && !aContext.mIMEState.IsEditable()) { - OnFocusChange(false, aWidget, aContext); + OnFocusChange(false, aWidget, aContext.mIMEState); } } @@ -4519,6 +4278,69 @@ 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() @@ -4651,12 +4473,11 @@ nsTextStore::Initialize() PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Initialize() is creating " - "a TSFStaticSink instance...")); - TSFStaticSink* staticSink = TSFStaticSink::GetInstance(); - if (!staticSink->Init(threadMgr, inputProcessorProfiles)) { - TSFStaticSink::Shutdown(); + "an nsTextStore instance...")); + nsRefPtr textStore = new nsTextStore(); + if (!textStore->Init(threadMgr)) { PR_LOG(sTextStoreLog, PR_LOG_ERROR, - ("TSF: nsTextStore::Initialize() FAILED to initialize TSFStaticSink " + ("TSF: nsTextStore::Initialize() FAILED to initialize nsTextStore " "instance")); return; } @@ -4669,6 +4490,7 @@ nsTextStore::Initialize() categoryMgr.swap(sCategoryMgr); disabledDocumentMgr.swap(sTsfDisabledDocumentMgr); disabledContext.swap(sTsfDisabledContext); + sEnabledTextStore = textStore; sCreateNativeCaretForATOK = Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true); @@ -4679,14 +4501,17 @@ nsTextStore::Initialize() Preferences::GetBool( "intl.tsf.hack.easy_changjei.do_not_return_no_layout_error", true); + MOZ_ASSERT(!sFlushTIPInputMessage); + sFlushTIPInputMessage = ::RegisterWindowMessageW(L"Flush TIP Input Message"); + PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Initialize(), sTsfThreadMgr=0x%p, " - "sTsfClientId=0x%08X, sDisplayAttrMgr=0x%p, " + "sTsfClientId=0x%08X, sEnabledTextStore=0x%p, sDisplayAttrMgr=0x%p, " "sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p, " "sCreateNativeCaretForATOK=%s, " "sDoNotReturnNoLayoutErrorToFreeChangJie=%s, " "sDoNotReturnNoLayoutErrorToEasyChangjei=%s", - sTsfThreadMgr, sTsfClientId, sDisplayAttrMgr, + sTsfThreadMgr, sTsfClientId, sEnabledTextStore, sDisplayAttrMgr, sCategoryMgr, sTsfDisabledDocumentMgr, sTsfDisabledContext, GetBoolName(sCreateNativeCaretForATOK), GetBoolName(sDoNotReturnNoLayoutErrorToFreeChangJie), @@ -4699,7 +4524,9 @@ nsTextStore::Terminate(void) { PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Terminate()")); - TSFStaticSink::Shutdown(); + if (sEnabledTextStore) { + sEnabledTextStore->Shutdown(); + } NS_IF_RELEASE(sDisplayAttrMgr); NS_IF_RELEASE(sCategoryMgr); @@ -4773,13 +4600,6 @@ 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 d6782f3385a..e429c92801d 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -41,7 +41,6 @@ class nsWindow; #ifdef MOZ_METRO class MetroWidget; #endif -class TSFStaticSink; namespace mozilla { namespace widget { @@ -55,6 +54,8 @@ struct MSGResult; class nsTextStore MOZ_FINAL : public ITextStoreACP , public ITfContextOwnerCompositionSink + , public ITfActiveLanguageProfileNotifySink + , public ITfInputProcessorProfileActivationSink , public ITfMouseTrackerACP { public: /*IUnknown*/ @@ -100,6 +101,14 @@ 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); @@ -125,10 +134,8 @@ public: static void CommitComposition(bool aDiscard) { - NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called"); - if (sEnabledTextStore) { - sEnabledTextStore->CommitCompositionInternal(aDiscard); - } + NS_ENSURE_TRUE_VOID(sEnabledTextStore); + sEnabledTextStore->CommitCompositionInternal(aDiscard); } static void SetInputContext(nsWindowBase* aWidget, @@ -137,33 +144,31 @@ public: static nsresult OnFocusChange(bool aGotFocus, nsWindowBase* aFocusedWidget, - const InputContext& aContext); + const IMEState& aIMEState); static nsresult OnTextChange(const IMENotification& aIMENotification) { - NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called"); - return sEnabledTextStore ? - sEnabledTextStore->OnTextChangeInternal(aIMENotification) : NS_OK; + NS_ENSURE_TRUE(sEnabledTextStore, NS_ERROR_NOT_AVAILABLE); + return sEnabledTextStore->OnTextChangeInternal(aIMENotification); } static nsresult OnSelectionChange(void) { - NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called"); - return sEnabledTextStore ? - sEnabledTextStore->OnSelectionChangeInternal() : NS_OK; + NS_ENSURE_TRUE(sEnabledTextStore, NS_ERROR_NOT_AVAILABLE); + return sEnabledTextStore->OnSelectionChangeInternal(); } static nsresult OnLayoutChange() { - NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called"); - return sEnabledTextStore ? - sEnabledTextStore->OnLayoutChangeInternal() : NS_OK; + NS_ENSURE_TRUE(sEnabledTextStore, NS_ERROR_NOT_AVAILABLE); + return sEnabledTextStore->OnLayoutChangeInternal(); } static nsresult OnMouseButtonEvent(const IMENotification& aIMENotification) { - NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called"); - return sEnabledTextStore ? - sEnabledTextStore->OnMouseButtonEventInternal(aIMENotification) : NS_OK; + if (NS_WARN_IF(!sEnabledTextStore)) { + return NS_ERROR_NOT_AVAILABLE; + } + return sEnabledTextStore->OnMouseButtonEventInternal(aIMENotification); } static nsIMEUpdatePreference GetIMEUpdatePreference(); @@ -190,32 +195,44 @@ public: return sMessagePump; } - static void* GetThreadManager() + static void* GetTextStore() { - return static_cast(sTsfThreadMgr); + return static_cast(sEnabledTextStore); } - static bool ThinksHavingFocus() + static bool ThinksHavingFocus() { return (sEnabledTextStore && sEnabledTextStore->mContext); } - static bool IsInTSFMode() + static bool IsInTSFMode() { return sTsfThreadMgr != nullptr; } - static bool IsComposing() + static bool IsComposing() { return (sEnabledTextStore && sEnabledTextStore->mComposition.IsComposing()); } - static bool IsComposingOn(nsWindowBase* aWidget) + static bool IsComposingOn(nsWindowBase* aWidget) { return (IsComposing() && sEnabledTextStore->mWidget == aWidget); } - static bool IsIMM_IME(); + 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); + } #ifdef DEBUG // Returns true when keyboard layout has IME (TIP). @@ -226,13 +243,19 @@ protected: nsTextStore(); ~nsTextStore(); - static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget, - const InputContext& aContext); + bool Init(ITfThreadMgr* aThreadMgr); + void Shutdown(); + static void MarkContextAsKeyboardDisabled(ITfContext* aContext); static void MarkContextAsEmpty(ITfContext* aContext); - bool Init(nsWindowBase* aWidget); - bool Destroy(); + 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); bool IsReadLock(DWORD aLock) const { @@ -291,12 +314,18 @@ 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 @@ -307,6 +336,9 @@ 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 { @@ -732,12 +764,14 @@ protected: // ITfContextOwnerServices::OnLayoutChange() after the layout is fixed and // the document is unlocked. bool mPendingOnLayoutChange; - // During the documet is locked, we shouldn't destroy the instance. - // If this is true, the instance will be destroyed after unlocked. - bool mPendingDestroy; // 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 @@ -767,6 +801,10 @@ protected: static bool sCreateNativeCaretForATOK; static bool sDoNotReturnNoLayoutErrorToFreeChangJie; static bool sDoNotReturnNoLayoutErrorToEasyChangjei; + + // Message the Tablet Input Panel uses to flush text during blurring. + // See comments in Destroy + static UINT sFlushTIPInputMessage; }; #endif /*NSTEXTSTORE_H_*/ diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index bb200fdf909..e1b35e07adf 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -1542,7 +1542,6 @@ NS_IMETHODIMP_(void) MetroWidget::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { - // XXX This should set mInputContext.mNativeIMEContext properly mInputContext = aContext; nsTextStore::SetInputContext(this, mInputContext, aAction); bool enable = (mInputContext.mIMEState.mEnabled == IMEState::ENABLED || @@ -1571,9 +1570,9 @@ MetroWidget::NotifyIME(const IMENotification& aIMENotification) nsTextStore::CommitComposition(true); return NS_OK; case NOTIFY_IME_OF_FOCUS: - return nsTextStore::OnFocusChange(true, this, mInputContext); + return nsTextStore::OnFocusChange(true, this, mInputContext.mIMEState); case NOTIFY_IME_OF_BLUR: - return nsTextStore::OnFocusChange(false, this, mInputContext); + return nsTextStore::OnFocusChange(false, this, mInputContext.mIMEState); case NOTIFY_IME_OF_SELECTION_CHANGE: return nsTextStore::OnSelectionChange(); case NOTIFY_IME_OF_TEXT_CHANGE: