#include "chrome/browser/spellchecker/spellcheck_service.h"
#include <iterator>
#include <memory>
#include <set>
#include <utility>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_split.h"
#include "base/supports_user_data.h"
#include "base/synchronization/waitable_event.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/spellcheck/browser/pref_names.h"
#include "components/spellcheck/browser/spellcheck_host_metrics.h"
#include "components/spellcheck/browser/spellcheck_platform.h"
#include "components/spellcheck/browser/spelling_service_client.h"
#include "components/spellcheck/common/spellcheck.mojom.h"
#include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "ui/base/l10n/l10n_util.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#endif
#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "components/spellcheck/browser/windows_spell_checker.h"
#endif
BrowserThread;
namespace {
SpellcheckService::SpellCheckerBinder& GetSpellCheckerBinderOverride() { … }
bool RecordSpellingConfigurationMetrics(content::BrowserContext* context) { … }
}
base::WaitableEvent* g_status_event = …;
SpellcheckService::EventType g_status_type = …;
SpellcheckService::SpellcheckService(content::BrowserContext* context)
: … { … }
SpellcheckService::~SpellcheckService() { … }
base::WeakPtr<SpellcheckService> SpellcheckService::GetWeakPtr() { … }
#if !BUILDFLAG(IS_MAC)
void SpellcheckService::GetDictionaries(
content::BrowserContext* browser_context,
std::vector<Dictionary>* dictionaries) { … }
#endif
bool SpellcheckService::SignalStatusEvent(
SpellcheckService::EventType status_type) { … }
std::string SpellcheckService::GetSupportedAcceptLanguageCode(
const std::string& supported_language_full_tag,
bool generic_only) { … }
#if BUILDFLAG(IS_WIN)
void SpellcheckService::EnableFirstUserLanguageForSpellcheck(
PrefService* prefs) {
base::Value::List user_dictionaries =
prefs->GetList(spellcheck::prefs::kSpellCheckDictionaries).Clone();
std::vector<std::string> user_languages =
base::SplitString(prefs->GetString(language::prefs::kAcceptLanguages),
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
std::string first_user_language;
std::vector<std::string> accept_languages;
l10n_util::GetAcceptLanguages(&accept_languages);
for (const auto& user_language : user_languages) {
if (base::Contains(accept_languages, user_language)) {
first_user_language = user_language;
break;
}
}
bool first_user_language_spellchecked = false;
for (const auto& dictionary_value : user_dictionaries) {
first_user_language_spellchecked =
base::Contains(dictionary_value.GetString(), first_user_language);
if (first_user_language_spellchecked)
break;
}
if (!first_user_language_spellchecked) {
user_dictionaries.Insert(user_dictionaries.begin(),
base::Value(first_user_language));
prefs->SetList(spellcheck::prefs::kSpellCheckDictionaries,
std::move(user_dictionaries));
}
}
#endif
void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) { … }
void SpellcheckService::InitForRenderer(content::RenderProcessHost* host) { … }
SpellCheckHostMetrics* SpellcheckService::GetMetrics() const { … }
SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() { … }
void SpellcheckService::LoadDictionaries() { … }
const std::vector<std::unique_ptr<SpellcheckHunspellDictionary>>&
SpellcheckService::GetHunspellDictionaries() { … }
bool SpellcheckService::IsSpellcheckEnabled() const { … }
void SpellcheckService::OnRenderProcessHostCreated(
content::RenderProcessHost* host) { … }
void SpellcheckService::OnCustomDictionaryLoaded() { … }
void SpellcheckService::OnCustomDictionaryChanged(
const SpellcheckCustomDictionary::Change& change) { … }
void SpellcheckService::OnHunspellDictionaryInitialized(
const std::string& language) { … }
void SpellcheckService::OnHunspellDictionaryDownloadBegin(
const std::string& language) { … }
void SpellcheckService::OnHunspellDictionaryDownloadSuccess(
const std::string& language) { … }
void SpellcheckService::OnHunspellDictionaryDownloadFailure(
const std::string& language) { … }
void SpellcheckService::InitializeDictionaries(base::OnceClosure done) { … }
#if BUILDFLAG(IS_WIN)
void SpellcheckService::InitWindowsDictionaryLanguages(
const std::vector<std::string>& windows_spellcheck_languages) {
windows_spellcheck_dictionary_map_.clear();
for (const auto& windows_spellcheck_language : windows_spellcheck_languages) {
std::string accept_language =
SpellcheckService::GetSupportedAcceptLanguageCode(
windows_spellcheck_language, false);
AddWindowsSpellcheckDictionary(accept_language,
windows_spellcheck_language);
if (base::EqualsCaseInsensitiveASCII(
"sr-Cyrl", SpellcheckService::GetLanguageAndScriptTag(
windows_spellcheck_language,
true))) {
AddWindowsSpellcheckDictionary("sr", windows_spellcheck_language);
}
accept_language = SpellcheckService::GetSupportedAcceptLanguageCode(
windows_spellcheck_language, true);
AddWindowsSpellcheckDictionary(accept_language, accept_language);
}
PrefService* prefs = user_prefs::UserPrefs::Get(context_);
DCHECK(prefs);
ScopedListPrefUpdate update(prefs,
spellcheck::prefs::kSpellCheckDictionaries);
update->EraseIf([this](const base::Value& entry) {
const std::string dictionary_name = entry.GetString();
return (!UsesWindowsDictionary(dictionary_name) &&
spellcheck::GetCorrespondingSpellCheckLanguage(dictionary_name)
.empty());
});
}
bool SpellcheckService::UsesWindowsDictionary(
std::string accept_language) const {
return !GetSupportedWindowsDictionaryLanguage(accept_language).empty();
}
#endif
void SpellcheckService::OverrideBinderForTesting(SpellCheckerBinder binder) { … }
std::string SpellcheckService::GetLanguageAndScriptTag(
const std::string& full_tag,
bool include_script_tag) { … }
#if BUILDFLAG(IS_WIN)
std::string SpellcheckService::GetSupportedAcceptLanguageCodeGenericOnly(
const std::string& supported_language_full_tag,
const std::vector<std::string>& accept_languages) {
auto iter = base::ranges::find_if(
accept_languages,
[supported_language_full_tag](const auto& accept_language) {
return base::EqualsCaseInsensitiveASCII(
SpellcheckService::GetLanguageAndScriptTag(
supported_language_full_tag,
false),
SpellcheckService::GetLanguageAndScriptTag(
accept_language,
false));
});
if (iter != accept_languages.end()) {
if (base::EqualsCaseInsensitiveASCII(
SpellcheckService::GetLanguageAndScriptTag(
supported_language_full_tag,
true),
"sr-Latn")) {
return "";
}
return *iter;
}
return "";
}
bool SpellcheckService::HasPrivateUseSubTag(const std::string& full_tag) {
std::vector<std::string> subtags = base::SplitString(
full_tag, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
return base::Contains(subtags, "x");
}
std::string SpellcheckService::GetTagToPassToWindowsSpellchecker(
const std::string& accept_language,
const std::string& supported_language_full_tag) {
if (base::EqualsCaseInsensitiveASCII(supported_language_full_tag,
accept_language)) {
return supported_language_full_tag;
}
if (base::EqualsCaseInsensitiveASCII("sr", accept_language))
return "sr-Cyrl";
return SpellcheckService::GetLanguageAndScriptTag(
supported_language_full_tag,
true);
}
#endif
void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) { … }
SpellcheckService::EventType SpellcheckService::GetStatusEvent() { … }
mojo::Remote<spellcheck::mojom::SpellChecker>
SpellcheckService::GetSpellCheckerForProcess(content::RenderProcessHost* host) { … }
void SpellcheckService::InitForAllRenderers() { … }
void SpellcheckService::OnSpellCheckDictionariesChanged() { … }
void SpellcheckService::OnUseSpellingServiceChanged() { … }
void SpellcheckService::OnAcceptLanguagesChanged() { … }
std::vector<std::string> SpellcheckService::GetNormalizedAcceptLanguages(
bool normalize_for_spellcheck) const { … }
#if BUILDFLAG(IS_WIN)
void SpellcheckService::InitializePlatformSpellchecker() {
if (!platform_spell_checker()) {
platform_spell_checker_ = std::make_unique<WindowsSpellChecker>(
base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()}));
}
}
void SpellcheckService::RecordSpellcheckLocalesStats() {
if (metrics_ && platform_spell_checker() && !hunspell_dictionaries_.empty()) {
std::vector<std::string> hunspell_locales;
for (auto& dict : hunspell_dictionaries_) {
hunspell_locales.push_back(dict->GetLanguage());
}
spellcheck_platform::RecordSpellcheckLocalesStats(
platform_spell_checker(), std::move(hunspell_locales));
}
}
void SpellcheckService::RecordChromeLocalesStats() {
const auto& accept_languages =
GetNormalizedAcceptLanguages( false);
if (metrics_ && platform_spell_checker() && !accept_languages.empty()) {
spellcheck_platform::RecordChromeLocalesStats(platform_spell_checker(),
std::move(accept_languages));
}
}
void SpellcheckService::AddWindowsSpellcheckDictionary(
const std::string& accept_language,
const std::string& supported_language_full_tag) {
if (!accept_language.empty()) {
windows_spellcheck_dictionary_map_.insert(
{accept_language, supported_language_full_tag});
}
}
std::string SpellcheckService::GetSupportedWindowsDictionaryLanguage(
const std::string& accept_language) const {
std::string spellcheck_language;
auto it = windows_spellcheck_dictionary_map_.find(accept_language);
if (it != windows_spellcheck_dictionary_map_.end())
spellcheck_language = it->second;
return spellcheck_language;
}
void SpellcheckService::AddSpellcheckLanguagesForTesting(
const std::vector<std::string>& languages) {
InitializePlatformSpellchecker();
if (platform_spell_checker()) {
spellcheck_platform::AddSpellcheckLanguagesForTesting(
platform_spell_checker(), languages);
}
}
#endif