/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <algorithm> #include <comutil.h> // for _bstr_t #include <oleauto.h> // for SysAllocString #include <olectl.h>
// For collecting other people's log, tell `MOZ_LOG=IMEHandler:4,sync` // rather than `MOZ_LOG=IMEHandler:5,sync` since using `5` may create too // big file. // Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
mozilla::LazyLogModule gIMELog("IMEHandler");
// TODO: GUID_PROP_URL has not been declared in the SDK yet. We should drop the // `s` prefix after it's released by a new SDK and define it with #if. staticconst GUID sGUID_PROP_URL = {
0xd5138268,
0xa1bf,
0x4308,
{0xbc, 0xbf, 0x2e, 0x73, 0x93, 0x98, 0xe2, 0x34}};
namespace mozilla { namespace widget {
/** * TSF related code should log its behavior even on release build especially * in the interface methods. * * In interface methods, use LogLevel::Info. * In internal methods, use LogLevel::Debug for logging normal behavior. * For logging error, use LogLevel::Error. * * When an instance method is called, start with following text: * "0x%p TSFFoo::Bar(", the 0x%p should be the "this" of the nsFoo. * after that, start with: * "0x%p TSFFoo::Bar(" * In an internal method, start with following text: * "0x%p TSFFoo::Bar(" * When a static method is called, start with following text: * "TSFFoo::Bar("
*/
enumclass TextInputProcessorID { // Internal use only. This won't be returned by TSFStaticSink::ActiveTIP().
eNotComputed,
// Not a TIP. E.g., simple keyboard layout or IMM-IME.
eNone,
// Used for other TIPs, i.e., any TIPs which we don't support specifically.
eUnknown,
// TIP for Japanese.
eMicrosoftIMEForJapanese,
eMicrosoftOfficeIME2010ForJapanese,
eGoogleJapaneseInput,
eATOK2011,
eATOK2012,
eATOK2013,
eATOK2014,
eATOK2015,
eATOK2016,
eATOKUnknown,
eJapanist10,
// TIP for Traditional Chinese.
eMicrosoftBopomofo,
eMicrosoftChangJie,
eMicrosoftPhonetic,
eMicrosoftQuick,
eMicrosoftNewChangJie,
eMicrosoftNewPhonetic,
eMicrosoftNewQuick,
eFreeChangJie,
// TIP for Simplified Chinese.
eMicrosoftPinyin,
eMicrosoftPinyinNewExperienceInputStyle,
eMicrosoftWubi,
// TIP for Korean.
eMicrosoftIMEForKorean,
eMicrosoftOldHangul,
// Keyman Desktop, which can install various language keyboards.
eKeymanDesktop,
};
staticconst nsCString GetFindFlagName(DWORD aFindFlag) {
nsCString description; if (!aFindFlag) {
description.AppendLiteral("no flags (0)"); return description;
} if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
} if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
} if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
} if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
} if (aFindFlag & TS_ATTR_FIND_WANT_END) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_WANT_END");
} if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
HandleSeparator(description);
description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
} if (description.IsEmpty()) {
description.AppendLiteral("Unknown (");
description.AppendInt(static_cast<uint32_t>(aFindFlag));
description.Append(')');
} return description;
}
class GetACPFromPointFlagName : public nsAutoCString { public: explicit GetACPFromPointFlagName(DWORD aFlags) { if (!aFlags) {
AppendLiteral("no flags (0)"); return;
} if (aFlags & GXFPF_ROUND_NEAREST) {
AppendLiteral("GXFPF_ROUND_NEAREST");
aFlags &= ~GXFPF_ROUND_NEAREST;
} if (aFlags & GXFPF_NEAREST) {
HandleSeparator(*this);
AppendLiteral("GXFPF_NEAREST");
aFlags &= ~GXFPF_NEAREST;
} if (aFlags) {
HandleSeparator(*this);
AppendLiteral("Unknown(");
AppendInt(static_cast<uint32_t>(aFlags));
Append(')');
}
} virtual ~GetACPFromPointFlagName() {}
};
staticconstchar* GetFocusChangeName(
InputContextAction::FocusChange aFocusChange) { switch (aFocusChange) { case InputContextAction::FOCUS_NOT_CHANGED: return"FOCUS_NOT_CHANGED"; case InputContextAction::GOT_FOCUS: return"GOT_FOCUS"; case InputContextAction::LOST_FOCUS: return"LOST_FOCUS"; case InputContextAction::MENU_GOT_PSEUDO_FOCUS: return"MENU_GOT_PSEUDO_FOCUS"; case InputContextAction::MENU_LOST_PSEUDO_FOCUS: return"MENU_LOST_PSEUDO_FOCUS"; case InputContextAction::WIDGET_CREATED: return"WIDGET_CREATED"; default: return"Unknown";
}
}
nsCString result; wchar_t buf[256]; if (WinRegistry::GetString(HKEY_CLASSES_ROOT, key, u""_ns, buf,
WinRegistry::kLegacyWinUtilsStringFlags)) {
result = NS_ConvertUTF16toUTF8(buf);
} else {
result = NS_ConvertUTF16toUTF8(str);
}
::CoTaskMemFree(str); return result;
}
staticconstchar* GetCommonReturnValueName(HRESULT aResult) { switch (aResult) { case S_OK: return"S_OK"; case E_ABORT: return"E_ABORT"; case E_ACCESSDENIED: return"E_ACCESSDENIED"; case E_FAIL: return"E_FAIL"; case E_HANDLE: return"E_HANDLE"; case E_INVALIDARG: return"E_INVALIDARG"; case E_NOINTERFACE: return"E_NOINTERFACE"; case E_NOTIMPL: return"E_NOTIMPL"; case E_OUTOFMEMORY: return"E_OUTOFMEMORY"; case E_POINTER: return"E_POINTER"; case E_UNEXPECTED: return"E_UNEXPECTED"; default: return SUCCEEDED(aResult) ? "Succeeded" : "Failed";
}
}
staticconstchar* GetTextStoreReturnValueName(HRESULT aResult) { switch (aResult) { case TS_E_FORMAT: return"TS_E_FORMAT"; case TS_E_INVALIDPOINT: return"TS_E_INVALIDPOINT"; case TS_E_INVALIDPOS: return"TS_E_INVALIDPOS"; case TS_E_NOINTERFACE: return"TS_E_NOINTERFACE"; case TS_E_NOLAYOUT: return"TS_E_NOLAYOUT"; case TS_E_NOLOCK: return"TS_E_NOLOCK"; case TS_E_NOOBJECT: return"TS_E_NOOBJECT"; case TS_E_NOSELECTION: return"TS_E_NOSELECTION"; case TS_E_NOSERVICE: return"TS_E_NOSERVICE"; case TS_E_READONLY: return"TS_E_READONLY"; case TS_E_SYNCHRONOUS: return"TS_E_SYNCHRONOUS"; case TS_S_ASYNC: return"TS_S_ASYNC"; default: return GetCommonReturnValueName(aResult);
}
}
staticconst nsCString GetSinkMaskNameStr(DWORD aSinkMask) {
nsCString description; if (aSinkMask & TS_AS_TEXT_CHANGE) {
description.AppendLiteral("TS_AS_TEXT_CHANGE");
} if (aSinkMask & TS_AS_SEL_CHANGE) {
HandleSeparator(description);
description.AppendLiteral("TS_AS_SEL_CHANGE");
} if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
HandleSeparator(description);
description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
} if (aSinkMask & TS_AS_ATTR_CHANGE) {
HandleSeparator(description);
description.AppendLiteral("TS_AS_ATTR_CHANGE");
} if (aSinkMask & TS_AS_STATUS_CHANGE) {
HandleSeparator(description);
description.AppendLiteral("TS_AS_STATUS_CHANGE");
} if (description.IsEmpty()) {
description.AppendLiteral("not-specified");
} return description;
}
class GetInputScopeString : public nsAutoCString { public: explicit GetInputScopeString(const nsTArray<InputScope>& aList) { for (InputScope inputScope : aList) { if (!IsEmpty()) {
AppendLiteral(", ");
} switch (inputScope) { case IS_DEFAULT:
AppendLiteral("IS_DEFAULT"); break; case IS_URL:
AppendLiteral("IS_URL"); break; case IS_FILE_FULLFILEPATH:
AppendLiteral("IS_FILE_FULLFILEPATH"); break; case IS_FILE_FILENAME:
AppendLiteral("IS_FILE_FILENAME"); break; case IS_EMAIL_USERNAME:
AppendLiteral("IS_EMAIL_USERNAME"); break; case IS_EMAIL_SMTPEMAILADDRESS:
AppendLiteral("IS_EMAIL_SMTPEMAILADDRESS"); break; case IS_LOGINNAME:
AppendLiteral("IS_LOGINNAME"); break; case IS_PERSONALNAME_FULLNAME:
AppendLiteral("IS_PERSONALNAME_FULLNAME"); break; case IS_PERSONALNAME_PREFIX:
AppendLiteral("IS_PERSONALNAME_PREFIX"); break; case IS_PERSONALNAME_GIVENNAME:
AppendLiteral("IS_PERSONALNAME_GIVENNAME"); break; case IS_PERSONALNAME_MIDDLENAME:
AppendLiteral("IS_PERSONALNAME_MIDDLENAME"); break; case IS_PERSONALNAME_SURNAME:
AppendLiteral("IS_PERSONALNAME_SURNAME"); break; case IS_PERSONALNAME_SUFFIX:
AppendLiteral("IS_PERSONALNAME_SUFFIX"); break; case IS_ADDRESS_FULLPOSTALADDRESS:
AppendLiteral("IS_ADDRESS_FULLPOSTALADDRESS"); break; case IS_ADDRESS_POSTALCODE:
AppendLiteral("IS_ADDRESS_POSTALCODE"); break; case IS_ADDRESS_STREET:
AppendLiteral("IS_ADDRESS_STREET"); break; case IS_ADDRESS_STATEORPROVINCE:
AppendLiteral("IS_ADDRESS_STATEORPROVINCE"); break; case IS_ADDRESS_CITY:
AppendLiteral("IS_ADDRESS_CITY"); break; case IS_ADDRESS_COUNTRYNAME:
AppendLiteral("IS_ADDRESS_COUNTRYNAME"); break; case IS_ADDRESS_COUNTRYSHORTNAME:
AppendLiteral("IS_ADDRESS_COUNTRYSHORTNAME"); break; case IS_CURRENCY_AMOUNTANDSYMBOL:
AppendLiteral("IS_CURRENCY_AMOUNTANDSYMBOL"); break; case IS_CURRENCY_AMOUNT:
AppendLiteral("IS_CURRENCY_AMOUNT"); break; case IS_DATE_FULLDATE:
AppendLiteral("IS_DATE_FULLDATE"); break; case IS_DATE_MONTH:
AppendLiteral("IS_DATE_MONTH"); break; case IS_DATE_DAY:
AppendLiteral("IS_DATE_DAY"); break; case IS_DATE_YEAR:
AppendLiteral("IS_DATE_YEAR"); break; case IS_DATE_MONTHNAME:
AppendLiteral("IS_DATE_MONTHNAME"); break; case IS_DATE_DAYNAME:
AppendLiteral("IS_DATE_DAYNAME"); break; case IS_DIGITS:
AppendLiteral("IS_DIGITS"); break; case IS_NUMBER:
AppendLiteral("IS_NUMBER"); break; case IS_ONECHAR:
AppendLiteral("IS_ONECHAR"); break; case IS_PASSWORD:
AppendLiteral("IS_PASSWORD"); break; case IS_TELEPHONE_FULLTELEPHONENUMBER:
AppendLiteral("IS_TELEPHONE_FULLTELEPHONENUMBER"); break; case IS_TELEPHONE_COUNTRYCODE:
AppendLiteral("IS_TELEPHONE_COUNTRYCODE"); break; case IS_TELEPHONE_AREACODE:
AppendLiteral("IS_TELEPHONE_AREACODE"); break; case IS_TELEPHONE_LOCALNUMBER:
AppendLiteral("IS_TELEPHONE_LOCALNUMBER"); break; case IS_TIME_FULLTIME:
AppendLiteral("IS_TIME_FULLTIME"); break; case IS_TIME_HOUR:
AppendLiteral("IS_TIME_HOUR"); break; case IS_TIME_MINORSEC:
AppendLiteral("IS_TIME_MINORSEC"); break; case IS_NUMBER_FULLWIDTH:
AppendLiteral("IS_NUMBER_FULLWIDTH"); break; case IS_ALPHANUMERIC_HALFWIDTH:
AppendLiteral("IS_ALPHANUMERIC_HALFWIDTH"); break; case IS_ALPHANUMERIC_FULLWIDTH:
AppendLiteral("IS_ALPHANUMERIC_FULLWIDTH"); break; case IS_CURRENCY_CHINESE:
AppendLiteral("IS_CURRENCY_CHINESE"); break; case IS_BOPOMOFO:
AppendLiteral("IS_BOPOMOFO"); break; case IS_HIRAGANA:
AppendLiteral("IS_HIRAGANA"); break; case IS_KATAKANA_HALFWIDTH:
AppendLiteral("IS_KATAKANA_HALFWIDTH"); break; case IS_KATAKANA_FULLWIDTH:
AppendLiteral("IS_KATAKANA_FULLWIDTH"); break; case IS_HANJA:
AppendLiteral("IS_HANJA"); break; case IS_PHRASELIST:
AppendLiteral("IS_PHRASELIST"); break; case IS_REGULAREXPRESSION:
AppendLiteral("IS_REGULAREXPRESSION"); break; case IS_SRGS:
AppendLiteral("IS_SRGS"); break; case IS_XML:
AppendLiteral("IS_XML"); break; case IS_PRIVATE:
AppendLiteral("IS_PRIVATE"); break; default:
AppendPrintf("Unknown Value(%d)", inputScope); break;
}
}
}
};
staticbool IsIMM_IMEActive() { // Use IMM API until TSFStaticSink starts to work. if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) { return IsIMM_IME(::GetKeyboardLayout(0));
} return sInstance->mIsIMM_IME;
}
/** * ActiveTIP() returns an ID for currently active TIP. * Please note that this method is expensive due to needs a lot of GUID * comparations if active language ID is one of CJKT. If you need to * check TIPs for a specific language, you should check current language * first.
*/ static TextInputProcessorID ActiveTIP() {
EnsureInstance(); if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) { return TextInputProcessorID::eUnknown;
}
sInstance->ComputeActiveTextInputProcessor(); if (NS_WARN_IF(sInstance->mActiveTIP ==
TextInputProcessorID::eNotComputed)) { return TextInputProcessorID::eUnknown;
} return sInstance->mActiveTIP;
}
staticbool GetActiveTIPNameForTelemetry(nsAString& aName) { if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) { returnfalse;
} if (sInstance->mActiveTIPGUID == GUID_NULL) {
aName.Truncate();
aName.AppendPrintf("0x%04X", sInstance->mLangID); returntrue;
} // key should be "LocaleID|Description". Although GUID of the // profile is unique key since description may be localized for system // language, unfortunately, it's too long to record as key with its // description. Therefore, we should record only the description with // LocaleID because Microsoft IME may not include language information. // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
aName.Truncate();
aName.AppendPrintf("0x%04X|", sInstance->mLangID);
nsAutoString description;
description.Assign(sInstance->mActiveTIPKeyboardDescription); staticconst uint32_t kMaxDescriptionLength = 72 - aName.Length(); if (description.Length() > kMaxDescriptionLength) { if (NS_IS_LOW_SURROGATE(description[kMaxDescriptionLength - 1]) &&
NS_IS_HIGH_SURROGATE(description[kMaxDescriptionLength - 2])) {
description.Truncate(kMaxDescriptionLength - 2);
} else {
description.Truncate(kMaxDescriptionLength - 1);
} // U+2026 is "..."
description.Append(char16_t(0x2026));
}
aName.Append(description); returntrue;
}
staticbool IsMSChangJieOrMSQuickActive() { // ActiveTIP() is expensive if it hasn't computed active TIP yet. // For avoiding unnecessary computation, we should check if the language // for current TIP is Traditional Chinese. if (!IsTraditionalChinese()) { returnfalse;
} switch (ActiveTIP()) { case TextInputProcessorID::eMicrosoftChangJie: case TextInputProcessorID::eMicrosoftQuick: returntrue; default: returnfalse;
}
}
staticbool IsMSPinyinOrMSWubiActive() { // ActiveTIP() is expensive if it hasn't computed active TIP yet. // For avoiding unnecessary computation, we should check if the language // for current TIP is Simplified Chinese. if (!IsSimplifiedChinese()) { returnfalse;
} switch (ActiveTIP()) { case TextInputProcessorID::eMicrosoftPinyin: case TextInputProcessorID::eMicrosoftWubi: returntrue; default: returnfalse;
}
}
staticbool IsMSJapaneseIMEActive() { // ActiveTIP() is expensive if it hasn't computed active TIP yet. // For avoiding unnecessary computation, we should check if the language // for current TIP is Japanese. if (!IsJapanese()) { returnfalse;
} return ActiveTIP() == TextInputProcessorID::eMicrosoftIMEForJapanese;
}
staticbool IsGoogleJapaneseInputActive() { // ActiveTIP() is expensive if it hasn't computed active TIP yet. // For avoiding unnecessary computation, we should check if the language // for current TIP is Japanese. if (!IsJapanese()) { returnfalse;
} return ActiveTIP() == TextInputProcessorID::eGoogleJapaneseInput;
}
staticbool IsATOKActive() { // ActiveTIP() is expensive if it hasn't computed active TIP yet. // For avoiding unnecessary computation, we should check if active TIP is // ATOK first since it's cheaper. return IsJapanese() && sInstance->IsATOKActiveInternal();
}
// Note that ATOK 2011 - 2016 refers native caret position for deciding its // popup window position. staticbool IsATOKReferringNativeCaretActive() { // ActiveTIP() is expensive if it hasn't computed active TIP yet. // For avoiding unnecessary computation, we should check if active TIP is // ATOK first since it's cheaper. if (!IsJapanese() || !sInstance->IsATOKActiveInternal()) { returnfalse;
} switch (ActiveTIP()) { case TextInputProcessorID::eATOK2011: case TextInputProcessorID::eATOK2012: case TextInputProcessorID::eATOK2013: case TextInputProcessorID::eATOK2014: case TextInputProcessorID::eATOK2015: returntrue; default: returnfalse;
}
}
bool IsATOKActiveInternal() {
EnsureInitActiveTIPKeyboard(); // FYI: Name of packaged ATOK includes the release year like "ATOK 2015". // Name of ATOK Passport (subscription) equals "ATOK". return StringBeginsWith(mActiveTIPKeyboardDescription, u"ATOK "_ns) ||
mActiveTIPKeyboardDescription.EqualsLiteral("ATOK");
}
void ComputeActiveTextInputProcessor() { if (mActiveTIP != TextInputProcessorID::eNotComputed) { return;
}
if (mActiveTIPGUID == GUID_NULL) {
mActiveTIP = TextInputProcessorID::eNone; return;
}
// Comparing GUID is slow. So, we should use language information to // reduce the comparing cost for TIP which is not we do not support // specifically since they are always compared with all supported TIPs. switch (mLangID) { case 0x0404:
mActiveTIP = ComputeActiveTIPAsTraditionalChinese(); break; case 0x0411:
mActiveTIP = ComputeActiveTIPAsJapanese(); break; case 0x0412:
mActiveTIP = ComputeActiveTIPAsKorean(); break; case 0x0804:
mActiveTIP = ComputeActiveTIPAsSimplifiedChinese(); break; default:
mActiveTIP = TextInputProcessorID::eUnknown; break;
} // Special case for Keyman Desktop, it is available for any languages. // Therefore, we need to check it only if we don't know the active TIP. if (mActiveTIP != TextInputProcessorID::eUnknown) { return;
}
// NOTE: There are some other Traditional Chinese TIPs installed in Windows: // * Chinese Traditional Array (version 6.0) // - {D38EFF65-AA46-4FD5-91A7-67845FB02F5B} (Win7, Win8.1) // * Chinese Traditional DaYi (version 6.0) // - {037B2C25-480C-4D7F-B027-D6CA6B69788A} (Win7, Win8.1)
// 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;
// Active TIP's GUID and CLSID
GUID mActiveTIPGUID;
CLSID mActiveTIPCLSID;
bool TSFStaticSink::Init(ITfThreadMgr* aThreadMgr,
ITfInputProcessorProfiles* aInputProcessorProfiles) {
MOZ_ASSERT(!mThreadMgr && !mInputProcessorProfiles, "TSFStaticSink::Init() must be called only once");
RefPtr<ITfSource> source;
HRESULT hr =
mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); if (FAILED(hr)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFStaticSink::Init() FAILED to get ITfSource " "instance (0x%08lX)", this, hr)); returnfalse;
}
// NOTE: On Vista or later, Windows let us know activate IME changed only // with ITfInputProcessorProfileActivationSink.
hr = source->AdviseSink(
IID_ITfInputProcessorProfileActivationSink, static_cast<ITfInputProcessorProfileActivationSink*>(this),
&mIPProfileCookie); if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFStaticSink::Init() FAILED to install " "ITfInputProcessorProfileActivationSink (0x%08lX)", this, hr)); returnfalse;
}
STDMETHODIMP
TSFStaticSink::OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID rclsid,
REFGUID catid, REFGUID guidProfile, HKL hkl,
DWORD dwFlags) { if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
(dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
catid == GUID_TFCAT_TIP_KEYBOARD)) {
mOnActivatedCalled = true;
mActiveTIP = TextInputProcessorID::eNotComputed;
mActiveTIPGUID = guidProfile;
mActiveTIPCLSID = rclsid;
mLangID = langid & 0xFFFF;
mIsIMM_IME = IsIMM_IME(hkl);
GetTIPDescription(rclsid, langid, guidProfile,
mActiveTIPKeyboardDescription); if (mActiveTIPGUID != GUID_NULL) { // key should be "LocaleID|Description". Although GUID of the // profile is unique key since description may be localized for system // language, unfortunately, it's too long to record as key with its // description. Therefore, we should record only the description with // LocaleID because Microsoft IME may not include language information. // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
nsAutoString key;
TSFStaticSink::GetActiveTIPNameForTelemetry(key);
glean::widget::ime_name_on_windows.Get(NS_ConvertUTF16toUTF8(key))
.Set(true);
} // Notify IMEHandler of changing active keyboard layout.
IMEHandler::OnKeyboardLayoutChanged();
}
MOZ_LOG(gIMELog, LogLevel::Info,
("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08lX), " "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%p, " "dwFlags=0x%08lX (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) { returntrue;
}
TF_INPUTPROCESSORPROFILE profile;
hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); if (hr == S_FALSE) {
MOZ_LOG(gIMELog, LogLevel::Info,
("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " "to get active keyboard layout profile due to no active profile, " "hr=0x%08lX", this, hr)); // XXX Should we call OnActivated() with arguments like non-TIP in this // case? returnfalse;
} if (FAILED(hr)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED " "to get active TIP keyboard, hr=0x%08lX", this, hr)); returnfalse;
}
RefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
HRESULT hr = mInputProcessorProfiles->EnumLanguageProfiles(
aLangID, getter_AddRefs(enumLangProfiles)); if (FAILED(hr) || !enumLangProfiles) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFStaticSink::IsTIPCategoryKeyboard(), FAILED " "to get language profiles enumerator, hr=0x%08lX", this, hr)); returnfalse;
}
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) { returntrue;
}
} returnfalse;
}
TSFTextStore::TSFTextStore()
: mEditCookie(0),
mSinkMask(0),
mLock(0),
mLockQueued(0),
mHandlingKeyMessage(0) { // We hope that 5 or more actions don't occur at once.
mPendingActions.SetCapacity(5);
if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED due to being initialized with " "destroyed widget", this)); returnfalse;
}
if (mDocumentMgr) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED due to already initialized", this)); returnfalse;
}
mWidget = aWidget; if (NS_WARN_IF(!mWidget)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED " "due to aWidget is nullptr ", this)); returnfalse;
}
mDispatcher = mWidget->GetTextEventDispatcher(); if (NS_WARN_IF(!mDispatcher)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED " "due to aWidget->GetTextEventDispatcher() failure", this)); returnfalse;
}
if (aContext.mURI) { // We don't need the document URL if it fails, let's ignore the error.
nsAutoCString spec; if (NS_SUCCEEDED(aContext.mURI->GetSpec(spec))) {
CopyUTF8toUTF16(spec, mDocumentURL);
}
}
// Create document manager
RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
RefPtr<ITfDocumentMgr> documentMgr;
HRESULT hr = threadMgr->CreateDocumentMgr(getter_AddRefs(documentMgr)); if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr " "(0x%08lX)", this, hr)); returnfalse;
} if (NS_WARN_IF(mDestroyed)) {
MOZ_LOG(
gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr due to " "TextStore being destroyed during calling " "ITfThreadMgr::CreateDocumentMgr()", this)); returnfalse;
} // Create context and add it to document manager
RefPtr<ITfContext> context;
hr = documentMgr->CreateContext(sClientId, 0, static_cast<ITextStoreACP*>(this),
getter_AddRefs(context), &mEditCookie); if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create the context " "(0x%08lX)", this, hr)); returnfalse;
} if (NS_WARN_IF(mDestroyed)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfContext due to " "TextStore being destroyed during calling " "ITfDocumentMgr::CreateContext()", this)); returnfalse;
}
hr = documentMgr->Push(context); if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to push the context (0x%08lX)", this, hr)); returnfalse;
} if (NS_WARN_IF(mDestroyed)) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfContext due to " "TextStore being destroyed during calling ITfDocumentMgr::Push()", this));
documentMgr->Pop(TF_POPF_ALL); returnfalse;
}
// Destroy native caret first because it's not directly related to TSF and // there may be another textstore which gets focus. So, we should avoid // to destroy caret after the new one recreates caret.
IMEHandler::MaybeDestroyNativeCaret();
// If there is composition, TSF keeps the composition even after the text // store destroyed. So, we should clear the composition here. if (mComposition.isSome()) {
CommitCompositionInternal(false);
}
// If this is called during handling a keydown or keyup message, we should // put off to release TSF objects until it completely finishes since // MS-IME for Japanese refers some objects without grabbing them. if (!mHandlingKeyMessage) {
ReleaseTSFObjects();
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.71Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.