// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/win/conflicts/enumerate_input_method_editors.h"
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/win/registry.h"
#include "chrome/browser/win/conflicts/module_info_util.h"
namespace {
// Returns true if |ime_guid| is the GUID of a built-in Microsoft IME.
bool IsMicrosoftIme(const wchar_t* ime_guid) {
// This list was provided by Microsoft.
static constexpr const wchar_t* kMicrosoftImeGuids[] = {
L"{0000897b-83df-4b96-be07-0fb58b01c4a4}",
L"{03b5835f-f03c-411b-9ce2-aa23e1171e36}",
L"{07eb03d6-b001-41df-9192-bf9b841ee71f}",
L"{3697c5fa-60dd-4b56-92d4-74a569205c16}",
L"{531fdebf-9b4c-4a43-a2aa-960e8fcdc732}",
L"{6a498709-e00b-4c45-a018-8f9e4081ae40}",
L"{78cb5b0e-26ed-4fcc-854c-77e8f3d1aa80}",
L"{81d4e9c9-1d3b-41bc-9e6c-4b40bf79e35e}",
L"{8613e14c-d0c0-4161-ac0f-1dd2563286bc}",
L"{a028ae76-01b1-46c2-99c4-acd9858ae02f}",
L"{a1e2b86b-924a-4d43-80f6-8a820df7190f}",
L"{ae6be008-07fb-400d-8beb-337a64f7051f}",
L"{b115690a-ea02-48d5-a231-e3578d2fdf80}",
L"{c1ee01f2-b3b6-4a6a-9ddd-e988c088ec82}",
L"{dcbd6fa8-032f-11d3-b5b1-00c04fc324a1}",
L"{e429b25a-e5d3-4d1f-9be3-0c608477e3a1}",
L"{f25e9f57-2fc8-4eb3-a41a-cce5f08541e6}",
L"{f89e9e58-bd2f-4008-9ac2-0f816c09f4ee}",
L"{fa445657-9379-11d6-b41a-00065b83ee53}",
};
auto comp = [](const wchar_t* lhs, const wchar_t* rhs) -> bool {
return base::CompareCaseInsensitiveASCII(lhs, rhs) == -1;
};
DCHECK(std::is_sorted(std::begin(kMicrosoftImeGuids),
std::end(kMicrosoftImeGuids), comp));
return std::binary_search(std::begin(kMicrosoftImeGuids),
std::end(kMicrosoftImeGuids), ime_guid, comp);
}
// Returns the path to the in-proc server DLL for |guid|, or an empty path if
// none is found.
base::FilePath GetInprocServerDllPath(const wchar_t* guid) {
const std::wstring key_name = GuidToClsid(guid);
base::win::RegKey registry_key;
std::wstring value;
if (registry_key.Open(HKEY_CLASSES_ROOT, key_name.c_str(), KEY_QUERY_VALUE) ==
ERROR_SUCCESS &&
registry_key.ReadValue(L"", &value) == ERROR_SUCCESS) {
return base::FilePath(value);
}
return base::FilePath();
}
void EnumerateImesOnBlockingSequence(
scoped_refptr<base::SequencedTaskRunner> task_runner,
OnImeEnumeratedCallback on_ime_enumerated,
base::OnceClosure on_enumeration_finished) {
int nb_imes = 0;
for (base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, kImeRegistryKey);
iter.Valid(); ++iter) {
const wchar_t* guid = iter.Name();
// Skip Microsoft IMEs since Chrome won't do anything about those.
if (IsMicrosoftIme(guid))
continue;
base::FilePath dll_path = GetInprocServerDllPath(guid);
if (dll_path.empty())
continue;
uint32_t size_of_image = 0;
uint32_t time_date_stamp = 0;
if (!GetModuleImageSizeAndTimeDateStamp(dll_path, &size_of_image,
&time_date_stamp)) {
continue;
}
nb_imes++;
task_runner->PostTask(
FROM_HERE, base::BindOnce(on_ime_enumerated, dll_path, size_of_image,
time_date_stamp));
}
task_runner->PostTask(FROM_HERE, std::move(on_enumeration_finished));
base::UmaHistogramCounts100("ThirdPartyModules.InputMethodEditorsCount",
nb_imes);
}
} // namespace
const wchar_t kImeRegistryKey[] = L"SOFTWARE\\Microsoft\\CTF\\TIP";
void EnumerateInputMethodEditors(OnImeEnumeratedCallback on_ime_enumerated,
base::OnceClosure on_enumeration_finished) {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&EnumerateImesOnBlockingSequence,
base::SequencedTaskRunner::GetCurrentDefault(),
std::move(on_ime_enumerated),
std::move(on_enumeration_finished)));
}