0

[OOBE] - Enable non-login input methods in OOBE

Goal of this CL is to lift login-only input methods restriction in OOBE
and enable typing in previously requested languages during
pre-onboarding part of OOBE (specifically, Japanese and Arabic).

CL implements the following steps:

* Add OOBE-specific allowlist of input method ids as a mechanism to enable input methods in OOBE per request. At the beginning we add only Japanese and Arabic IMs there among non-XKB IMs.
* Introduce new method `EnableOobeInputMethods` that will replace usages of the `EnableLoginLayouts` in all OOBE contexts. Main difference is usage of the `IsOobeAllowlisted` function to decide whether enable input method or not.
* `IsOobeAllowlisted` enables only allowlisted input methods.
* Logic of the `EnableLoginLayouts` remains the same to not change current language flows of login screen and kiosk.
* Extend `oobe_localization_browsertest` to support new changes.

Bug: b:260013276
Change-Id: I33397725fd37f3b417939e8618ac5796343dc5b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6375683
Reviewed-by: Bao-Duy Tran <tranbaoduy@chromium.org>
Commit-Queue: Bohdan Tyshchenko <bohdanty@google.com>
Reviewed-by: Danila Kuzmin <dkuzmin@google.com>
Cr-Commit-Position: refs/heads/main@{#1442615}
This commit is contained in:
Bohdan Tyshchenko
2025-04-04 01:18:30 -07:00
committed by Chromium LUCI CQ
parent 2470a124f2
commit e10682f3e9
17 changed files with 309 additions and 84 deletions

@ -301,8 +301,61 @@ void InputMethodManagerImpl::StateImpl::EnableLoginLayouts(
}
}
manager_->GetMigratedInputMethodIDs(&layouts);
enabled_input_method_ids_.swap(layouts);
// Empty |initial_layouts| work too.
FinalizeInputMethodsEnabling(layouts, initial_layouts);
}
void InputMethodManagerImpl::StateImpl::EnableOobeInputMethods(
const std::string& language_code,
const std::vector<std::string>& initial_input_methods) {
if (IsShuttingDown()) {
return;
}
// First, hardware input methods should be shown.
std::vector<std::string> candidates =
manager_->util_.GetHardwareInputMethodIds();
// Second, locale based input method should be shown.
// Add input methods associated with the language.
std::vector<std::string> input_methods_from_locale;
manager_->util_.GetInputMethodIdsFromLanguageCode(
language_code, kAllInputMethods, &input_methods_from_locale);
candidates.insert(candidates.end(), input_methods_from_locale.begin(),
input_methods_from_locale.end());
std::vector<std::string> resulting_input_methods;
// First, add the initial input method ID, if it's requested, to
// resulting_input_methods, so it appears first on the list of enabled input
// methods at the input language status menu.
for (const auto& initial_input_method : initial_input_methods) {
if (manager_->util_.IsValidInputMethodId(initial_input_method) &&
manager_->util_.IsOobeAllowlisted(initial_input_method) &&
IsInputMethodAllowed(initial_input_method)) {
resulting_input_methods.push_back(initial_input_method);
}
}
// Add candidates to resulting_input_methods, while skipping duplicates.
for (const auto& candidate : candidates) {
// Not efficient, but should be fine, as the two vectors are very
// short (2-5 items).
if (!base::Contains(resulting_input_methods, candidate) &&
manager_->util_.IsOobeAllowlisted(candidate) &&
IsInputMethodAllowed(candidate)) {
resulting_input_methods.push_back(candidate);
}
}
// Empty |initial_input_methods| work too.
FinalizeInputMethodsEnabling(resulting_input_methods, initial_input_methods);
}
void InputMethodManagerImpl::StateImpl::FinalizeInputMethodsEnabling(
std::vector<std::string>& input_methods_to_enable,
const std::vector<std::string>& initial_input_methods) {
manager_->GetMigratedInputMethodIDs(&input_methods_to_enable);
enabled_input_method_ids_.swap(input_methods_to_enable);
if (IsActive()) {
// Initialize candidate window controller and widgets such as
@ -312,11 +365,10 @@ void InputMethodManagerImpl::StateImpl::EnableLoginLayouts(
manager_->MaybeInitializeCandidateWindowController();
}
// you can pass empty |initial_layout|.
ChangeInputMethod(initial_layouts.empty()
ChangeInputMethod(initial_input_methods.empty()
? std::string()
: extension_ime_util::GetInputMethodIDByEngineID(
initial_layouts[0]),
initial_input_methods[0]),
false);
}
}
@ -707,34 +759,34 @@ void InputMethodManagerImpl::StateImpl::SetEnabledExtensionImes(
void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefaultFromVPD(
const std::string& locale,
const std::string& oem_layout) {
std::string layout;
std::string input_method_id;
if (!oem_layout.empty()) {
// If the OEM layout information is provided, use it.
layout = oem_layout;
input_method_id = oem_layout;
} else {
// Otherwise, determine the hardware keyboard from the locale.
std::vector<std::string> input_method_ids;
if (manager_->util_.GetInputMethodIdsFromLanguageCode(
locale, kKeyboardLayoutsOnly, &input_method_ids)) {
locale, kAllInputMethods, &input_method_ids)) {
// The output list |input_method_ids| is sorted by popularity, hence
// input_method_ids[0] now contains the most popular keyboard layout
// for the given locale.
DCHECK_GE(input_method_ids.size(), 1U);
layout = input_method_ids[0];
input_method_id = input_method_ids[0];
}
}
if (layout.empty()) {
if (input_method_id.empty()) {
return;
}
std::vector<std::string> layouts = base::SplitString(
layout, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
manager_->GetMigratedInputMethodIDs(&layouts);
std::vector<std::string> input_method_ids = base::SplitString(
input_method_id, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
manager_->GetMigratedInputMethodIDs(&input_method_ids);
PrefService* prefs = g_browser_process->local_state();
prefs->SetString(prefs::kHardwareKeyboardLayout,
base::JoinString(layouts, ","));
base::JoinString(input_method_ids, ","));
// This asks the file thread to save the prefs (i.e. doesn't block).
// The latest values of Local State reside in memory so we can safely
@ -744,11 +796,13 @@ void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefaultFromVPD(
manager_->util_.UpdateHardwareLayoutCache();
EnableLoginLayouts(locale, layouts);
// This function is called only during system setup in OOBE.
EnableOobeInputMethods(locale, input_method_ids);
LoadNecessaryComponentExtensions();
}
void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefault() {
void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefault(
bool is_in_oobe_context) {
// Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
// and US dvorak keyboard layouts.
if (g_browser_process && g_browser_process->local_state()) {
@ -771,7 +825,11 @@ void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefault() {
input_method_ids_to_be_enabled.push_back(initial_input_method_id);
}
}
EnableLoginLayouts(locale, input_method_ids_to_be_enabled);
if (is_in_oobe_context) {
EnableOobeInputMethods(locale, input_method_ids_to_be_enabled);
} else {
EnableLoginLayouts(locale, input_method_ids_to_be_enabled);
}
LoadNecessaryComponentExtensions();
}
}

@ -84,6 +84,9 @@ class InputMethodManagerImpl : public InputMethodManager,
void EnableLoginLayouts(
const std::string& language_code,
const std::vector<std::string>& initial_layouts) override;
void EnableOobeInputMethods(
const std::string& language_code,
const std::vector<std::string>& initial_input_methods) override;
void DisableNonLockScreenLayouts() override;
void GetInputMethodExtensions(InputMethodDescriptors* result) override;
InputMethodDescriptors GetEnabledInputMethodsSortedByLocalizedDisplayNames()
@ -94,7 +97,7 @@ class InputMethodManagerImpl : public InputMethodManager,
const std::string& input_method_id) const override;
size_t GetNumEnabledInputMethods() const override;
void SetEnabledExtensionImes(base::span<const std::string> ids) override;
void SetInputMethodLoginDefault() override;
void SetInputMethodLoginDefault(bool is_in_oobe_context) override;
void SetInputMethodLoginDefaultFromVPD(const std::string& locale,
const std::string& layout) override;
void SwitchToNextInputMethod() override;
@ -135,6 +138,14 @@ class InputMethodManagerImpl : public InputMethodManager,
const InputMethodDescriptor* LookupInputMethod(
const std::string& input_method_id);
// Replaces currently enabled input methnods ids list with the
// |input_methods_to_enable|. Initializes candidate window controller and
// activates first entry of |initial_input_methods| if caller's state is in
// the active state and |initial_input_methods| is not empty.
void FinalizeInputMethodsEnabling(
std::vector<std::string>& input_methods_to_enable,
const std::vector<std::string>& initial_input_methods);
const raw_ptr<Profile, DanglingUntriaged> profile_;
const raw_ptr<InputMethodManagerImpl, DanglingUntriaged> manager_;

@ -1574,7 +1574,8 @@ TEST_F(InputMethodManagerImplTest, SetLoginDefaultWithAllowedInputMethods) {
EXPECT_TRUE(manager_->GetActiveIMEState()->SetAllowedInputMethods(allowed));
EXPECT_TRUE(manager_->GetActiveIMEState()->ReplaceEnabledInputMethods(
manager_->GetActiveIMEState()->GetAllowedInputMethodIds()));
manager_->GetActiveIMEState()->SetInputMethodLoginDefault();
manager_->GetActiveIMEState()->SetInputMethodLoginDefault(
/*is_in_oobe_context=*/false);
EXPECT_THAT(manager_->GetActiveIMEState()->GetEnabledInputMethodIds(),
testing::ElementsAre(ImeIdFromEngineId("xkb:us::eng"),
ImeIdFromEngineId("xkb:de::ger"),

@ -70,7 +70,7 @@ void SetUserInputMethod(const AccountId& account_id,
DVLOG(0) << "SetUserInputMethod: failed to set user layout. Switching to "
"default.";
ime_state->SetInputMethodLoginDefault();
ime_state->SetInputMethodLoginDefault(false /* is_in_oobe_context */);
}
}
@ -134,7 +134,7 @@ void StopEnforcingPolicyInputMethods() {
imm_state->SetAllowedInputMethods(std::vector<std::string>());
if (ImeControllerClientImpl::Get()) // Can be null in tests.
ImeControllerClientImpl::Get()->SetImesManagedByPolicy(false);
imm_state->SetInputMethodLoginDefault();
imm_state->SetInputMethodLoginDefault(false /* is_in_oobe_context */);
}
void SetKeyboardSettings(const AccountId& account_id) {

@ -102,13 +102,18 @@ struct LocalizationTestParams {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
// ------------------ Non-Latin setup
// For a non-Latin keyboard layout like Russian, we expect to see the US
// keyboard.
{"ru", "xkb:ru::rus", "ru", kUSLayout, "xkb:us::eng"},
{"ru", "xkb:us::eng,xkb:ru::rus", "ru", kUSLayout, "xkb:us::eng"},
// keyboard and XKB-IMs attached to a given locale.
{"ru", "xkb:ru::rus", "ru", kUSLayout,
"xkb:us::eng,xkb:ru::rus,[xkb:ru:phonetic:rus]"},
{"ru", "xkb:us::eng,xkb:ru::rus", "ru", kUSLayout,
"xkb:us::eng,xkb:ru::rus,[xkb:ru:phonetic:rus]"},
// IMEs do not load at OOBE, so we just expect to see the (Latin) Japanese
// keyboard.
{"ja", "xkb:jp::jpn", "ja", "xkb:jp::jpn", "xkb:jp::jpn,[xkb:us::eng]"},
// Only allowlisted IMEs are available during OOBE, if given locale doesn't
// have any allowlisted IMs attached we will see only Latin IM enabled.
{"ja", "xkb:jp::jpn", "ja", "xkb:jp::jpn",
"xkb:jp::jpn,[nacl_mozc_us,nacl_mozc_jp,xkb:us::eng]"},
{"ar", "xkb:us::eng", "ar", "xkb:us::eng", "xkb:us::eng,[vkd_ar]"},
{"ko", "xkb:us::eng", "ko", "xkb:us::eng", "xkb:us::eng"},
// We don't use the Icelandic locale but the Icelandic keyboard layout
// should still be selected when specified as the default.
@ -130,7 +135,8 @@ struct LocalizationTestParams {
{"es,en-US,nl", "xkb:be::nld", "es,en-US,nl", "xkb:be::nld",
"xkb:be::nld,[xkb:es::spa,xkb:latam::spa,xkb:us::eng]"},
{"ru,de", "xkb:ru::rus", "ru,de", kUSLayout, "xkb:us::eng"},
{"ru,de", "xkb:ru::rus", "ru,de", kUSLayout,
"xkb:us::eng,xkb:ru::rus,[xkb:ru:phonetic:rus]"},
// ------------------ Regional Locales
// Synthetic example to test correct merging of different locales.
@ -146,13 +152,18 @@ struct LocalizationTestParams {
#else
// ------------------ Non-Latin setup
// For a non-Latin keyboard layout like Russian, we expect to see the US
// keyboard.
{"ru", "xkb:ru::rus", "ru", kUSLayout, "xkb:us::eng"},
{"ru", "xkb:us::eng,xkb:ru::rus", "ru", kUSLayout, "xkb:us::eng"},
// keyboard and XKB-IMs attached to a given locale.
{"ru", "xkb:ru::rus", "ru", kUSLayout,
"xkb:us::eng,xkb:ru::rus,[xkb:ru:phonetic:rus]"},
{"ru", "xkb:us::eng,xkb:ru::rus", "ru", kUSLayout,
"xkb:us::eng,xkb:ru::rus,[xkb:ru:phonetic:rus]"},
// IMEs do not load at OOBE, so we just expect to see the (Latin) Japanese
// keyboard.
{"ja", "xkb:jp::jpn", "ja", "xkb:jp::jpn", "xkb:jp::jpn,[xkb:us::eng]"},
// Only allowlisted IMEs are available during OOBE, if given locale doesn't
// have any allowlisted IMs attached we will see only Latin IM enabled.
{"ja", "xkb:jp::jpn", "ja", "xkb:jp::jpn",
"xkb:jp::jpn,[nacl_mozc_us,nacl_mozc_jp,xkb:us::eng]"},
{"ar", "xkb:us::eng", "ar", "xkb:us::eng", "xkb:us::eng,[vkd_ar]"},
{"ko", "xkb:us::eng", "ko", "xkb:us::eng", "xkb:us::eng"},
// We don't use the Icelandic locale but the Icelandic keyboard layout
// should still be selected when specified as the default.
@ -174,7 +185,8 @@ struct LocalizationTestParams {
{"es,en-US,nl", "xkb:be::nld", "es,en-US,nl", "xkb:be::nld",
"xkb:be::nld,[xkb:es::spa,xkb:latam::spa,xkb:us::eng]"},
{"ru,de", "xkb:ru::rus", "ru,de", kUSLayout, "xkb:us::eng"},
{"ru,de", "xkb:ru::rus", "ru,de", kUSLayout,
"xkb:us::eng,xkb:ru::rus,[xkb:ru:phonetic:rus]"},
// ------------------ Regional Locales
// Synthetic example to test correct merging of different locales.
@ -323,16 +335,24 @@ std::string OobeLocalizationTest::DumpOptions(const char* select_id) {
return test::OobeJS().GetString(expression);
}
std::string TranslateXKB2Extension(const std::string& src) {
std::string TranslateLocal2Global(const std::string& src) {
std::string result(src);
// Modifies the expected keyboard select control options for the new
// extension based xkb id.
size_t pos = 0;
std::string repl_old = "xkb:";
std::string repl_new = extension_ime_util::GetInputMethodIDByEngineID("xkb:");
while ((pos = result.find(repl_old, pos)) != std::string::npos) {
result.replace(pos, repl_old.length(), repl_new);
pos += repl_new.length();
const std::vector<std::string> replacements = {
"xkb:",
"nacl_mozc_",
"vkd_",
};
for (const auto& replacement : replacements) {
// Modifies the expected keyboard select control options for the new
// extension based id.
size_t pos = 0;
std::string repl_new =
extension_ime_util::GetInputMethodIDByEngineID(replacement);
while ((pos = result.find(replacement, pos)) != std::string::npos) {
result.replace(pos, replacement.length(), repl_new);
pos += repl_new.length();
}
}
return result;
}
@ -346,7 +366,7 @@ void OobeLocalizationTest::RunLocalizationTest() {
GetParam()->expected_keyboard_select_control);
const std::string expected_keyboard_select =
TranslateXKB2Extension(expected_keyboard_select_control);
TranslateLocal2Global(expected_keyboard_select_control);
ASSERT_NO_FATAL_FAILURE(LanguageListWaiter().RunUntilLanguageListReady());
@ -361,7 +381,7 @@ void OobeLocalizationTest::RunLocalizationTest() {
<< DumpOptions(kLanguageSelect);
ASSERT_NO_FATAL_FAILURE(VerifyInitialOptions(
kKeyboardSelect, TranslateXKB2Extension(expected_keyboard_layout).c_str(),
kKeyboardSelect, TranslateLocal2Global(expected_keyboard_layout).c_str(),
false))
<< "Actual value of " << kKeyboardSelect << ":\n"
<< DumpOptions(kKeyboardSelect);

@ -244,9 +244,8 @@ void WelcomeScreen::SetApplicationLocaleAndInputMethod(
base::BindOnce(&WelcomeScreen::OnLanguageChangedCallback,
language_weak_ptr_factory_.GetWeakPtr(),
base::Owned(new InputEventsBlocker), input_method));
locale_util::SwitchLanguage(locale, true /* enableLocaleKeyboardLayouts */,
true /* login_layouts_only */,
std::move(callback),
locale_util::SwitchLanguage(locale, /*enable_locale_keyboard_layouts=*/true,
/*login_layouts_only=*/false, std::move(callback),
ProfileManager::GetActiveUserProfile());
}
@ -272,9 +271,8 @@ void WelcomeScreen::SetApplicationLocale(const std::string& locale,
base::BindOnce(&WelcomeScreen::OnLanguageChangedCallback,
language_weak_ptr_factory_.GetWeakPtr(),
base::Owned(new InputEventsBlocker), std::string()));
locale_util::SwitchLanguage(locale, true /* enableLocaleKeyboardLayouts */,
true /* login_layouts_only */,
std::move(callback),
locale_util::SwitchLanguage(locale, /*enable_locale_keyboard_layouts=*/true,
/*login_layouts_only=*/false, std::move(callback),
ProfileManager::GetActiveUserProfile());
if (is_from_ui) {
// Write into the local state to save data about locale changes in case of

@ -82,7 +82,7 @@ void StartKioskSession(KioskAppId app, bool is_auto_launch = false) {
CHECK_DEREF(input_method::InputMethodManager::Get())
.GetActiveIMEState()
->SetInputMethodLoginDefault();
->SetInputMethodLoginDefault(/*is_in_oobe_context=*/false);
// Manages its own lifetime. See ShutdownDisplayHost().
auto* display_host = new LoginDisplayHostWebUI();

@ -124,8 +124,7 @@ void SetGaiaInputMethods(const AccountId& account_id) {
lock_screen_utils::EnforceDevicePolicyInputMethods(std::string());
std::vector<std::string> input_method_ids;
if (gaia_ime_state->GetAllowedInputMethodIds().empty()) {
input_method_ids =
imm->GetInputMethodUtil()->GetHardwareLoginInputMethodIds();
input_method_ids = imm->GetInputMethodUtil()->GetHardwareInputMethodIds();
} else {
input_method_ids = gaia_ime_state->GetAllowedInputMethodIds();
}
@ -139,7 +138,7 @@ void SetGaiaInputMethods(const AccountId& account_id) {
PushFrontImIfNotExists(owner_input_method_id, &input_method_ids);
PushFrontImIfNotExists(system_input_method_id, &input_method_ids);
gaia_ime_state->EnableLoginLayouts(
gaia_ime_state->EnableOobeInputMethods(
g_browser_process->GetApplicationLocale(), input_method_ids);
if (!system_input_method_id.empty()) {

@ -391,7 +391,8 @@ void OnLanguageSwitchedCallback(
// (`switch_locale` is empty) or after a locale switch otherwise.
void TriggerShowLoginWizardFinish(
std::string switch_locale,
std::unique_ptr<ShowLoginWizardSwitchLanguageCallbackData> data) {
std::unique_ptr<ShowLoginWizardSwitchLanguageCallbackData> data,
bool login_input_methods_only) {
if (switch_locale.empty()) {
ShowLoginWizardFinish(data->first_screen, data->startup_manifest);
} else {
@ -400,9 +401,10 @@ void TriggerShowLoginWizardFinish(
// Load locale keyboards here. Hardware layout would be automatically
// enabled.
locale_util::SwitchLanguage(
switch_locale, true, true /* login_layouts_only */, std::move(callback),
ProfileManager::GetActiveUserProfile());
locale_util::SwitchLanguage(switch_locale,
/*enable_locale_keyboard_layouts=*/true,
login_input_methods_only, std::move(callback),
ProfileManager::GetActiveUserProfile());
}
}
@ -1205,7 +1207,8 @@ void ShowLoginWizard(OobeScreenId first_screen) {
input_method::InputMethodManager::Get();
if (g_browser_process && g_browser_process->local_state()) {
manager->GetActiveIMEState()->SetInputMethodLoginDefault();
manager->GetActiveIMEState()->SetInputMethodLoginDefault(
/*is_in_oobe_context=*/true);
}
system::InputDeviceSettings::Get()->SetNaturalScroll(
@ -1266,7 +1269,8 @@ void ShowLoginWizard(OobeScreenId first_screen) {
std::unique_ptr<ShowLoginWizardSwitchLanguageCallbackData> data =
std::make_unique<ShowLoginWizardSwitchLanguageCallbackData>(
first_screen, nullptr);
TriggerShowLoginWizardFinish(switch_locale, std::move(data));
TriggerShowLoginWizardFinish(switch_locale, std::move(data),
/*login_input_methods_only=*/true);
return;
}
@ -1293,7 +1297,8 @@ void ShowLoginWizard(OobeScreenId first_screen) {
startup_manifest));
if (!current_locale.empty() || locale.empty()) {
TriggerShowLoginWizardFinish(std::string(), std::move(data));
TriggerShowLoginWizardFinish(std::string(), std::move(data),
/*login_input_methods_only=*/false);
return;
}
@ -1304,7 +1309,8 @@ void ShowLoginWizard(OobeScreenId first_screen) {
prefs->SetString(language::prefs::kApplicationLocale, locale);
StartupUtils::SetInitialLocale(locale);
TriggerShowLoginWizardFinish(locale, std::move(data));
TriggerShowLoginWizardFinish(locale, std::move(data),
/*login_input_methods_only=*/false);
}
void SwitchWebUItoMojo() {

@ -500,7 +500,7 @@ std::string FindMostRelevantLocale(
return fallback_locale;
}
base::Value::List GetAndActivateLoginKeyboardLayouts(
base::Value::List GetAndActivateOobeInputMethods(
const std::string& locale,
const std::string& selected,
input_method::InputMethodManager* input_method_manager) {
@ -508,24 +508,24 @@ base::Value::List GetAndActivateLoginKeyboardLayouts(
input_method::InputMethodUtil* util =
input_method_manager->GetInputMethodUtil();
const std::vector<std::string>& hardware_login_input_methods =
util->GetHardwareLoginInputMethodIds();
const std::vector<std::string>& hardware_input_methods =
util->GetHardwareInputMethodIds();
DCHECK(
ProfileHelper::IsSigninProfile(ProfileManager::GetActiveUserProfile()));
input_method_manager->GetActiveIMEState()->EnableLoginLayouts(
locale, hardware_login_input_methods);
input_method_manager->GetActiveIMEState()->EnableOobeInputMethods(
locale, hardware_input_methods);
input_method::InputMethodDescriptors input_methods(
input_method_manager->GetActiveIMEState()->GetEnabledInputMethods());
std::set<std::string> input_methods_added;
for (const auto& hardware_login_input_method : hardware_login_input_methods) {
for (const auto& hardware_input_method : hardware_input_methods) {
const input_method::InputMethodDescriptor* ime =
util->GetInputMethodDescriptorFromId(hardware_login_input_method);
util->GetInputMethodDescriptorFromId(hardware_input_method);
// Do not crash in case of misconfiguration.
if (ime) {
input_methods_added.insert(hardware_login_input_method);
input_methods_added.insert(hardware_input_method);
input_methods_list.Append(CreateInputMethodsEntry(*ime, selected, util));
} else {
NOTREACHED();

@ -65,16 +65,16 @@ std::string FindMostRelevantLocale(
const base::Value::List& available_locales,
const std::string& fallback_locale);
// Return a list of keyboard layouts that can be used for `locale` on the login
// Return a list of input methods that can be used for `locale` on the welcome
// screen. Each list entry is a dictionary that contains data such as an ID and
// a display name. The list will consist of the device's hardware layouts,
// followed by a divider and locale-specific keyboard layouts, if any. The list
// will also always contain the US keyboard layout. If `selected` matches the ID
// followed by a divider and locale-specific input method IDs, if any. The list
// will also always contain the US keyboard ID. If `selected` matches the ID
// of any entry in the resulting list, that entry will be marked as selected.
// In addition to returning the list of keyboard layouts, this function also
// activates them, so that they can be selected by the user (e.g. by cycling
// through keyboard layouts via keyboard shortcuts).
base::Value::List GetAndActivateLoginKeyboardLayouts(
// In addition to returning the list of IDs, this function also activates them,
// so that they can be selected by the user (e.g. by cycling through input
// methods via keyboard shortcuts).
base::Value::List GetAndActivateOobeInputMethods(
const std::string& locale,
const std::string& selected,
input_method::InputMethodManager* input_method_manager);

@ -261,7 +261,7 @@ void WelcomeScreenHandler::GetAdditionalParameters(base::Value::Dict* dict) {
}
dict->Set("languageList", std::move(language_list));
dict->Set("inputMethodsList", GetAndActivateLoginKeyboardLayouts(
dict->Set("inputMethodsList", GetAndActivateOobeInputMethods(
application_locale, selected_input_method,
input_method_manager));
dict->Set("timezoneList", GetTimezoneList());

@ -174,6 +174,18 @@ class COMPONENT_EXPORT(UI_BASE_IME_ASH) InputMethodManager {
const std::string& language_code,
const std::vector<std::string>& initial_layouts) = 0;
// Enables OOBE-eligible (based on the allowlist) input methods that are
// attached to the |language_code| and then switches to
// |initial_input_methods| if the given list is not empty.
// For example, if |language_code| is "en-US", US Qwerty, US International,
// US Extended, US Dvorak, and US Colemak input methods would be enabled.
// Likewise, for Japan locale, "Alphanumeric with Japanese keyboard"
// together with the "fuzzy" Japanese input methods will be enabled as they
// are part of the allowlist.
virtual void EnableOobeInputMethods(
const std::string& language_code,
const std::vector<std::string>& initial_input_methods) = 0;
// Filters current state layouts and leaves only suitable for lock screen.
virtual void DisableNonLockScreenLayouts() = 0;
@ -212,10 +224,11 @@ class COMPONENT_EXPORT(UI_BASE_IME_ASH) InputMethodManager {
virtual void SetEnabledExtensionImes(base::span<const std::string> ids) = 0;
// Sets current input method to login default (first owners, then hardware).
virtual void SetInputMethodLoginDefault() = 0;
virtual void SetInputMethodLoginDefault(bool is_in_oobe_context) = 0;
// Sets current input method to login default with the given locale and
// layout info from VPD.
// This function is called only during system setup in OOBE.
virtual void SetInputMethodLoginDefaultFromVPD(
const std::string& locale,
const std::string& layout) = 0;

@ -395,6 +395,98 @@ const struct InputMethodNameMap {
{"__MSG_TRANSLITERATION_UR__", IDS_IME_NAME_TRANSLITERATION_UR},
};
// List of input methods that should be enabled in OOBE.
// The only fuzzy IMs included in this list are those that have required data
// pre-bundled in the system's image. Entries in the list should be updated
// if pre-bundled data for any fuzzy IM is removed from the image.
const char* const kOobeAllowlistedExtensionLocalIds[] = {
// Keyboard layout IMs.
"xkb:us::eng", // English (US)
"xkb:us:altgr-intl:eng", // English (US) with Extended keyboard
"xkb:us:colemak:eng", // English (US) with Colemak keyboard
"xkb:us:dvorak:eng", // English (US) with Dvorak keyboard
"xkb:us:dvp:eng", // English (US) with Programmer Dvorak keyboard
"xkb:us:intl_pc:eng", // English (US) with International PC keyboard
"xkb:us:intl:eng", // English (US) with International keyboard
"xkb:us:workman-intl:eng", // English (US) with Workman International
// keyboard
"xkb:us:workman:eng", // English (US) with Workman keyboard
"xkb:dk::dan", // Danish
"xkb:be::nld", // Dutch (Belgium)
"xkb:us:intl_pc:nld", // Dutch (Netherlands) with US International PC
// keyboard
"xkb:us:intl:nld", // Dutch (Netherlands)
"xkb:ca:eng:eng", // English (Canada)
"xkb:gb:dvorak:eng", // English (UK) with Dvorak keyboard
"xkb:gb:extd:eng", // English (UK)
"xkb:fi::fin", // Finnish
"xkb:be::fra", // French (Belgium)
"xkb:ca::fra", // French (Canada)
"xkb:ca:multix:fra", // French (Canada) with Multilingual keyboard
"xkb:fr::fra", // French (France)
"xkb:fr:bepo:fra", // French (France) with Bépo keyboard
"xkb:ch:fr:fra", // French (Switzerland)
"xkb:be::ger", // German (Belgium)
"xkb:de::ger", // German (Germany)
"xkb:de:neo:ger", // German (Germany) with Neo 2 keyboard
"xkb:ch::ger", // German (Switzerland)
"xkb:it::ita", // Italian
"xkb:no::nob", // Norwegian
"xkb:pl::pol", // Polish
"xkb:br::por", // Portuguese (Brazil)
"xkb:pt::por", // Portuguese (Portugal)
"xkb:us:intl_pc:por", // Portuguese with US International PC keyboard
"xkb:us:intl:por", // Portuguese with US International keyboard
"xkb:latam::spa", // Spanish (Latin America)
"xkb:es::spa", // Spanish (Spain)
"xkb:se::swe", // Swedish
"xkb:tr::tur", // Turkish
"xkb:tr:f:tur", // Turkish with F-keyboard
"xkb:am:phonetic:arm", // Armenian
"xkb:bg::bul", // Bulgarian
"xkb:bg:phonetic:bul", // Bulgarian with Phonetic keyboard
"xkb:by::bel", // Belarusian
"xkb:es:cat:cat", // Catalan
"xkb:cz:qwerty:cze", // Czech with QWERTY keyboard
"xkb:cz::cze", // Czech
"xkb:in::eng", // English (India)
"xkb:pk::eng", // English (Pakistan)
"xkb:za:gb:eng", // English (South Africa)
"xkb:ee::est", // Estonian
"xkb:us::fil", // Filipino
"xkb:fo::fao", // Faroese
"xkb:ge::geo", // Georgian
"xkb:gr::gre", // Greek
"xkb:hr::scr", // Croatian
"xkb:hu::hun", // Hungarian
"xkb:hu:qwerty:hun", // Hungarian with QWERTY keyboard
"xkb:us::ind", // Indonesian
"xkb:ie::ga", // Irish
"xkb:il::heb", // Hebrew
"xkb:is::ice", // Icelandic
"xkb:jp::jpn", // Alphanumeric with Japanese keyboard
"xkb:kz::kaz", // Kazakh
"xkb:lt::lit", // Lithuanian
"xkb:lv:apostrophe:lav", // Latvian
"xkb:mk::mkd", // Macedonian
"xkb:mn::mon", // Mongolian
"xkb:us::msa", // Malay
"xkb:mt::mlt", // Maltese
"xkb:ro::rum", // Romanian
"xkb:ro:std:rum", // Romanian with Standard keyboard
"xkb:ru::rus", // Russian
"xkb:ru:phonetic:rus", // Russian with Phonetic keyboard
"xkb:rs::srp", // Serbian
"xkb:si::slv", // Slovenian
"xkb:sk::slo", // Slovak
"xkb:ua::ukr", // Ukrainian
// Deterministic IMEs.
"vkd_ar", // Arabic
// Fuzzy IMs.
"nacl_mozc_us", // Japanese
"nacl_mozc_jp", // Japanese with US keyboard
};
// Inserts {key, value} into the multimap if it does not exist.
void MultimapDeduplicatedInsert(LanguageCodeToIdsMap& multimap,
const std::string& key,
@ -434,6 +526,13 @@ InputMethodUtil::InputMethodUtil(InputMethodDelegate* delegate)
english_to_resource_id_ = EnglishToIDMap(std::move(map_storage));
DCHECK(english_to_resource_id_.size() == kEnglishToResourceIdArraySize)
<< "Duplicate string is found";
for (const char* extension_local_id : kOobeAllowlistedExtensionLocalIds) {
std::string fully_qualified_id =
extension_ime_util::GetInputMethodIDByEngineID(
std::string(extension_local_id));
oobe_allowlisted_ids_.insert(fully_qualified_id);
}
}
InputMethodUtil::~InputMethodUtil() = default;
@ -781,6 +880,12 @@ bool InputMethodUtil::IsLoginKeyboard(
return ime ? ime->is_login_keyboard() : false;
}
bool InputMethodUtil::IsOobeAllowlisted(
const std::string& input_method_id) const {
return oobe_allowlisted_ids_.find(input_method_id) !=
oobe_allowlisted_ids_.cend();
}
void InputMethodUtil::AppendInputMethods(const InputMethodDescriptors& imes) {
for (const auto& input_method : imes) {
DCHECK(!input_method.language_codes().empty());

@ -8,6 +8,7 @@
#include <cstddef>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <vector>
@ -132,6 +133,9 @@ class COMPONENT_EXPORT(UI_BASE_IME_ASH) InputMethodUtil {
// Returns true if given input method can be used to input login data.
bool IsLoginKeyboard(const std::string& input_method_id) const;
// Returns true if given input method is allowlisted for OOBE.
bool IsOobeAllowlisted(const std::string& input_method_id) const;
// Returns true if the given input method id is supported.
bool IsValidInputMethodId(const std::string& input_method_id) const;
@ -185,6 +189,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_ASH) InputMethodUtil {
std::vector<std::string> hardware_layouts_;
std::vector<std::string> hardware_login_layouts_;
std::vector<std::string> cached_hardware_layouts_;
std::set<std::string> oobe_allowlisted_ids_;
};
} // namespace input_method

@ -43,7 +43,11 @@ bool MockInputMethodManager::State::EnableInputMethod(
void MockInputMethodManager::State::EnableLoginLayouts(
const std::string& language_code,
const std::vector<std::string>& initial_layout) {}
const std::vector<std::string>& initial_layouts) {}
void MockInputMethodManager::State::EnableOobeInputMethods(
const std::string& language_code,
const std::vector<std::string>& initial_input_methods) {}
void MockInputMethodManager::State::DisableNonLockScreenLayouts() {}
@ -78,7 +82,8 @@ size_t MockInputMethodManager::State::GetNumEnabledInputMethods() const {
void MockInputMethodManager::State::SetEnabledExtensionImes(
base::span<const std::string> ids) {}
void MockInputMethodManager::State::SetInputMethodLoginDefault() {}
void MockInputMethodManager::State::SetInputMethodLoginDefault(
bool is_in_oobe_context) {}
void MockInputMethodManager::State::SetInputMethodLoginDefaultFromVPD(
const std::string& locale,

@ -44,6 +44,9 @@ class COMPONENT_EXPORT(UI_BASE_IME_ASH) MockInputMethodManager
void EnableLoginLayouts(
const std::string& language_code,
const std::vector<std::string>& initial_layouts) override;
void EnableOobeInputMethods(
const std::string& language_code,
const std::vector<std::string>& initial_input_methods) override;
void DisableNonLockScreenLayouts() override;
void GetInputMethodExtensions(InputMethodDescriptors* result) override;
InputMethodDescriptors GetEnabledInputMethodsSortedByLocalizedDisplayNames()
@ -54,7 +57,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_ASH) MockInputMethodManager
const std::string& input_method_id) const override;
size_t GetNumEnabledInputMethods() const override;
void SetEnabledExtensionImes(base::span<const std::string> ids) override;
void SetInputMethodLoginDefault() override;
void SetInputMethodLoginDefault(bool is_in_oobe_context) override;
void SetInputMethodLoginDefaultFromVPD(const std::string& locale,
const std::string& layout) override;
void SwitchToNextInputMethod() override;