chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc

// Copyright 2015 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/extensions/api/language_settings_private/language_settings_private_api.h"

#include <map>
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h"
#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h"
#include "chrome/browser/language/language_model_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/translate/translate_service.h"
#include "chrome/common/extensions/api/language_settings_private.h"
#include "chrome/common/pref_names.h"
#include "components/language/core/browser/language_model_manager.h"
#include "components/language/core/browser/pref_names.h"
#include "components/language/core/common/language_util.h"
#include "components/language/core/common/locale_util.h"
#include "components/spellcheck/browser/spellcheck_platform.h"
#include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "components/translate/core/browser/translate_download_manager.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "third_party/icu/source/i18n/unicode/coll.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/l10n_util_collator.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/ime/ash/component_extension_ime_manager.h"
#include "ui/base/ime/ash/extension_ime_util.h"
#include "ui/base/ime/ash/input_method_descriptor.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/base/ime/ash/input_method_util.h"
#endif

namespace extensions {

language_settings_private;

namespace {

#if BUILDFLAG(IS_CHROMEOS_ASH)
using ::ash::input_method::InputMethodDescriptor;
using ::ash::input_method::InputMethodDescriptors;
using ::ash::input_method::InputMethodManager;
using ::ash::input_method::InputMethodUtil;

// Number of IMEs that are needed to automatically enable the IME menu option.
const size_t kNumImesToAutoEnableImeMenu = 2;

// Returns the set of IDs of enabled IMEs for the given pref.
base::flat_set<std::string> GetIMEsFromPref(PrefService* prefs,
                                            const char* pref_name) {
  return base::SplitString(prefs->GetString(pref_name), ",",
                           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}

// Returns the set of allowed UI locales.
base::flat_set<std::string> GetAllowedLanguages(PrefService* prefs) {
  const auto& allowed_languages_values =
      prefs->GetList(prefs::kAllowedLanguages);
  return base::MakeFlatSet<std::string>(
      allowed_languages_values, {},
      [](const auto& locale_value) { return locale_value.GetString(); });
}

// Sorts the input methods by the order of their associated languages. For
// example, if the enabled language order is:
//  - French
//  - English
//  then the French keyboard should come before the English keyboard.
std::vector<std::string> GetSortedComponentIMEs(
    InputMethodManager* manager,
    scoped_refptr<InputMethodManager::State> ime_state,
    const base::flat_set<std::string>& component_ime_set,
    PrefService* prefs) {
  std::vector<std::string> enabled_languages =
      base::SplitString(prefs->GetString(language::prefs::kPreferredLanguages),
                        ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);

  // Duplicate set for membership testing.
  std::set<std::string> available_component_imes(component_ime_set.begin(),
                                                 component_ime_set.end());
  std::vector<std::string> component_ime_list;

  for (const auto& language_code : enabled_languages) {
    // Get all input methods for this language.
    std::vector<std::string> input_method_ids;
    manager->GetInputMethodUtil()->GetInputMethodIdsFromLanguageCode(
        language_code, ash::input_method::kAllInputMethods, &input_method_ids);
    // Append the enabled ones to the new list. Also remove them from the set
    // so they aren't duplicated for other languages.
    for (const auto& input_method_id : input_method_ids) {
      if (base::Contains(available_component_imes, input_method_id)) {
        component_ime_list.push_back(input_method_id);
        available_component_imes.erase(input_method_id);
      }
    }
  }
  for (const auto& input_method_id : available_component_imes) {
    component_ime_list.push_back(input_method_id);
  }

  return component_ime_list;
}

// Sorts the third-party IMEs by the order of their associated languages.
std::vector<std::string> GetSortedThirdPartyIMEs(
    scoped_refptr<InputMethodManager::State> ime_state,
    const base::flat_set<std::string>& third_party_ime_set,
    PrefService* prefs) {
  std::vector<std::string> ime_list;
  std::string preferred_languages =
      prefs->GetString(language::prefs::kPreferredLanguages);
  std::vector<std::string_view> enabled_languages =
      base::SplitStringPiece(preferred_languages, ",", base::TRIM_WHITESPACE,
                             base::SPLIT_WANT_NONEMPTY);

  // Add the fake language for ARC IMEs at the very last of the list. Unlike
  // Chrome OS IMEs, these ARC ones are not associated with any (real) language.
  enabled_languages.push_back(ash::extension_ime_util::kArcImeLanguage);

  InputMethodDescriptors descriptors;
  ime_state->GetInputMethodExtensions(&descriptors);

  // Filter out the IMEs not in |third_party_ime_set|.
  std::erase_if(descriptors, [&third_party_ime_set](
                                 const InputMethodDescriptor& descriptor) {
    return !third_party_ime_set.contains(descriptor.id());
  });

  // A set of the elements of |ime_list|.
  std::set<std::string> ime_set;

  // For each language, add any candidate IMEs that support it.
  for (const auto& language : enabled_languages) {
    for (const InputMethodDescriptor& descriptor : descriptors) {
      const std::string& id = descriptor.id();
      if (!base::Contains(ime_set, id) &&
          base::Contains(descriptor.language_codes(), language)) {
        ime_list.push_back(id);
        ime_set.insert(id);
      }
    }
  }

  // Add the rest of the third party IMEs
  for (const InputMethodDescriptor& descriptor : descriptors) {
    const std::string& id = descriptor.id();
    if (!base::Contains(ime_set, id)) {
      ime_list.push_back(id);
    }
  }

  return ime_list;
}

std::vector<std::string> GetInputMethodTags(
    language_settings_private::InputMethod* input_method) {
  std::vector<std::string> tags = {input_method->display_name};
  const std::string app_locale = g_browser_process->GetApplicationLocale();
  for (const auto& language_code : input_method->language_codes) {
    tags.push_back(base::UTF16ToUTF8(l10n_util::GetDisplayNameForLocale(
        language_code, app_locale, /*is_for_ui=*/true)));
  }
  return tags;
}

#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

std::unique_ptr<translate::TranslatePrefs>
CreateTranslatePrefsForBrowserContext(
    content::BrowserContext* browser_context) {}

}  // namespace

LanguageSettingsPrivateGetLanguageListFunction::
    LanguageSettingsPrivateGetLanguageListFunction() = default;

LanguageSettingsPrivateGetLanguageListFunction::
    ~LanguageSettingsPrivateGetLanguageListFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetLanguageListFunction::Run() {}

#if BUILDFLAG(IS_WIN)
void LanguageSettingsPrivateGetLanguageListFunction::
    OnDictionariesInitialized() {
  UpdateSupportedPlatformDictionaries();
  Respond(WithArguments(std::move(language_list_)));
  // Matches the AddRef in Run().
  Release();
}

void LanguageSettingsPrivateGetLanguageListFunction::
    UpdateSupportedPlatformDictionaries() {
  SpellcheckService* service =
      SpellcheckServiceFactory::GetForContext(browser_context());
  for (auto& language_val : language_list_) {
    base::Value::Dict& language_val_dict = language_val.GetDict();
    const std::string* str = language_val_dict.FindString("code");
    if (str && service->UsesWindowsDictionary(*str)) {
      language_val_dict.Set("supportsSpellcheck", true);
    }
  }
}
#endif  // BUILDFLAG(IS_WIN)

LanguageSettingsPrivateEnableLanguageFunction::
    LanguageSettingsPrivateEnableLanguageFunction() = default;

LanguageSettingsPrivateEnableLanguageFunction::
    ~LanguageSettingsPrivateEnableLanguageFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateEnableLanguageFunction::Run() {}

LanguageSettingsPrivateDisableLanguageFunction::
    LanguageSettingsPrivateDisableLanguageFunction() = default;

LanguageSettingsPrivateDisableLanguageFunction::
    ~LanguageSettingsPrivateDisableLanguageFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateDisableLanguageFunction::Run() {}

LanguageSettingsPrivateSetEnableTranslationForLanguageFunction::
    LanguageSettingsPrivateSetEnableTranslationForLanguageFunction() = default;

LanguageSettingsPrivateSetEnableTranslationForLanguageFunction::
    ~LanguageSettingsPrivateSetEnableTranslationForLanguageFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateSetEnableTranslationForLanguageFunction::Run() {}

LanguageSettingsPrivateGetAlwaysTranslateLanguagesFunction::
    LanguageSettingsPrivateGetAlwaysTranslateLanguagesFunction() = default;

LanguageSettingsPrivateGetAlwaysTranslateLanguagesFunction::
    ~LanguageSettingsPrivateGetAlwaysTranslateLanguagesFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetAlwaysTranslateLanguagesFunction::Run() {}

LanguageSettingsPrivateSetLanguageAlwaysTranslateStateFunction::
    LanguageSettingsPrivateSetLanguageAlwaysTranslateStateFunction() = default;

LanguageSettingsPrivateSetLanguageAlwaysTranslateStateFunction::
    ~LanguageSettingsPrivateSetLanguageAlwaysTranslateStateFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateSetLanguageAlwaysTranslateStateFunction::Run() {}

LanguageSettingsPrivateGetNeverTranslateLanguagesFunction::
    LanguageSettingsPrivateGetNeverTranslateLanguagesFunction() = default;

LanguageSettingsPrivateGetNeverTranslateLanguagesFunction::
    ~LanguageSettingsPrivateGetNeverTranslateLanguagesFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetNeverTranslateLanguagesFunction::Run() {}

LanguageSettingsPrivateMoveLanguageFunction::
    LanguageSettingsPrivateMoveLanguageFunction() = default;

LanguageSettingsPrivateMoveLanguageFunction::
    ~LanguageSettingsPrivateMoveLanguageFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateMoveLanguageFunction::Run() {}

LanguageSettingsPrivateGetSpellcheckDictionaryStatusesFunction::
    LanguageSettingsPrivateGetSpellcheckDictionaryStatusesFunction() = default;

LanguageSettingsPrivateGetSpellcheckDictionaryStatusesFunction::
    ~LanguageSettingsPrivateGetSpellcheckDictionaryStatusesFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetSpellcheckDictionaryStatusesFunction::Run() {}

LanguageSettingsPrivateGetSpellcheckWordsFunction::
    LanguageSettingsPrivateGetSpellcheckWordsFunction() = default;

LanguageSettingsPrivateGetSpellcheckWordsFunction::
    ~LanguageSettingsPrivateGetSpellcheckWordsFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetSpellcheckWordsFunction::Run() {}

void LanguageSettingsPrivateGetSpellcheckWordsFunction::
    OnCustomDictionaryLoaded() {}

void LanguageSettingsPrivateGetSpellcheckWordsFunction::
    OnCustomDictionaryChanged(
        const SpellcheckCustomDictionary::Change& dictionary_change) {}

base::Value::List
LanguageSettingsPrivateGetSpellcheckWordsFunction::GetSpellcheckWords() const {}

LanguageSettingsPrivateAddSpellcheckWordFunction::
    LanguageSettingsPrivateAddSpellcheckWordFunction() = default;

LanguageSettingsPrivateAddSpellcheckWordFunction::
    ~LanguageSettingsPrivateAddSpellcheckWordFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateAddSpellcheckWordFunction::Run() {}

LanguageSettingsPrivateRemoveSpellcheckWordFunction::
    LanguageSettingsPrivateRemoveSpellcheckWordFunction() = default;

LanguageSettingsPrivateRemoveSpellcheckWordFunction::
    ~LanguageSettingsPrivateRemoveSpellcheckWordFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateRemoveSpellcheckWordFunction::Run() {}

LanguageSettingsPrivateGetTranslateTargetLanguageFunction::
    LanguageSettingsPrivateGetTranslateTargetLanguageFunction() = default;

LanguageSettingsPrivateGetTranslateTargetLanguageFunction::
    ~LanguageSettingsPrivateGetTranslateTargetLanguageFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetTranslateTargetLanguageFunction::Run() {}

LanguageSettingsPrivateSetTranslateTargetLanguageFunction::
    LanguageSettingsPrivateSetTranslateTargetLanguageFunction() = default;

LanguageSettingsPrivateSetTranslateTargetLanguageFunction::
    ~LanguageSettingsPrivateSetTranslateTargetLanguageFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateSetTranslateTargetLanguageFunction::Run() {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// Populates the vector of input methods using information in the list of
// descriptors. Used for languageSettingsPrivate.getInputMethodLists().
void PopulateInputMethodListFromDescriptors(
    const InputMethodDescriptors& descriptors,
    std::vector<language_settings_private::InputMethod>* input_methods) {
  InputMethodManager* manager = InputMethodManager::Get();
  InputMethodUtil* util = manager->GetInputMethodUtil();
  scoped_refptr<InputMethodManager::State> ime_state =
      manager->GetActiveIMEState();
  if (!ime_state.get())
    return;

  const base::flat_set<std::string> enabled_ids(
      ime_state->GetEnabledInputMethodIds());
  const base::flat_set<std::string> allowed_ids(
      ime_state->GetAllowedInputMethodIds());

  UErrorCode error = U_ZERO_ERROR;
  std::unique_ptr<icu::Collator> collator(
      icu::Collator::createInstance(error));  // use current ICU locale
  DCHECK(U_SUCCESS(error));

  // Map of sorted [display name -> input methods].
  std::map<std::u16string, language_settings_private::InputMethod,
           l10n_util::StringComparator<std::u16string>>
      input_map(l10n_util::StringComparator<std::u16string>(collator.get()));

  for (const auto& descriptor : descriptors) {
    language_settings_private::InputMethod input_method;
    input_method.id = descriptor.id();
    input_method.display_name = util->GetLocalizedDisplayName(descriptor);
    input_method.language_codes = descriptor.language_codes();
    input_method.tags = GetInputMethodTags(&input_method);
    if (base::Contains(enabled_ids, input_method.id))
      input_method.enabled = true;
    if (descriptor.options_page_url().is_valid())
      input_method.has_options_page = true;
    if (!allowed_ids.empty() && !base::Contains(allowed_ids, input_method.id)) {
      input_method.is_prohibited_by_policy = true;
    }
    input_map[base::UTF8ToUTF16(util->GetLocalizedDisplayName(descriptor))] =
        std::move(input_method);
  }

  for (auto& entry : input_map) {
    input_methods->push_back(std::move(entry.second));
  }
}
#endif

LanguageSettingsPrivateGetInputMethodListsFunction::
    LanguageSettingsPrivateGetInputMethodListsFunction() = default;

LanguageSettingsPrivateGetInputMethodListsFunction::
    ~LanguageSettingsPrivateGetInputMethodListsFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateGetInputMethodListsFunction::Run() {}

LanguageSettingsPrivateAddInputMethodFunction::
    LanguageSettingsPrivateAddInputMethodFunction() = default;

LanguageSettingsPrivateAddInputMethodFunction::
    ~LanguageSettingsPrivateAddInputMethodFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateAddInputMethodFunction::Run() {}

LanguageSettingsPrivateRemoveInputMethodFunction::
    LanguageSettingsPrivateRemoveInputMethodFunction() = default;

LanguageSettingsPrivateRemoveInputMethodFunction::
    ~LanguageSettingsPrivateRemoveInputMethodFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateRemoveInputMethodFunction::Run() {}

LanguageSettingsPrivateRetryDownloadDictionaryFunction::
    LanguageSettingsPrivateRetryDownloadDictionaryFunction() = default;

LanguageSettingsPrivateRetryDownloadDictionaryFunction::
    ~LanguageSettingsPrivateRetryDownloadDictionaryFunction() = default;

ExtensionFunction::ResponseAction
LanguageSettingsPrivateRetryDownloadDictionaryFunction::Run() {}

}  // namespace extensions