// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include <vector>
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/memory/ref_counted.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/extensions/input_method_event_router.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/api/test/test_api.h"
#include "extensions/common/switches.h"
#include "extensions/test/extension_test_message_listener.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ime/ash/extension_ime_util.h"
#include "ui/base/ime/ash/ime_bridge.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"
namespace {
using ::ash::extension_ime_util::GetInputMethodIDByEngineID;
using ::ash::input_method::InputMethodDescriptor;
using ::ash::input_method::InputMethodManager;
const char kLoginScreenUILanguage[] = "fr";
const char kInitialInputMethodOnLoginScreen[] = "xkb:us::eng";
const char kTestIMEID[] = "_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest";
const char kTestIMEID2[] = "_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest2";
const InputMethodDescriptor CreateInputMethodDescriptor(
const std::string& engineId,
const std::string& indicator,
const std::string& layout,
const std::vector<std::string>& language_codes) {
return InputMethodDescriptor(GetInputMethodIDByEngineID(engineId), "",
indicator, {layout}, language_codes, true,
GURL(), GURL(),
/*handwriting_language=*/std::nullopt);
}
class ExtensionInputMethodApiTest : public extensions::ExtensionApiTest {
public:
ExtensionInputMethodApiTest() {}
ExtensionInputMethodApiTest(const ExtensionInputMethodApiTest&) = delete;
ExtensionInputMethodApiTest& operator=(const ExtensionInputMethodApiTest&) =
delete;
~ExtensionInputMethodApiTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
extensions::ExtensionApiTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(
extensions::switches::kAllowlistedExtensionID,
"ilanclmaeigfpnmdlgelmhkpkegdioip");
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, Basic) {
const std::vector<InputMethodDescriptor> xkb_input_method_descriptors = {
CreateInputMethodDescriptor("xkb:us::eng", "US", "us",
{"en", "en-US", "en-AU", "en-NZ"}),
CreateInputMethodDescriptor("xkb:fr::fra", "FR", "fr(oss)",
{"fr", "fr-FR"}),
CreateInputMethodDescriptor("xkb:fr:bepo:fra", "FR", "fr(bepo)",
{"fr", "fr-FR"}),
CreateInputMethodDescriptor("xkb:be::fra", "BE", "fr(be)", {"fr"}),
CreateInputMethodDescriptor("xkb:ca::fra", "CA", "ca", {"fr", "fr-CA"}),
CreateInputMethodDescriptor("xkb:ch:fr::fra", "CH", "ch(fr)",
{"fr", "fr-CH"}),
CreateInputMethodDescriptor("xkb:ca:multix:fra", "CA", "ca(multix)",
{"fr", "fr-CA"}),
};
// Initializes IMF for testing.
InputMethodManager* manager = InputMethodManager::Get();
manager->GetInputMethodUtil()->InitXkbInputMethodsForTesting(
xkb_input_method_descriptors);
std::vector<std::string> keyboard_layouts;
keyboard_layouts.push_back(
GetInputMethodIDByEngineID(kInitialInputMethodOnLoginScreen));
manager->GetActiveIMEState()->EnableLoginLayouts(kLoginScreenUILanguage,
keyboard_layouts);
ASSERT_TRUE(RunExtensionTest("input_method/basic")) << message_;
}
// TODO(crbug.com/41478266): Flaky on multiple platforms.
IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, DISABLED_Typing) {
// Enable the test IME from the test extension.
std::vector<std::string> extension_ime_ids = {
"_ext_ime_ilanclmaeigfpnmdlgelmhkpkegdioiptest"};
InputMethodManager::Get()->GetActiveIMEState()->SetEnabledExtensionImes(
extension_ime_ids);
GURL test_url = ui_test_utils::GetTestUrl(
base::FilePath("extensions/api_test/input_method/typing/"),
base::FilePath("test_page.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));
ASSERT_TRUE(RunExtensionTest("input_method/typing")) << message_;
}
IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, ImeMenuActivation) {
// Listener for IME menu initial state ready.
ExtensionTestMessageListener config_listener("config_ready");
// Listener for IME menu event ready.
ExtensionTestMessageListener event_listener("event_ready");
browser()->profile()->GetPrefs()->SetBoolean(prefs::kLanguageImeMenuActivated,
true);
// Test the initial state and add listener for IME menu activation change.
ASSERT_TRUE(
LoadExtension(test_data_dir_.AppendASCII("input_method/ime_menu")));
ASSERT_TRUE(config_listener.WaitUntilSatisfied()) << message_;
// Trigger chrome.inputMethodPrivate.onImeMenuActivationChanged() event.
browser()->profile()->GetPrefs()->SetBoolean(prefs::kLanguageImeMenuActivated,
false);
// Test that the extension gets the IME activation change event properly.
ASSERT_TRUE(event_listener.WaitUntilSatisfied()) << message_;
}
IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, ImeMenuAPITest) {
ExtensionTestMessageListener activated_listener("activated");
ExtensionTestMessageListener menu_listener("get_menu_update");
ExtensionTestMessageListener list_listenter("list_change");
browser()->profile()->GetPrefs()->SetBoolean(prefs::kLanguageImeMenuActivated,
true);
ASSERT_TRUE(
LoadExtension(test_data_dir_.AppendASCII("input_method/ime_menu2")));
std::vector<std::string> extension_ime_ids;
extension_ime_ids.push_back(kTestIMEID);
extension_ime_ids.push_back(kTestIMEID2);
InputMethodManager::Get()->GetActiveIMEState()->SetEnabledExtensionImes(
extension_ime_ids);
ash::input_method::InputMethodDescriptors extension_imes;
InputMethodManager::Get()->GetActiveIMEState()->GetInputMethodExtensions(
&extension_imes);
InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
kTestIMEID, false /* show_message */);
ash::TextInputMethod* engine_handler =
ash::IMEBridge::Get()->GetCurrentEngineHandler();
ASSERT_TRUE(engine_handler);
ASSERT_TRUE(activated_listener.WaitUntilSatisfied()) << message_;
ASSERT_TRUE(menu_listener.WaitUntilSatisfied()) << message_;
InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
kTestIMEID2, false /* show_message */);
ASSERT_TRUE(list_listenter.WaitUntilSatisfied()) << message_;
}
IN_PROC_BROWSER_TEST_F(ExtensionInputMethodApiTest, Settings) {
ASSERT_TRUE(RunExtensionTest("input_method/settings")) << message_;
}