#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h"
#include <optional>
#include <string>
#include <vector>
#include "base/check_deref.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.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/extensions/extension_service_test_base.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/test_browser_window.h"
#include "chrome/test/base/testing_profile.h"
#include "components/crx_file/id_util.h"
#include "components/language/core/browser/pref_names.h"
#include "components/prefs/pref_member.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/translate/core/browser/translate_download_manager.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_prefs.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/constants/ash_features.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/fake_input_method_delegate.h"
#include "ui/base/ime/ash/input_method_util.h"
#include "ui/base/ime/ash/mock_component_extension_ime_manager_delegate.h"
#include "ui/base/ime/ash/mock_input_method_manager.h"
#include "ui/base/l10n/l10n_util.h"
#endif
namespace extensions {
DictionaryStatus;
class MockLanguageSettingsPrivateDelegate
: public LanguageSettingsPrivateDelegate { … };
std::vector<DictionaryStatus>
MockLanguageSettingsPrivateDelegate::GetHunspellDictionaryStatuses() { … }
void MockLanguageSettingsPrivateDelegate::RetryDownloadHunspellDictionary(
const std::string& language) { … }
namespace {
std::unique_ptr<KeyedService> BuildEventRouter(
content::BrowserContext* profile) { … }
std::unique_ptr<KeyedService> BuildLanguageSettingsPrivateDelegate(
content::BrowserContext* profile) { … }
std::unique_ptr<KeyedService> BuildSpellcheckService(
content::BrowserContext* profile) { … }
}
class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase { … };
TEST_F(LanguageSettingsPrivateApiTest, RetryDownloadHunspellDictionaryTest) { … }
TEST_F(LanguageSettingsPrivateApiTest, GetSpellcheckDictionaryStatusesTest) { … }
TEST_F(LanguageSettingsPrivateApiTest, SetLanguageAlwaysTranslateStateTest) { … }
TEST_F(LanguageSettingsPrivateApiTest, GetAlwaysTranslateLanguagesListTest) { … }
TEST_F(LanguageSettingsPrivateApiTest, SetTranslateTargetLanguageTest) { … }
TEST_F(LanguageSettingsPrivateApiTest, GetNeverTranslateLanguagesListTest) { … }
class LanguageSettingsPrivateApiGetLanguageListTest
: public LanguageSettingsPrivateApiTest { … };
TEST_F(LanguageSettingsPrivateApiGetLanguageListTest, GetLanguageList) { … }
void LanguageSettingsPrivateApiTest::RunGetLanguageListTest() { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
namespace {
namespace input_method = ::ash::input_method;
using input_method::InputMethodDescriptor;
using input_method::InputMethodManager;
using input_method::MockComponentExtensionIMEManagerDelegate;
std::string GetExtensionImeId() {
std::string kExtensionImeId = ash::extension_ime_util::GetInputMethodID(
crx_file::id_util::GenerateId("test.extension.ime"), "us");
return kExtensionImeId;
}
std::string GetComponentExtensionImeId() {
std::string kComponentExtensionImeId =
ash::extension_ime_util::GetComponentInputMethodID(
crx_file::id_util::GenerateId("test.component.extension.ime"), "us");
return kComponentExtensionImeId;
}
std::string GetArcImeId() {
std::string kArcImeId = ash::extension_ime_util::GetArcInputMethodID(
crx_file::id_util::GenerateId("test.arc.ime"), "us");
return kArcImeId;
}
class TestInputMethodManager : public input_method::MockInputMethodManager {
public:
class TestState : public input_method::MockInputMethodManager::State {
public:
TestState() {
std::string layout("us");
InputMethodDescriptor extension_ime(
GetExtensionImeId(), "ExtensionIme", "", layout, {"vi"},
false , GURL(), GURL(),
std::nullopt);
InputMethodDescriptor component_extension_ime(
GetComponentExtensionImeId(), "ComponentExtensionIme", "", layout,
{"en-US", "en"}, false , GURL(), GURL(),
std::nullopt);
InputMethodDescriptor arc_ime(GetArcImeId(), "ArcIme", "", layout,
{ash::extension_ime_util::kArcImeLanguage},
false , GURL(),
GURL(),
std::nullopt);
input_methods_ = {extension_ime, component_extension_ime, arc_ime};
}
TestState(const TestState&) = delete;
TestState& operator=(const TestState&) = delete;
void GetInputMethodExtensions(
input_method::InputMethodDescriptors* descriptors) override {
for (const auto& descriptor : input_methods_)
descriptors->push_back(descriptor);
}
input_method::InputMethodDescriptors input_methods_;
protected:
friend base::RefCounted<InputMethodManager::State>;
~TestState() override = default;
};
TestInputMethodManager() : state_(new TestState), util_(&delegate_) {
util_.AppendInputMethods(state_->input_methods_);
component_ext_mgr_ = std::make_unique<ash::ComponentExtensionIMEManager>(
std::make_unique<MockComponentExtensionIMEManagerDelegate>());
}
TestInputMethodManager(const TestInputMethodManager&) = delete;
TestInputMethodManager& operator=(const TestInputMethodManager&) = delete;
scoped_refptr<InputMethodManager::State> GetActiveIMEState() override {
return state_;
}
input_method::InputMethodUtil* GetInputMethodUtil() override {
return &util_;
}
ash::ComponentExtensionIMEManager* GetComponentExtensionIMEManager()
override {
return component_ext_mgr_.get();
}
private:
scoped_refptr<TestState> state_;
input_method::FakeInputMethodDelegate delegate_;
input_method::InputMethodUtil util_;
std::unique_ptr<ash::ComponentExtensionIMEManager> component_ext_mgr_;
};
}
TEST_F(LanguageSettingsPrivateApiTest, GetInputMethodListsTest) {
TestInputMethodManager::Initialize(new TestInputMethodManager);
StringPrefMember enabled_imes;
enabled_imes.Init(prefs::kLanguageEnabledImes, profile()->GetPrefs());
StringPrefMember preload_engines;
preload_engines.Init(prefs::kLanguagePreloadEngines, profile()->GetPrefs());
enabled_imes.SetValue(
base::JoinString({GetExtensionImeId(), GetArcImeId()}, ","));
preload_engines.SetValue(GetComponentExtensionImeId());
auto function = base::MakeRefCounted<
LanguageSettingsPrivateGetInputMethodListsFunction>();
std::optional<base::Value> result_val =
api_test_utils::RunFunctionAndReturnSingleResult(function.get(), "[]",
profile());
ASSERT_TRUE(result_val) << function->GetError();
ASSERT_TRUE(result_val->is_dict());
const base::Value::Dict& result = result_val->GetDict();
const base::Value::List* input_methods =
result.FindList("thirdPartyExtensionImes");
ASSERT_NE(input_methods, nullptr);
EXPECT_EQ(3u, input_methods->size());
for (auto& input_method_val : *input_methods) {
const base::Value::Dict& input_method = input_method_val.GetDict();
const base::Value::List* ime_tags_ptr = input_method.FindList("tags");
ASSERT_NE(nullptr, ime_tags_ptr);
const base::Value* ime_name_ptr = input_method.Find("displayName");
EXPECT_TRUE(base::Contains(*ime_tags_ptr, CHECK_DEREF(ime_name_ptr)));
const base::Value::List* ime_language_codes_ptr =
input_method.FindList("languageCodes");
ASSERT_NE(nullptr, ime_language_codes_ptr);
for (auto& language_code : *ime_language_codes_ptr) {
std::u16string language_display_name = l10n_util::GetDisplayNameForLocale(
language_code.GetString(), "en", true);
if (!language_display_name.empty()) {
EXPECT_TRUE(
base::Contains(*ime_tags_ptr, base::Value(language_display_name)));
}
}
}
TestInputMethodManager::Shutdown();
}
TEST_F(LanguageSettingsPrivateApiTest, AddInputMethodTest) {
TestInputMethodManager::Initialize(new TestInputMethodManager);
profile()->GetPrefs()->SetString(language::prefs::kPreferredLanguages,
"en-US");
StringPrefMember enabled_imes;
enabled_imes.Init(prefs::kLanguageEnabledImes, profile()->GetPrefs());
StringPrefMember preload_engines;
preload_engines.Init(prefs::kLanguagePreloadEngines, profile()->GetPrefs());
BooleanPrefMember language_menu_enabled;
language_menu_enabled.Init(prefs::kLanguageImeMenuActivated,
profile()->GetPrefs());
enabled_imes.SetValue(std::string());
preload_engines.SetValue(std::string());
language_menu_enabled.SetValue(false);
{
auto function =
base::MakeRefCounted<LanguageSettingsPrivateAddInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetExtensionImeId() + "\"]", profile());
EXPECT_EQ(GetExtensionImeId(), enabled_imes.GetValue());
EXPECT_TRUE(preload_engines.GetValue().empty());
EXPECT_FALSE(language_menu_enabled.GetValue());
}
enabled_imes.SetValue(std::string());
preload_engines.SetValue(std::string());
language_menu_enabled.SetValue(false);
{
auto function =
base::MakeRefCounted<LanguageSettingsPrivateAddInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetComponentExtensionImeId() + "\"]",
profile());
EXPECT_TRUE(enabled_imes.GetValue().empty());
EXPECT_EQ(GetComponentExtensionImeId(), preload_engines.GetValue());
EXPECT_FALSE(language_menu_enabled.GetValue());
}
enabled_imes.SetValue(std::string());
preload_engines.SetValue(std::string());
language_menu_enabled.SetValue(false);
{
auto function =
base::MakeRefCounted<LanguageSettingsPrivateAddInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetArcImeId() + "\"]", profile());
EXPECT_EQ(GetArcImeId(), enabled_imes.GetValue());
EXPECT_TRUE(preload_engines.GetValue().empty());
EXPECT_FALSE(language_menu_enabled.GetValue());
}
enabled_imes.SetValue(std::string());
preload_engines.SetValue(std::string());
language_menu_enabled.SetValue(false);
{
auto function =
base::MakeRefCounted<LanguageSettingsPrivateAddInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetExtensionImeId() + "\"]", profile());
function =
base::MakeRefCounted<LanguageSettingsPrivateAddInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetComponentExtensionImeId() + "\"]",
profile());
EXPECT_EQ(GetExtensionImeId(), enabled_imes.GetValue());
EXPECT_EQ(GetComponentExtensionImeId(), preload_engines.GetValue());
EXPECT_TRUE(language_menu_enabled.GetValue());
}
TestInputMethodManager::Shutdown();
}
TEST_F(LanguageSettingsPrivateApiTest, RemoveInputMethodTest) {
TestInputMethodManager::Initialize(new TestInputMethodManager);
StringPrefMember enabled_imes;
enabled_imes.Init(prefs::kLanguageEnabledImes, profile()->GetPrefs());
StringPrefMember preload_engines;
preload_engines.Init(prefs::kLanguagePreloadEngines, profile()->GetPrefs());
enabled_imes.SetValue(
base::JoinString({GetExtensionImeId(), GetArcImeId()}, ","));
preload_engines.SetValue(GetComponentExtensionImeId());
{
auto function = base::MakeRefCounted<
LanguageSettingsPrivateRemoveInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetExtensionImeId() + "\"]", profile());
EXPECT_EQ(GetArcImeId(), enabled_imes.GetValue());
EXPECT_EQ(GetComponentExtensionImeId(), preload_engines.GetValue());
}
{
auto function = base::MakeRefCounted<
LanguageSettingsPrivateRemoveInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetComponentExtensionImeId() + "\"]",
profile());
EXPECT_EQ(GetArcImeId(), enabled_imes.GetValue());
EXPECT_TRUE(preload_engines.GetValue().empty());
}
{
auto function = base::MakeRefCounted<
LanguageSettingsPrivateRemoveInputMethodFunction>();
api_test_utils::RunFunctionAndReturnSingleResult(
function.get(), "[\"" + GetArcImeId() + "\"]", profile());
EXPECT_TRUE(enabled_imes.GetValue().empty());
EXPECT_TRUE(preload_engines.GetValue().empty());
}
TestInputMethodManager::Shutdown();
}
#endif
#if BUILDFLAG(IS_WIN)
class LanguageSettingsPrivateApiTestDelayInit
: public LanguageSettingsPrivateApiTest {
public:
LanguageSettingsPrivateApiTestDelayInit() = default;
protected:
void InitFeatures() override {
feature_list_.InitAndEnableFeature(
spellcheck::kWinDelaySpellcheckServiceInit);
}
void AddSpellcheckLanguagesForTesting(
const std::vector<std::string>& spellcheck_languages_for_testing)
override {
SpellcheckServiceFactory::GetInstance()
->GetForContext(profile())
->AddSpellcheckLanguagesForTesting(spellcheck_languages_for_testing);
}
};
TEST_F(LanguageSettingsPrivateApiTestDelayInit, GetLanguageListTest) {
RunGetLanguageListTest();
}
#endif
}