718 lines
25 KiB
C++
718 lines
25 KiB
C++
/* -*- 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 "TSFStaticSink.h"
|
|
|
|
#include "TSFTextStore.h"
|
|
#include "TSFUtils.h"
|
|
#include "WinIMEHandler.h"
|
|
#include "WinMessages.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/glean/WidgetWindowsMetrics.h"
|
|
|
|
#include <comutil.h> // for _bstr_t
|
|
#include <oleauto.h> // for SysAllocString
|
|
#include <olectl.h>
|
|
|
|
extern mozilla::LazyLogModule gIMELog; // defined in TSFUtils.cpp
|
|
|
|
namespace mozilla::widget {
|
|
|
|
StaticRefPtr<TSFStaticSink> TSFStaticSink::sInstance;
|
|
|
|
// static
|
|
TSFStaticSink* TSFStaticSink::GetInstance() {
|
|
if (!sInstance) {
|
|
const RefPtr<ITfThreadMgr> threadMgr = TSFUtils::GetThreadMgr();
|
|
if (NS_WARN_IF(!threadMgr)) {
|
|
MOZ_LOG(
|
|
gIMELog, LogLevel::Error,
|
|
("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
|
|
"instance due to no ThreadMgr instance"));
|
|
return nullptr;
|
|
}
|
|
const RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
|
|
TSFUtils::GetInputProcessorProfiles();
|
|
if (NS_WARN_IF(!inputProcessorProfiles)) {
|
|
MOZ_LOG(
|
|
gIMELog, LogLevel::Error,
|
|
("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
|
|
"instance due to no InputProcessorProfiles instance"));
|
|
return nullptr;
|
|
}
|
|
RefPtr<TSFStaticSink> staticSink = new TSFStaticSink();
|
|
if (NS_WARN_IF(!staticSink->Init(threadMgr, inputProcessorProfiles))) {
|
|
staticSink->Destroy();
|
|
MOZ_LOG(
|
|
gIMELog, LogLevel::Error,
|
|
("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
|
|
"instance"));
|
|
return nullptr;
|
|
}
|
|
sInstance = staticSink.forget();
|
|
}
|
|
return sInstance;
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::GetActiveTIPNameForTelemetry(nsAString& aName) {
|
|
if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
|
|
return false;
|
|
}
|
|
if (sInstance->mActiveTIPGUID == GUID_NULL) {
|
|
aName.Truncate();
|
|
aName.AppendPrintf("0x%04X", sInstance->mLangID);
|
|
return true;
|
|
}
|
|
// 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);
|
|
static const 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);
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::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()) {
|
|
return false;
|
|
}
|
|
switch (ActiveTIP()) {
|
|
case TextInputProcessorID::MicrosoftChangJie:
|
|
case TextInputProcessorID::MicrosoftQuick:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::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()) {
|
|
return false;
|
|
}
|
|
switch (ActiveTIP()) {
|
|
case TextInputProcessorID::MicrosoftPinyin:
|
|
case TextInputProcessorID::MicrosoftWubi:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::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()) {
|
|
return false;
|
|
}
|
|
return ActiveTIP() == TextInputProcessorID::MicrosoftIMEForJapanese;
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::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()) {
|
|
return false;
|
|
}
|
|
return ActiveTIP() == TextInputProcessorID::GoogleJapaneseInput;
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::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();
|
|
}
|
|
|
|
// static
|
|
bool TSFStaticSink::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()) {
|
|
return false;
|
|
}
|
|
switch (ActiveTIP()) {
|
|
case TextInputProcessorID::ATOK2011:
|
|
case TextInputProcessorID::ATOK2012:
|
|
case TextInputProcessorID::ATOK2013:
|
|
case TextInputProcessorID::ATOK2014:
|
|
case TextInputProcessorID::ATOK2015:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool TSFStaticSink::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 TSFStaticSink::ComputeActiveTextInputProcessor() {
|
|
if (mActiveTIP != TextInputProcessorID::NotComputed) {
|
|
return;
|
|
}
|
|
|
|
if (mActiveTIPGUID == GUID_NULL) {
|
|
mActiveTIP = TextInputProcessorID::None;
|
|
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::Unknown;
|
|
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::Unknown) {
|
|
return;
|
|
}
|
|
|
|
// Note that keyboard layouts for Keyman assign its GUID on install
|
|
// randomly, but CLSID is constant in any environments.
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1670834#c7
|
|
// https://github.com/keymanapp/keyman/blob/318c73a9e1d571d942837ff9964590626e5bd5aa/windows/src/engine/kmtip/globals.cpp#L37
|
|
// {FE0420F1-38D1-4B4C-96BF-E7E20A74CFB7}
|
|
static constexpr CLSID kKeymanDesktop_CLSID = {
|
|
0xFE0420F1,
|
|
0x38D1,
|
|
0x4B4C,
|
|
{0x96, 0xBF, 0xE7, 0xE2, 0x0A, 0x74, 0xCF, 0xB7}};
|
|
if (mActiveTIPCLSID == kKeymanDesktop_CLSID) {
|
|
mActiveTIP = TextInputProcessorID::KeymanDesktop;
|
|
}
|
|
}
|
|
|
|
TextInputProcessorID TSFStaticSink::ComputeActiveTIPAsJapanese() {
|
|
// {A76C93D9-5523-4E90-AAFA-4DB112F9AC76} (Win7, Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftIMEForJapaneseGUID = {
|
|
0xA76C93D9,
|
|
0x5523,
|
|
0x4E90,
|
|
{0xAA, 0xFA, 0x4D, 0xB1, 0x12, 0xF9, 0xAC, 0x76}};
|
|
if (mActiveTIPGUID == kMicrosoftIMEForJapaneseGUID) {
|
|
return TextInputProcessorID::MicrosoftIMEForJapanese;
|
|
}
|
|
// {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
|
|
static constexpr GUID kMicrosoftOfficeIME2010ForJapaneseGUID = {
|
|
0x54EDCC94,
|
|
0x1524,
|
|
0x4BB1,
|
|
{0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64}};
|
|
if (mActiveTIPGUID == kMicrosoftOfficeIME2010ForJapaneseGUID) {
|
|
return TextInputProcessorID::MicrosoftOfficeIME2010ForJapanese;
|
|
}
|
|
// {773EB24E-CA1D-4B1B-B420-FA985BB0B80D}
|
|
static constexpr GUID kGoogleJapaneseInputGUID = {
|
|
0x773EB24E,
|
|
0xCA1D,
|
|
0x4B1B,
|
|
{0xB4, 0x20, 0xFA, 0x98, 0x5B, 0xB0, 0xB8, 0x0D}};
|
|
if (mActiveTIPGUID == kGoogleJapaneseInputGUID) {
|
|
return TextInputProcessorID::GoogleJapaneseInput;
|
|
}
|
|
// {F9C24A5C-8A53-499D-9572-93B2FF582115}
|
|
static const GUID kATOK2011GUID = {
|
|
0xF9C24A5C,
|
|
0x8A53,
|
|
0x499D,
|
|
{0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15}};
|
|
if (mActiveTIPGUID == kATOK2011GUID) {
|
|
return TextInputProcessorID::ATOK2011;
|
|
}
|
|
// {1DE01562-F445-401B-B6C3-E5B18DB79461}
|
|
static constexpr GUID kATOK2012GUID = {
|
|
0x1DE01562,
|
|
0xF445,
|
|
0x401B,
|
|
{0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61}};
|
|
if (mActiveTIPGUID == kATOK2012GUID) {
|
|
return TextInputProcessorID::ATOK2012;
|
|
}
|
|
// {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
|
|
static constexpr GUID kATOK2013GUID = {
|
|
0x3C4DB511,
|
|
0x189A,
|
|
0x4168,
|
|
{0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15}};
|
|
if (mActiveTIPGUID == kATOK2013GUID) {
|
|
return TextInputProcessorID::ATOK2013;
|
|
}
|
|
// {4EF33B79-6AA9-4271-B4BF-9321C279381B}
|
|
static constexpr GUID kATOK2014GUID = {
|
|
0x4EF33B79,
|
|
0x6AA9,
|
|
0x4271,
|
|
{0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B}};
|
|
if (mActiveTIPGUID == kATOK2014GUID) {
|
|
return TextInputProcessorID::ATOK2014;
|
|
}
|
|
// {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
|
|
static constexpr GUID kATOK2015GUID = {
|
|
0xEAB4DC00,
|
|
0xCE2E,
|
|
0x483D,
|
|
{0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A}};
|
|
if (mActiveTIPGUID == kATOK2015GUID) {
|
|
return TextInputProcessorID::ATOK2015;
|
|
}
|
|
// {0B557B4C-5740-4110-A60A-1493FA10BF2B}
|
|
static constexpr GUID kATOK2016GUID = {
|
|
0x0B557B4C,
|
|
0x5740,
|
|
0x4110,
|
|
{0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B}};
|
|
if (mActiveTIPGUID == kATOK2016GUID) {
|
|
return TextInputProcessorID::ATOK2016;
|
|
}
|
|
|
|
// * ATOK 2017
|
|
// - {6DBFD8F5-701D-11E6-920F-782BCBA6348F}
|
|
// * ATOK Passport (confirmed with version 31.1.2)
|
|
// - {A38F2FD9-7199-45E1-841C-BE0313D8052F}
|
|
|
|
if (IsATOKActiveInternal()) {
|
|
return TextInputProcessorID::ATOKUnknown;
|
|
}
|
|
|
|
// {E6D66705-1EDA-4373-8D01-1D0CB2D054C7}
|
|
static constexpr GUID kJapanist10GUID = {
|
|
0xE6D66705,
|
|
0x1EDA,
|
|
0x4373,
|
|
{0x8D, 0x01, 0x1D, 0x0C, 0xB2, 0xD0, 0x54, 0xC7}};
|
|
if (mActiveTIPGUID == kJapanist10GUID) {
|
|
return TextInputProcessorID::Japanist10;
|
|
}
|
|
|
|
return TextInputProcessorID::Unknown;
|
|
}
|
|
|
|
TextInputProcessorID TSFStaticSink::ComputeActiveTIPAsTraditionalChinese() {
|
|
// {B2F9C502-1742-11D4-9790-0080C882687E} (Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftBopomofoGUID = {
|
|
0xB2F9C502,
|
|
0x1742,
|
|
0x11D4,
|
|
{0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftBopomofoGUID) {
|
|
return TextInputProcessorID::MicrosoftBopomofo;
|
|
}
|
|
// {4BDF9F03-C7D3-11D4-B2AB-0080C882687E} (Win7, Win8.1, Win10)
|
|
static const GUID kMicrosoftChangJieGUID = {
|
|
0x4BDF9F03,
|
|
0xC7D3,
|
|
0x11D4,
|
|
{0xB2, 0xAB, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftChangJieGUID) {
|
|
return TextInputProcessorID::MicrosoftChangJie;
|
|
}
|
|
// {761309DE-317A-11D4-9B5D-0080C882687E} (Win7)
|
|
static constexpr GUID kMicrosoftPhoneticGUID = {
|
|
0x761309DE,
|
|
0x317A,
|
|
0x11D4,
|
|
{0x9B, 0x5D, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftPhoneticGUID) {
|
|
return TextInputProcessorID::MicrosoftPhonetic;
|
|
}
|
|
// {6024B45F-5C54-11D4-B921-0080C882687E} (Win7, Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftQuickGUID = {
|
|
0x6024B45F,
|
|
0x5C54,
|
|
0x11D4,
|
|
{0xB9, 0x21, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftQuickGUID) {
|
|
return TextInputProcessorID::MicrosoftQuick;
|
|
}
|
|
// {F3BA907A-6C7E-11D4-97FA-0080C882687E} (Win7)
|
|
static constexpr GUID kMicrosoftNewChangJieGUID = {
|
|
0xF3BA907A,
|
|
0x6C7E,
|
|
0x11D4,
|
|
{0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftNewChangJieGUID) {
|
|
return TextInputProcessorID::MicrosoftNewChangJie;
|
|
}
|
|
// {B2F9C502-1742-11D4-9790-0080C882687E} (Win7)
|
|
static constexpr GUID kMicrosoftNewPhoneticGUID = {
|
|
0xB2F9C502,
|
|
0x1742,
|
|
0x11D4,
|
|
{0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftNewPhoneticGUID) {
|
|
return TextInputProcessorID::MicrosoftNewPhonetic;
|
|
}
|
|
// {0B883BA0-C1C7-11D4-87F9-0080C882687E} (Win7)
|
|
static constexpr GUID kMicrosoftNewQuickGUID = {
|
|
0x0B883BA0,
|
|
0xC1C7,
|
|
0x11D4,
|
|
{0x87, 0xF9, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftNewQuickGUID) {
|
|
return TextInputProcessorID::MicrosoftNewQuick;
|
|
}
|
|
|
|
// 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)
|
|
|
|
// {B58630B5-0ED3-4335-BBC9-E77BBCB43CAD}
|
|
static const GUID kFreeChangJieGUID = {
|
|
0xB58630B5,
|
|
0x0ED3,
|
|
0x4335,
|
|
{0xBB, 0xC9, 0xE7, 0x7B, 0xBC, 0xB4, 0x3C, 0xAD}};
|
|
if (mActiveTIPGUID == kFreeChangJieGUID) {
|
|
return TextInputProcessorID::FreeChangJie;
|
|
}
|
|
|
|
return TextInputProcessorID::Unknown;
|
|
}
|
|
|
|
TextInputProcessorID TSFStaticSink::ComputeActiveTIPAsSimplifiedChinese() {
|
|
// FYI: This matches with neither "Microsoft Pinyin ABC Input Style" nor
|
|
// "Microsoft Pinyin New Experience Input Style" on Win7.
|
|
// {FA550B04-5AD7-411F-A5AC-CA038EC515D7} (Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftPinyinGUID = {
|
|
0xFA550B04,
|
|
0x5AD7,
|
|
0x411F,
|
|
{0xA5, 0xAC, 0xCA, 0x03, 0x8E, 0xC5, 0x15, 0xD7}};
|
|
if (mActiveTIPGUID == kMicrosoftPinyinGUID) {
|
|
return TextInputProcessorID::MicrosoftPinyin;
|
|
}
|
|
|
|
// {F3BA9077-6C7E-11D4-97FA-0080C882687E} (Win7)
|
|
static constexpr GUID kMicrosoftPinyinNewExperienceInputStyleGUID = {
|
|
0xF3BA9077,
|
|
0x6C7E,
|
|
0x11D4,
|
|
{0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
|
|
if (mActiveTIPGUID == kMicrosoftPinyinNewExperienceInputStyleGUID) {
|
|
return TextInputProcessorID::MicrosoftPinyinNewExperienceInputStyle;
|
|
}
|
|
// {82590C13-F4DD-44F4-BA1D-8667246FDF8E} (Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftWubiGUID = {
|
|
0x82590C13,
|
|
0xF4DD,
|
|
0x44F4,
|
|
{0xBA, 0x1D, 0x86, 0x67, 0x24, 0x6F, 0xDF, 0x8E}};
|
|
if (mActiveTIPGUID == kMicrosoftWubiGUID) {
|
|
return TextInputProcessorID::MicrosoftWubi;
|
|
}
|
|
// NOTE: There are some other Simplified Chinese TIPs installed in Windows:
|
|
// * Chinese Simplified QuanPin (version 6.0)
|
|
// - {54FC610E-6ABD-4685-9DDD-A130BDF1B170} (Win8.1)
|
|
// * Chinese Simplified ZhengMa (version 6.0)
|
|
// - {733B4D81-3BC3-4132-B91A-E9CDD5E2BFC9} (Win8.1)
|
|
// * Chinese Simplified ShuangPin (version 6.0)
|
|
// - {EF63706D-31C4-490E-9DBB-BD150ADC454B} (Win8.1)
|
|
// * Microsoft Pinyin ABC Input Style
|
|
// - {FCA121D2-8C6D-41FB-B2DE-A2AD110D4820} (Win7)
|
|
return TextInputProcessorID::Unknown;
|
|
}
|
|
|
|
TextInputProcessorID TSFStaticSink::ComputeActiveTIPAsKorean() {
|
|
// {B5FE1F02-D5F2-4445-9C03-C568F23C99A1} (Win7, Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftIMEForKoreanGUID = {
|
|
0xB5FE1F02,
|
|
0xD5F2,
|
|
0x4445,
|
|
{0x9C, 0x03, 0xC5, 0x68, 0xF2, 0x3C, 0x99, 0xA1}};
|
|
if (mActiveTIPGUID == kMicrosoftIMEForKoreanGUID) {
|
|
return TextInputProcessorID::MicrosoftIMEForKorean;
|
|
}
|
|
// {B60AF051-257A-46BC-B9D3-84DAD819BAFB} (Win8.1, Win10)
|
|
static constexpr GUID kMicrosoftOldHangulGUID = {
|
|
0xB60AF051,
|
|
0x257A,
|
|
0x46BC,
|
|
{0xB9, 0xD3, 0x84, 0xDA, 0xD8, 0x19, 0xBA, 0xFB}};
|
|
if (mActiveTIPGUID == kMicrosoftOldHangulGUID) {
|
|
return TextInputProcessorID::MicrosoftOldHangul;
|
|
}
|
|
|
|
// NOTE: There is the other Korean TIP installed in Windows:
|
|
// * Microsoft IME 2010
|
|
// - {48878C45-93F9-4aaf-A6A1-272CD863C4F5} (Win7)
|
|
|
|
return TextInputProcessorID::Unknown;
|
|
}
|
|
|
|
bool TSFStaticSink::Init(ITfThreadMgr* aThreadMgr,
|
|
ITfInputProcessorProfiles* aInputProcessorProfiles) {
|
|
MOZ_ASSERT(!mThreadMgr && !mInputProcessorProfiles,
|
|
"TSFStaticSink::Init() must be called only once");
|
|
|
|
mThreadMgr = aThreadMgr;
|
|
mInputProcessorProfiles = aInputProcessorProfiles;
|
|
|
|
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));
|
|
return false;
|
|
}
|
|
|
|
// 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));
|
|
return false;
|
|
}
|
|
|
|
MOZ_LOG(gIMELog, LogLevel::Info,
|
|
("0x%p TSFStaticSink::Init(), "
|
|
"mIPProfileCookie=0x%08lX",
|
|
this, mIPProfileCookie));
|
|
return true;
|
|
}
|
|
|
|
void TSFStaticSink::Destroy() {
|
|
MOZ_LOG(gIMELog, LogLevel::Info,
|
|
("0x%p TSFStaticSink::Shutdown() "
|
|
"mIPProfileCookie=0x%08lX",
|
|
this, mIPProfileCookie));
|
|
|
|
if (mIPProfileCookie != TF_INVALID_COOKIE) {
|
|
RefPtr<ITfSource> source;
|
|
HRESULT hr =
|
|
mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
|
|
if (FAILED(hr)) {
|
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
|
("0x%p TSFStaticSink::Shutdown() FAILED to get "
|
|
"ITfSource instance (0x%08lX)",
|
|
this, hr));
|
|
} else {
|
|
hr = source->UnadviseSink(mIPProfileCookie);
|
|
if (FAILED(hr)) {
|
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
|
("0x%p TSFTextStore::Shutdown() FAILED to uninstall "
|
|
"ITfInputProcessorProfileActivationSink (0x%08lX)",
|
|
this, hr));
|
|
}
|
|
}
|
|
}
|
|
|
|
mThreadMgr = nullptr;
|
|
mInputProcessorProfiles = nullptr;
|
|
}
|
|
|
|
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::NotComputed;
|
|
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, AutoClsidCString(rclsid).get(),
|
|
AutoRawGuidCString(catid).get(), AutoRawGuidCString(guidProfile).get(),
|
|
hkl, dwFlags, TSFUtils::BoolToChar(dwFlags & TF_IPSINK_FLAG_ACTIVE),
|
|
TSFUtils::BoolToChar(mIsIMM_IME),
|
|
NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
|
|
return S_OK;
|
|
}
|
|
|
|
bool TSFStaticSink::EnsureInitActiveTIPKeyboard() {
|
|
if (mOnActivatedCalled) {
|
|
return true;
|
|
}
|
|
|
|
RefPtr<ITfInputProcessorProfileMgr> profileMgr;
|
|
HRESULT hr = mInputProcessorProfiles->QueryInterface(
|
|
IID_ITfInputProcessorProfileMgr, getter_AddRefs(profileMgr));
|
|
if (FAILED(hr) || !profileMgr) {
|
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
|
("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
|
|
"to get input processor profile manager, hr=0x%08lX",
|
|
this, hr));
|
|
return false;
|
|
}
|
|
|
|
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?
|
|
return false;
|
|
}
|
|
if (FAILED(hr)) {
|
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
|
("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
|
|
"to get active TIP keyboard, hr=0x%08lX",
|
|
this, hr));
|
|
return false;
|
|
}
|
|
|
|
MOZ_LOG(gIMELog, LogLevel::Info,
|
|
("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;
|
|
}
|
|
|
|
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)) {
|
|
MOZ_LOG(gIMELog, LogLevel::Error,
|
|
("0x%p TSFStaticSink::InitActiveTIPDescription() FAILED "
|
|
"due to GetLanguageProfileDescription() failure, hr=0x%08lX",
|
|
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;
|
|
}
|
|
|
|
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));
|
|
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;
|
|
}
|
|
|
|
} // namespace mozilla::widget
|