| // Copyright 2021 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/ash/input_method/input_method_settings.h" |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "base/feature_list.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/common/pref_names.h" |
| #include "chromeos/ash/services/ime/public/mojom/input_method.mojom-shared.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| namespace input_method { |
| namespace { |
| |
| namespace mojom = ::ash::ime::mojom; |
| |
| constexpr char kUsEnglishEngineId[] = "xkb:us::eng"; |
| constexpr char kKoreanEngineId[] = "ko-t-i0-und"; |
| constexpr char kPinyinEngineId[] = "zh-t-i0-pinyin"; |
| constexpr char kZhuyinEngineId[] = "zh-hant-t-i0-und"; |
| constexpr char kJapaneseEngineId[] = "nacl_mozc_jp"; |
| constexpr char kJapaneseUsEngineId[] = "nacl_mozc_us"; |
| |
| constexpr char kVietnameseVniEngineId[] = "vkd_vi_vni"; |
| constexpr char kVietnameseTelexEngineId[] = "vkd_vi_telex"; |
| |
| void RegisterTestingPrefs(TestingPrefServiceSimple& prefs, |
| const base::Value::Dict& dict) { |
| prefs.registry()->RegisterDictionaryPref( |
| ::prefs::kLanguageInputMethodSpecificSettings); |
| prefs.Set(::prefs::kLanguageInputMethodSpecificSettings, |
| base::Value(dict.Clone())); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateLatinSettingsDefault) { |
| base::Value::Dict dict; |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId); |
| |
| ASSERT_TRUE(settings->is_latin_settings()); |
| const auto& latin_settings = *settings->get_latin_settings(); |
| EXPECT_FALSE(latin_settings.autocorrect); |
| EXPECT_TRUE(latin_settings.predictive_writing); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateLatinSettingsWithMultiwordEnabled) { |
| base::test::ScopedFeatureList features; |
| TestingPrefServiceSimple prefs; |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kUsEnglishEngineId, |
| ".physicalKeyboardAutoCorrectionLevel"}), |
| 1); |
| dict.SetByDottedPath( |
| base::StrCat( |
| {kUsEnglishEngineId, ".physicalKeyboardEnablePredictiveWriting"}), |
| base::Value(true)); |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId); |
| |
| ASSERT_TRUE(settings->is_latin_settings()); |
| const auto& latin_settings = *settings->get_latin_settings(); |
| EXPECT_TRUE(latin_settings.autocorrect); |
| EXPECT_TRUE(latin_settings.predictive_writing); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateLatinSettingsWithMultiwordDisabled) { |
| base::test::ScopedFeatureList features; |
| TestingPrefServiceSimple prefs; |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kUsEnglishEngineId, |
| ".physicalKeyboardAutoCorrectionLevel"}), |
| 1); |
| dict.SetByDottedPath( |
| base::StrCat( |
| {kUsEnglishEngineId, ".physicalKeyboardEnablePredictiveWriting"}), |
| base::Value(false)); |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId); |
| |
| ASSERT_TRUE(settings->is_latin_settings()); |
| const auto& latin_settings = *settings->get_latin_settings(); |
| EXPECT_TRUE(latin_settings.autocorrect); |
| EXPECT_FALSE(latin_settings.predictive_writing); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, |
| PredictiveWritingEnabledWhenMultiWordAllowedAndEnabled) { |
| base::test::ScopedFeatureList features; |
| TestingPrefServiceSimple prefs; |
| base::Value::Dict dict; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId); |
| |
| ASSERT_TRUE(settings->is_latin_settings()); |
| const auto& latin_settings = *settings->get_latin_settings(); |
| EXPECT_TRUE(latin_settings.predictive_writing); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, |
| PredictiveWritingDisabledWhenMultiwordDisabled) { |
| base::test::ScopedFeatureList features; |
| features.InitWithFeatures({}, {features::kAssistMultiWord}); |
| TestingPrefServiceSimple prefs; |
| base::Value::Dict dict; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kUsEnglishEngineId); |
| |
| ASSERT_TRUE(settings->is_latin_settings()); |
| const auto& latin_settings = *settings->get_latin_settings(); |
| EXPECT_FALSE(latin_settings.predictive_writing); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateKoreanSettingsDefault) { |
| base::Value::Dict dict; |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kKoreanEngineId); |
| |
| ASSERT_TRUE(settings->is_korean_settings()); |
| const auto& korean_settings = *settings->get_korean_settings(); |
| EXPECT_EQ(korean_settings.layout, mojom::KoreanLayout::kDubeolsik); |
| EXPECT_FALSE(korean_settings.input_multiple_syllables); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateKoreanSettings) { |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kKoreanEngineId, ".koreanKeyboardLayout"}), |
| "3 Set (390) / 세벌식 (390)"); |
| dict.SetByDottedPath( |
| base::StrCat({kKoreanEngineId, ".koreanEnableSyllableInput"}), false); |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kKoreanEngineId); |
| |
| ASSERT_TRUE(settings->is_korean_settings()); |
| const auto& korean_settings = *settings->get_korean_settings(); |
| EXPECT_EQ(korean_settings.layout, mojom::KoreanLayout::kSebeolsik390); |
| EXPECT_TRUE(korean_settings.input_multiple_syllables); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreatePinyinSettingsDefault) { |
| base::Value::Dict dict; |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kPinyinEngineId); |
| |
| ASSERT_TRUE(settings->is_pinyin_settings()); |
| const auto& pinyin_settings = *settings->get_pinyin_settings(); |
| ASSERT_TRUE(pinyin_settings.fuzzy_pinyin); |
| const auto& fuzzy_pinyin = *pinyin_settings.fuzzy_pinyin; |
| EXPECT_FALSE(fuzzy_pinyin.an_ang); |
| EXPECT_FALSE(fuzzy_pinyin.en_eng); |
| EXPECT_FALSE(fuzzy_pinyin.ian_iang); |
| EXPECT_FALSE(fuzzy_pinyin.k_g); |
| EXPECT_FALSE(fuzzy_pinyin.r_l); |
| EXPECT_FALSE(fuzzy_pinyin.uan_uang); |
| EXPECT_FALSE(fuzzy_pinyin.c_ch); |
| EXPECT_FALSE(fuzzy_pinyin.f_h); |
| EXPECT_FALSE(fuzzy_pinyin.in_ing); |
| EXPECT_FALSE(fuzzy_pinyin.l_n); |
| EXPECT_FALSE(fuzzy_pinyin.s_sh); |
| EXPECT_FALSE(fuzzy_pinyin.z_zh); |
| EXPECT_EQ(pinyin_settings.layout, mojom::PinyinLayout::kUsQwerty); |
| EXPECT_TRUE(pinyin_settings.use_hyphen_and_equals_to_page_candidates); |
| EXPECT_TRUE(pinyin_settings.use_comma_and_period_to_page_candidates); |
| EXPECT_TRUE(pinyin_settings.default_to_chinese); |
| EXPECT_FALSE(pinyin_settings.default_to_full_width_characters); |
| EXPECT_TRUE(pinyin_settings.default_to_full_width_punctuation); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreatePinyinSettings) { |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".en:eng"}), true); |
| dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".k:g"}), true); |
| dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".in:ing"}), true); |
| dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".xkbLayout"}), |
| "Colemak"); |
| dict.SetByDottedPath( |
| base::StrCat({kPinyinEngineId, ".pinyinEnableLowerPaging"}), false); |
| dict.SetByDottedPath( |
| base::StrCat({kPinyinEngineId, ".pinyinEnableUpperPaging"}), false); |
| dict.SetByDottedPath(base::StrCat({kPinyinEngineId, ".pinyinDefaultChinese"}), |
| false); |
| dict.SetByDottedPath( |
| base::StrCat({kPinyinEngineId, ".pinyinFullWidthCharacter"}), true); |
| dict.SetByDottedPath( |
| base::StrCat({kPinyinEngineId, ".pinyinChinesePunctuation"}), false); |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kPinyinEngineId); |
| |
| ASSERT_TRUE(settings->is_pinyin_settings()); |
| const auto& pinyin_settings = *settings->get_pinyin_settings(); |
| ASSERT_TRUE(pinyin_settings.fuzzy_pinyin); |
| const auto& fuzzy_pinyin = *pinyin_settings.fuzzy_pinyin; |
| EXPECT_FALSE(fuzzy_pinyin.an_ang); |
| EXPECT_TRUE(fuzzy_pinyin.en_eng); |
| EXPECT_FALSE(fuzzy_pinyin.ian_iang); |
| EXPECT_TRUE(fuzzy_pinyin.k_g); |
| EXPECT_FALSE(fuzzy_pinyin.r_l); |
| EXPECT_FALSE(fuzzy_pinyin.uan_uang); |
| EXPECT_FALSE(fuzzy_pinyin.c_ch); |
| EXPECT_FALSE(fuzzy_pinyin.f_h); |
| EXPECT_TRUE(fuzzy_pinyin.in_ing); |
| EXPECT_FALSE(fuzzy_pinyin.l_n); |
| EXPECT_FALSE(fuzzy_pinyin.s_sh); |
| EXPECT_FALSE(fuzzy_pinyin.z_zh); |
| EXPECT_EQ(pinyin_settings.layout, mojom::PinyinLayout::kColemak); |
| EXPECT_FALSE(pinyin_settings.use_comma_and_period_to_page_candidates); |
| EXPECT_FALSE(pinyin_settings.use_hyphen_and_equals_to_page_candidates); |
| EXPECT_FALSE(pinyin_settings.default_to_chinese); |
| EXPECT_TRUE(pinyin_settings.default_to_full_width_characters); |
| EXPECT_FALSE(pinyin_settings.default_to_full_width_punctuation); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateZhuyinSettingsDefault) { |
| base::Value::Dict dict; |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kZhuyinEngineId); |
| |
| ASSERT_TRUE(settings->is_zhuyin_settings()); |
| const auto& zhuyin_settings = *settings->get_zhuyin_settings(); |
| EXPECT_EQ(zhuyin_settings.layout, mojom::ZhuyinLayout::kStandard); |
| EXPECT_EQ(zhuyin_settings.selection_keys, |
| mojom::ZhuyinSelectionKeys::k1234567890); |
| EXPECT_EQ(zhuyin_settings.page_size, 10u); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateVietnameseVniSettings) { |
| base::Value::Dict dict; |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kVietnameseVniEngineId); |
| |
| ASSERT_TRUE(settings->is_vietnamese_vni_settings()); |
| const auto& vni_settings = *settings->get_vietnamese_vni_settings(); |
| EXPECT_TRUE(vni_settings.allow_flexible_diacritics); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateVietnameseTelexSettings) { |
| base::Value::Dict dict; |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = |
| CreateSettingsFromPrefs(prefs, kVietnameseTelexEngineId); |
| |
| ASSERT_TRUE(settings->is_vietnamese_telex_settings()); |
| const auto& telex_settings = *settings->get_vietnamese_telex_settings(); |
| EXPECT_TRUE(telex_settings.allow_flexible_diacritics); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, CreateZhuyinSettings) { |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".zhuyinKeyboardLayout"}), |
| "IBM"); |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".zhuyinSelectKeys"}), |
| "asdfghjkl;"); |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".zhuyinPageSize"}), "8"); |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| const auto settings = CreateSettingsFromPrefs(prefs, kZhuyinEngineId); |
| |
| ASSERT_TRUE(settings->is_zhuyin_settings()); |
| const auto& zhuyin_settings = *settings->get_zhuyin_settings(); |
| EXPECT_EQ(zhuyin_settings.layout, mojom::ZhuyinLayout::kIbm); |
| EXPECT_EQ(zhuyin_settings.selection_keys, |
| mojom::ZhuyinSelectionKeys::kAsdfghjkl); |
| EXPECT_EQ(zhuyin_settings.page_size, 8u); |
| } |
| |
| class JapaneseTesting : public testing::TestWithParam<std::string> {}; |
| |
| TEST_P(JapaneseTesting, CreateJapaneseSettingsFromPrefsTest) { |
| using ::ash::ime::mojom::JapaneseSettings; |
| base::Value::Dict jp_prefs; |
| jp_prefs.Set("AutomaticallySendStatisticsToGoogle", false); |
| jp_prefs.Set("AutomaticallySwitchToHalfwidth", false); |
| jp_prefs.Set("JapaneseDisableSuggestions", true); |
| jp_prefs.Set("JapaneseInputMode", "Kana"); |
| jp_prefs.Set("JapaneseKeymapStyle", "ChromeOs"); |
| jp_prefs.Set("JapanesePunctuationStyle", "CommaPeriod"); |
| jp_prefs.Set("JapaneseSectionShortcut", "ASDFGHJKL"); |
| jp_prefs.Set("JapaneseSpaceInputStyle", "Fullwidth"); |
| jp_prefs.Set("JapaneseSymbolStyle", "SquareBracketMiddleDot"); |
| jp_prefs.Set("ShiftKeyModeStyle", "Off"); |
| jp_prefs.Set("UseInputHistory", false); |
| jp_prefs.Set("UseSystemDictionary", false); |
| jp_prefs.Set("numberOfSuggestions", 5); |
| |
| base::Value::Dict full_prefs; |
| |
| // TODO(crbug.com/203464079): Use distinct CrOS prefs for nacl_mozc_jp |
| // ("Japanese [for JIS keyboard]") and nacl_mozc_us ("Japanese for US |
| // keyboard") input methods. Due to singleton constraints in the legacy |
| // implementation, unlike all other input methods whose settings were distinct |
| // from one another, these two input methods shared the same settings. Upon |
| // migration to CrOS prefs, the unintended sharing was intentionally retained |
| // until the issue is separately addressed outside the scope of the said |
| // migration. Thus a single Japanese prefs entry with key "nacl_mozc_jp" is |
| // currently used for both "nacl_mozc_jp" and "nacl_mozc_us" input methods. |
| full_prefs.Set(kJapaneseEngineId, std::move(jp_prefs)); |
| |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, full_prefs); |
| |
| const mojom::InputMethodSettingsPtr settings = |
| CreateSettingsFromPrefs(prefs, GetParam()); |
| |
| ASSERT_TRUE(settings->is_japanese_settings()); |
| mojom::JapaneseSettingsPtr expected = mojom::JapaneseSettings::New(); |
| expected->unused2 = false; |
| expected->disable_personalized_suggestions = true; |
| expected->input_mode = JapaneseSettings::InputMode::kKana; |
| expected->keymap_style = JapaneseSettings::KeymapStyle::kChromeos; |
| expected->punctuation_style = |
| JapaneseSettings::PunctuationStyle::kCommaPeriod; |
| expected->selection_shortcut = |
| JapaneseSettings::SelectionShortcut::kAsdfghjkl; |
| expected->space_input_style = JapaneseSettings::SpaceInputStyle::kFullWidth; |
| expected->symbol_style = |
| JapaneseSettings::SymbolStyle::kSquareBracketMiddleDot; |
| expected->shift_key_mode_style = JapaneseSettings::ShiftKeyModeStyle::kOff; |
| expected->use_input_history = false; |
| expected->use_system_dictionary = false; |
| expected->number_of_suggestions = 5; |
| EXPECT_EQ(settings->get_japanese_settings(), expected); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(CreateJapaneseSettingsFromPrefsTestSuite, |
| JapaneseTesting, |
| testing::Values(kJapaneseEngineId, |
| kJapaneseUsEngineId)); |
| |
| TEST(CreateSettingsFromPrefsTest, AutocorrectIsSupportedForLatin) { |
| ASSERT_TRUE(IsAutocorrectSupported("xkb:ca:multix:fra")); |
| ASSERT_TRUE(IsAutocorrectSupported("xkb:de::ger")); |
| ASSERT_TRUE(IsAutocorrectSupported("xkb:us::eng")); |
| ASSERT_TRUE(IsAutocorrectSupported("xkb:us:intl_pc:por")); |
| ASSERT_TRUE(IsAutocorrectSupported("xkb:us:workman-intl:eng")); |
| } |
| |
| TEST(CreateSettingsFromPrefsTest, AutocorrectIsNotSupportedForNonLatin) { |
| ASSERT_FALSE(IsAutocorrectSupported("ko-t-i0-und")); |
| ASSERT_FALSE(IsAutocorrectSupported("nacl_mozc_us")); |
| ASSERT_FALSE(IsAutocorrectSupported("xkb:am:phonetic:arm")); |
| ASSERT_FALSE(IsAutocorrectSupported("xkb:in::eng")); |
| ASSERT_FALSE(IsAutocorrectSupported("zh-hant-t-i0-pinyin")); |
| ASSERT_FALSE(IsAutocorrectSupported("zh-hant-t-i0-und")); |
| ASSERT_FALSE(IsAutocorrectSupported("zh-t-i0-pinyin")); |
| } |
| |
| TEST(InputMethodSettingsTest, GetLanguageSpecificInputMethodSettings) { |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field1"}), "DEFAULT1"); |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field2"}), "DEFAULT2"); |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field3"}), "DEFAULT3"); |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| base::Value::Dict new_prefs; |
| new_prefs.Set("field2", "CHANGED"); |
| EXPECT_EQ( |
| *GetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, "field1"), |
| "DEFAULT1"); |
| EXPECT_EQ( |
| *GetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, "field2"), |
| "DEFAULT2"); |
| EXPECT_EQ( |
| *GetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, "field3"), |
| "DEFAULT3"); |
| } |
| |
| TEST(InputMethodSettingsTest, |
| SetLanguageInputMethodSpecificSettingExistingEngine) { |
| base::Value::Dict dict; |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field1"}), "DEFAULT"); |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field2"}), "DEFAULT"); |
| dict.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field3"}), "DEFAULT"); |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| base::Value::Dict new_prefs; |
| new_prefs.Set("field2", "CHANGED"); |
| SetLanguageInputMethodSpecificSetting(prefs, kZhuyinEngineId, new_prefs); |
| |
| const base::Value* prefs_val = |
| prefs.GetUserPref(::prefs::kLanguageInputMethodSpecificSettings); |
| |
| base::Value::Dict expected; |
| expected.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field1"}), |
| "DEFAULT"); |
| expected.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field2"}), |
| "CHANGED"); |
| expected.SetByDottedPath(base::StrCat({kZhuyinEngineId, ".field3"}), |
| "DEFAULT"); |
| |
| EXPECT_EQ(*prefs_val->GetIfDict(), expected); |
| } |
| |
| TEST(InputMethodSettingsTest, SetLanguageInputMethodSpecificSettingNewEngine) { |
| base::Value::Dict dict; |
| dict.SetByDottedPath("existing-engine.field1", "DEFAULT"); |
| TestingPrefServiceSimple prefs; |
| RegisterTestingPrefs(prefs, dict); |
| |
| base::Value::Dict new_prefs; |
| new_prefs.Set("field1", "NEW"); |
| SetLanguageInputMethodSpecificSetting(prefs, "brand-new-engine", new_prefs); |
| |
| const base::Value* prefs_val = |
| prefs.GetUserPref(::prefs::kLanguageInputMethodSpecificSettings); |
| |
| base::Value::Dict expected; |
| expected.SetByDottedPath("existing-engine.field1", "DEFAULT"); |
| expected.SetByDottedPath("brand-new-engine.field1", "NEW"); |
| |
| EXPECT_EQ(*prefs_val->GetIfDict(), expected); |
| } |
| |
| } // namespace |
| } // namespace input_method |
| } // namespace ash |