Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / input_method_util.cc
blob21b9d64db79cd1b69d3ba32f0a988eaea02e43b9
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/input_method/input_method_util.h"
7 #include <algorithm>
8 #include <functional>
9 #include <map>
10 #include <utility>
12 #include "base/basictypes.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chromeos/ime/component_extension_ime_manager.h"
19 #include "chromeos/ime/extension_ime_util.h"
20 // For SetHardwareKeyboardLayoutForTesting.
21 #include "chromeos/ime/fake_input_method_delegate.h"
22 #include "chromeos/ime/input_method_delegate.h"
23 #include "chromeos/ime/input_method_whitelist.h"
24 // TODO(nona): move this header from this file.
25 #include "grit/generated_resources.h"
27 namespace {
29 // A mapping from an input method id to a string for the language indicator. The
30 // mapping is necessary since some input methods belong to the same language.
31 // For example, both "xkb:us::eng" and "xkb:us:dvorak:eng" are for US English.
32 const struct {
33 const char* input_method_id;
34 const char* indicator_text;
35 } kMappingFromIdToIndicatorText[] = {
36 // To distinguish from "xkb:jp::jpn"
37 // TODO(nona): Make following variables configurable. http://crbug.com/232260.
38 { "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us", "\xe3\x81\x82" },
39 { "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp", "\xe3\x81\x82" },
40 { "_comp_ime_bbaiamgfapehflhememkfglaehiobjnknacl_mozc_us", "\xe3\x81\x82" },
41 { "_comp_ime_bbaiamgfapehflhememkfglaehiobjnknacl_mozc_jp", "\xe3\x81\x82" },
42 // For simplified Chinese input methods
43 { "pinyin", "\xe6\x8b\xbc" }, // U+62FC
44 { "_comp_ime_cpgalbafkoofkjmaeonnfijgpfennjjnzh-t-i0-pinyin",
45 "\xe6\x8b\xbc" },
46 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin",
47 "\xe6\x8b\xbc" },
48 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-wubi-1986",
49 "\xe4\xba\x94" }, // U+4E94
50 { "pinyin-dv", "\xe6\x8b\xbc" },
51 // For traditional Chinese input methods
52 { "mozc-chewing", "\xe9\x85\xb7" }, // U+9177
53 { "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und",
54 "\xE6\xB3\xA8" }, // U+6CE8
55 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-und",
56 "\xE6\xB3\xA8" }, // U+6CE8
57 { "m17n:zh:cangjie", "\xe5\x80\x89" }, // U+5009
58 { "_comp_ime_aeebooiibjahgpgmhkeocbeekccfknbjzh-hant-t-i0-cangjie-1987",
59 "\xe5\x80\x89" }, // U+5009
60 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-cangjie-1987",
61 "\xe5\x80\x89" }, // U+5009
62 { "m17n:zh:quick", "\xe9\x80\x9f" }, // U+901F
63 // For Hangul input method.
64 { "mozc-hangul", "\xed\x95\x9c" }, // U+D55C
65 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_2set", "\xed\x95\x9c" },
66 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3set390",
67 "\xed\x95\x9c" },
68 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setfinal",
69 "\xed\x95\x9c" },
70 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setnoshift",
71 "\xed\x95\x9c" },
72 { "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_romaja", "\xed\x95\x9c" },
75 const size_t kMappingFromIdToIndicatorTextLen =
76 ARRAYSIZE_UNSAFE(kMappingFromIdToIndicatorText);
78 // A mapping from an input method id to a resource id for a
79 // medium length language indicator.
80 // For those languages that want to display a slightly longer text in the
81 // "Your input method has changed to..." bubble than in the status tray.
82 // If an entry is not found in this table the short name is used.
83 const struct {
84 const char* input_method_id;
85 const int resource_id;
86 } kMappingImeIdToMediumLenNameResourceId[] = {
87 { "m17n:zh:cangjie", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
88 { "m17n:zh:quick", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
89 { "mozc-chewing", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
90 { "mozc-hangul", IDS_LANGUAGES_MEDIUM_LEN_NAME_KOREAN },
91 { "pinyin", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
92 { "pinyin-dv", IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
93 { "_comp_ime_cpgalbafkoofkjmaeonnfijgpfennjjnzh-t-i0-pinyin",
94 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED},
95 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin",
96 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
97 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-wubi-1986",
98 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_SIMPLIFIED },
99 { "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und",
100 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
101 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-und",
102 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
103 { "_comp_ime_aeebooiibjahgpgmhkeocbeekccfknbjzh-hant-t-i0-cangjie-1987",
104 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
105 { "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-cangjie-1987",
106 IDS_LANGUAGES_MEDIUM_LEN_NAME_CHINESE_TRADITIONAL },
108 const size_t kMappingImeIdToMediumLenNameResourceIdLen =
109 ARRAYSIZE_UNSAFE(kMappingImeIdToMediumLenNameResourceId);
111 // Due to asynchronous initialization of component extension manager,
112 // GetFirstLogingInputMethodIds may miss component extension IMEs. To enable
113 // component extension IME as the first loging input method, we have to prepare
114 // component extension IME IDs.
115 const struct {
116 const char* locale;
117 const char* layout;
118 const char* input_method_id;
119 } kDefaultInputMethodRecommendation[] = {
120 { "ja", "us", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us" },
121 { "ja", "jp", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp" },
122 { "zh-CN", "us", "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin" },
123 { "zh-TW", "us",
124 "_comp_ime_gjaehgfemfahhmlgpdfknkhdnemmolopzh-hant-t-i0-und" },
125 #if defined(OFFICIAL_BUILD)
126 { "th", "us", "_comp_ime_habcdindjejkmepknlhkkloncjcpcnbfvkd_th" },
127 { "vi", "us", "_comp_ime_habcdindjejkmepknlhkkloncjcpcnbfvkd_vi_tcvn" },
128 { "vi", "us", "_comp_ime_habcdindjejkmepknlhkkloncjcpcnbfvkd_vi_tcvn" },
129 #else
130 { "th", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th" },
131 { "vi", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
132 { "vi", "us", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
133 #endif
136 // The map from xkb layout to the indicator text.
137 // Refer to crbug.com/349829.
138 const char* const kXkbIndicators[][2] = {{"am", "AM"},
139 {"be", "BE"},
140 {"bg", "BG"},
141 {"bg(phonetic)", "BG"},
142 {"br", "BR"},
143 {"by", "BY"},
144 {"ca", "CA"},
145 {"ca(eng)", "CA"},
146 {"ca(multix)", "CA"},
147 {"ch", "CH"},
148 {"ch(fr)", "CH"},
149 {"cz", "CZ"},
150 {"cz(qwerty)", "CS"},
151 {"de", "DE"},
152 {"de(neo)", "NEO"},
153 {"dk", "DK"},
154 {"ee", "EE"},
155 {"es", "ES"},
156 {"es(cat)", "CAS"},
157 {"fi", "FI"},
158 {"fr", "FR"},
159 {"gb(dvorak)", "DV"},
160 {"gb(extd)", "GB"},
161 {"ge", "GE"},
162 {"gr", "GR"},
163 {"hr", "HR"},
164 {"hu", "HU"},
165 {"il", "IL"},
166 {"is", "IS"},
167 {"it", "IT"},
168 {"jp", "JA"},
169 {"latam", "LA"},
170 {"lt", "LT"},
171 {"lv(apostrophe)", "LV"},
172 {"mn", "MN"},
173 {"no", "NO"},
174 {"pl", "PL"},
175 {"pt", "PT"},
176 {"ro", "RO"},
177 {"rs", "RS"},
178 {"ru", "RU"},
179 {"ru(phonetic)", "RU"},
180 {"se", "SE"},
181 {"si", "SI"},
182 {"sk", "SK"},
183 {"tr", "TR"},
184 {"ua", "UA"},
185 {"us", "US"},
186 {"us(altgr-intl)", "EXTD"},
187 {"us(colemak)", "CO"},
188 {"us(dvorak)", "DV"},
189 {"us(intl)", "INTL"}, };
191 // The old chinese input method ids for migration.
192 // See crbug.com/357384.
193 const char* kOldChineseExtensionIds[] = {
194 "goedamlknlnjaengojinmfgpmdjmkooo",
195 "nmblnjkfdkabgdofidlkienfnnbjhnab",
196 "gjhclobljhjhgoebiipblnmdodbmpdgd"
199 // The new chinese input method id for migration.
200 // See crbug.com/357384.
201 const char* kNewChineseExtensionId = "gjaehgfemfahhmlgpdfknkhdnemmolop";
203 const size_t kExtensionIdLen = 32;
205 } // namespace
207 namespace chromeos {
209 extern const char* kExtensionImePrefix;
211 namespace input_method {
213 namespace {
215 const struct EnglishToResouceId {
216 const char* english_string_from_ibus;
217 int resource_id;
218 } kEnglishToResourceIdArray[] = {
219 // For ibus-mozc-hangul
220 { "Hanja mode", IDS_STATUSBAR_IME_KOREAN_HANJA_INPUT_MODE },
221 { "Hangul mode", IDS_STATUSBAR_IME_KOREAN_HANGUL_INPUT_MODE },
223 // For ibus-mozc-pinyin.
224 { "Full/Half width",
225 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF },
226 { "Full/Half width punctuation",
227 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_FULL_HALF_PUNCTUATION },
228 // TODO(hsumita): Fixes a typo
229 { "Simplfied/Traditional Chinese",
230 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_S_T_CHINESE },
231 { "Chinese",
232 IDS_STATUSBAR_IME_CHINESE_PINYIN_TOGGLE_CHINESE_ENGLISH },
234 // For ibus-mozc-chewing.
235 { "English",
236 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_ENGLISH_MODE },
237 { "_Chinese",
238 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_CHINESE_MODE },
239 { "Full-width English",
240 IDS_STATUSBAR_IME_CHINESE_MOZC_CHEWING_FULL_WIDTH_ENGLISH_MODE },
242 // For the "Languages and Input" dialog.
243 { "m17n:ar:kbd", IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
244 { "m17n:hi:itrans", // also uses the "STANDARD_INPUT_METHOD" id.
245 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
246 { "m17n:zh:cangjie",
247 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_CANGJIE_INPUT_METHOD },
248 { "m17n:zh:quick",
249 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_CHINESE_QUICK_INPUT_METHOD },
250 { "m17n:fa:isiri",
251 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_PERSIAN_ISIRI_2901_INPUT_METHOD },
252 { "m17n:th:kesmanee",
253 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_KESMANEE_INPUT_METHOD },
254 { "m17n:th:tis820",
255 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_TIS820_INPUT_METHOD },
256 { "m17n:th:pattachote",
257 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_THAI_PATTACHOTE_INPUT_METHOD },
258 { "m17n:vi:tcvn",
259 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TCVN_INPUT_METHOD },
260 { "m17n:vi:telex",
261 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_TELEX_INPUT_METHOD },
262 { "m17n:vi:viqr",
263 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VIQR_INPUT_METHOD },
264 { "m17n:vi:vni",
265 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_VIETNAMESE_VNI_INPUT_METHOD },
266 { "m17n:bn:itrans",
267 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
268 { "m17n:gu:itrans",
269 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
270 { "m17n:ml:itrans",
271 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
272 { "m17n:mr:itrans",
273 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
274 { "m17n:ta:phonetic",
275 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_PHONETIC },
276 { "m17n:ta:inscript",
277 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_INSCRIPT },
278 { "m17n:ta:tamil99",
279 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TAMIL99 },
280 { "m17n:ta:itrans",
281 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_ITRANS },
282 { "m17n:ta:typewriter",
283 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_TAMIL_TYPEWRITER },
284 { "m17n:am:sera",
285 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
286 { "m17n:te:itrans",
287 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
288 { "m17n:kn:itrans",
289 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD },
291 { "mozc-chewing",
292 IDS_OPTIONS_SETTINGS_LANGUAGES_CHEWING_INPUT_METHOD },
293 { "pinyin", IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_INPUT_METHOD },
294 { "pinyin-dv",
295 IDS_OPTIONS_SETTINGS_LANGUAGES_PINYIN_DV_INPUT_METHOD },
296 { "zinnia-japanese",
297 IDS_OPTIONS_SETTINGS_LANGUAGES_JAPANESE_HANDWRITING_INPUT_METHOD },
298 { "mozc-hangul", IDS_OPTIONS_SETTINGS_LANGUAGES_KOREAN_INPUT_METHOD },
300 // For ibus-xkb-layouts engine: third_party/ibus-xkb-layouts/files
301 { "xkb:jp::jpn", IDS_STATUSBAR_LAYOUT_JAPAN },
302 { "xkb:si::slv", IDS_STATUSBAR_LAYOUT_SLOVENIA },
303 { "xkb:de::ger", IDS_STATUSBAR_LAYOUT_GERMANY },
304 { "xkb:de:neo:ger", IDS_STATUSBAR_LAYOUT_GERMANY_NEO2 },
305 { "xkb:it::ita", IDS_STATUSBAR_LAYOUT_ITALY },
306 { "xkb:ee::est", IDS_STATUSBAR_LAYOUT_ESTONIA },
307 { "xkb:hu::hun", IDS_STATUSBAR_LAYOUT_HUNGARY },
308 { "xkb:pl::pol", IDS_STATUSBAR_LAYOUT_POLAND },
309 { "xkb:dk::dan", IDS_STATUSBAR_LAYOUT_DENMARK },
310 { "xkb:hr::scr", IDS_STATUSBAR_LAYOUT_CROATIA },
311 { "xkb:br::por", IDS_STATUSBAR_LAYOUT_BRAZIL },
312 { "xkb:rs::srp", IDS_STATUSBAR_LAYOUT_SERBIA },
313 { "xkb:cz::cze", IDS_STATUSBAR_LAYOUT_CZECHIA },
314 { "xkb:cz:qwerty:cze", IDS_STATUSBAR_LAYOUT_CZECHIA_QWERTY },
315 { "xkb:us:dvorak:eng", IDS_STATUSBAR_LAYOUT_USA_DVORAK },
316 { "xkb:us:colemak:eng", IDS_STATUSBAR_LAYOUT_USA_COLEMAK },
317 { "xkb:ro::rum", IDS_STATUSBAR_LAYOUT_ROMANIA },
318 { "xkb:us::eng", IDS_STATUSBAR_LAYOUT_USA },
319 { "xkb:us:altgr-intl:eng", IDS_STATUSBAR_LAYOUT_USA_EXTENDED },
320 { "xkb:us:intl:eng", IDS_STATUSBAR_LAYOUT_USA_INTERNATIONAL },
321 { "xkb:lt::lit", IDS_STATUSBAR_LAYOUT_LITHUANIA },
322 { "xkb:gb:extd:eng", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM },
323 { "xkb:gb:dvorak:eng", IDS_STATUSBAR_LAYOUT_UNITED_KINGDOM_DVORAK },
324 { "xkb:sk::slo", IDS_STATUSBAR_LAYOUT_SLOVAKIA },
325 { "xkb:ru::rus", IDS_STATUSBAR_LAYOUT_RUSSIA },
326 { "xkb:ru:phonetic:rus", IDS_STATUSBAR_LAYOUT_RUSSIA_PHONETIC },
327 { "xkb:gr::gre", IDS_STATUSBAR_LAYOUT_GREECE },
328 { "xkb:be::fra", IDS_STATUSBAR_LAYOUT_BELGIUM },
329 { "xkb:be::ger", IDS_STATUSBAR_LAYOUT_BELGIUM },
330 { "xkb:be::nld", IDS_STATUSBAR_LAYOUT_BELGIUM },
331 { "xkb:bg::bul", IDS_STATUSBAR_LAYOUT_BULGARIA },
332 { "xkb:bg:phonetic:bul", IDS_STATUSBAR_LAYOUT_BULGARIA_PHONETIC },
333 { "xkb:ch::ger", IDS_STATUSBAR_LAYOUT_SWITZERLAND },
334 { "xkb:ch:fr:fra", IDS_STATUSBAR_LAYOUT_SWITZERLAND_FRENCH },
335 { "xkb:tr::tur", IDS_STATUSBAR_LAYOUT_TURKEY },
336 { "xkb:pt::por", IDS_STATUSBAR_LAYOUT_PORTUGAL },
337 { "xkb:es::spa", IDS_STATUSBAR_LAYOUT_SPAIN },
338 { "xkb:fi::fin", IDS_STATUSBAR_LAYOUT_FINLAND },
339 { "xkb:ua::ukr", IDS_STATUSBAR_LAYOUT_UKRAINE },
340 { "xkb:es:cat:cat", IDS_STATUSBAR_LAYOUT_SPAIN_CATALAN },
341 { "xkb:fr::fra", IDS_STATUSBAR_LAYOUT_FRANCE },
342 { "xkb:no::nob", IDS_STATUSBAR_LAYOUT_NORWAY },
343 { "xkb:se::swe", IDS_STATUSBAR_LAYOUT_SWEDEN },
344 { "xkb:nl::nld", IDS_STATUSBAR_LAYOUT_NETHERLANDS },
345 { "xkb:latam::spa", IDS_STATUSBAR_LAYOUT_LATIN_AMERICAN },
346 { "xkb:lv:apostrophe:lav", IDS_STATUSBAR_LAYOUT_LATVIA },
347 { "xkb:ca::fra", IDS_STATUSBAR_LAYOUT_CANADA },
348 { "xkb:ca:eng:eng", IDS_STATUSBAR_LAYOUT_CANADA_ENGLISH },
349 { "xkb:il::heb", IDS_STATUSBAR_LAYOUT_ISRAEL },
350 { "xkb:kr:kr104:kor", IDS_STATUSBAR_LAYOUT_KOREA_104 },
351 { "xkb:is::ice", IDS_STATUSBAR_LAYOUT_ICELANDIC },
352 { "xkb:ca:multix:fra", IDS_STATUSBAR_LAYOUT_CANADIAN_MULTILINGUAL },
353 { "xkb:by::bel", IDS_STATUSBAR_LAYOUT_BELARUSIAN },
354 { "xkb:am:phonetic:arm", IDS_STATUSBAR_LAYOUT_ARMENIAN_PHONETIC },
355 { "xkb:ge::geo", IDS_STATUSBAR_LAYOUT_GEORGIAN },
356 { "xkb:mn::mon", IDS_STATUSBAR_LAYOUT_MONGOLIAN },
358 { "english-m", IDS_STATUSBAR_LAYOUT_USA_MYSTERY },
360 const size_t kEnglishToResourceIdArraySize =
361 arraysize(kEnglishToResourceIdArray);
363 } // namespace
365 InputMethodUtil::InputMethodUtil(
366 InputMethodDelegate* delegate,
367 scoped_ptr<InputMethodDescriptors> supported_input_methods)
368 : supported_input_methods_(supported_input_methods.Pass()),
369 delegate_(delegate) {
370 // Makes sure the supported input methods at least have the fallback ime.
371 // So that it won't cause massive test failures.
372 if (supported_input_methods_->empty())
373 supported_input_methods_->push_back(GetFallbackInputMethodDescriptor());
375 ReloadInternalMaps();
377 // Initialize a map from English string to Chrome string resource ID as well.
378 for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) {
379 const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i];
380 const bool result = english_to_resource_id_.insert(std::make_pair(
381 map_entry.english_string_from_ibus, map_entry.resource_id)).second;
382 DCHECK(result) << "Duplicated string is found: "
383 << map_entry.english_string_from_ibus;
386 // Initialize the map from xkb layout to indicator text.
387 for (size_t i = 0; i < arraysize(kXkbIndicators); ++i) {
388 xkb_layout_to_indicator_[kXkbIndicators[i][0]] = kXkbIndicators[i][1];
392 InputMethodUtil::~InputMethodUtil() {
395 bool InputMethodUtil::TranslateStringInternal(
396 const std::string& english_string, base::string16 *out_string) const {
397 DCHECK(out_string);
398 HashType::const_iterator iter = english_to_resource_id_.find(english_string);
399 if (iter == english_to_resource_id_.end()) {
400 // TODO(yusukes): Write Autotest which checks if all display names and all
401 // property names for supported input methods are listed in the resource
402 // ID array (crosbug.com/4572).
403 LOG(ERROR) << "Resource ID is not found for: " << english_string;
404 return false;
407 *out_string = delegate_->GetLocalizedString(iter->second);
408 return true;
411 base::string16 InputMethodUtil::TranslateString(
412 const std::string& english_string) const {
413 base::string16 localized_string;
414 if (TranslateStringInternal(english_string, &localized_string)) {
415 return localized_string;
417 return base::UTF8ToUTF16(english_string);
420 bool InputMethodUtil::IsValidInputMethodId(
421 const std::string& input_method_id) const {
422 // We can't check the component extension is whilelisted or not here because
423 // it might not be initialized.
424 return GetInputMethodDescriptorFromId(input_method_id) != NULL ||
425 extension_ime_util::IsComponentExtensionIME(input_method_id);
428 // static
429 bool InputMethodUtil::IsKeyboardLayout(const std::string& input_method_id) {
430 return StartsWithASCII(input_method_id, "xkb:", false) ||
431 extension_ime_util::IsKeyboardLayoutExtension(input_method_id);
434 std::string InputMethodUtil::GetLanguageCodeFromInputMethodId(
435 const std::string& input_method_id) const {
436 // The code should be compatible with one of codes used for UI languages,
437 // defined in app/l10_util.cc.
438 const char kDefaultLanguageCode[] = "en-US";
439 std::map<std::string, std::string>::const_iterator iter
440 = id_to_language_code_.find(input_method_id);
441 return (iter == id_to_language_code_.end()) ?
442 // Returning |kDefaultLanguageCode| here is not for Chrome OS but for
443 // Ubuntu where the ibus-xkb-layouts engine could be missing.
444 kDefaultLanguageCode : iter->second;
447 std::string InputMethodUtil::GetKeyboardLayoutName(
448 const std::string& input_method_id) const {
449 InputMethodIdToDescriptorMap::const_iterator iter
450 = id_to_descriptor_.find(input_method_id);
451 return (iter == id_to_descriptor_.end()) ?
452 "" : iter->second.GetPreferredKeyboardLayout();
455 std::string InputMethodUtil::GetInputMethodDisplayNameFromId(
456 const std::string& input_method_id) const {
457 base::string16 display_name;
458 if (!extension_ime_util::IsExtensionIME(input_method_id) &&
459 TranslateStringInternal(input_method_id, &display_name)) {
460 return base::UTF16ToUTF8(display_name);
462 // Return an empty string if the display name is not found.
463 return "";
466 base::string16 InputMethodUtil::GetInputMethodShortName(
467 const InputMethodDescriptor& input_method) const {
468 // For the status area, we use two-letter, upper-case language code like
469 // "US" and "JP".
471 // Use the indicator string if set.
472 if (!input_method.indicator().empty()) {
473 return base::UTF8ToUTF16(input_method.indicator());
476 base::string16 text;
477 // Check special cases first.
478 for (size_t i = 0; i < kMappingFromIdToIndicatorTextLen; ++i) {
479 if (kMappingFromIdToIndicatorText[i].input_method_id ==
480 input_method.id()) {
481 text = base::UTF8ToUTF16(kMappingFromIdToIndicatorText[i].indicator_text);
482 break;
486 // Display the keyboard layout name when using a keyboard layout.
487 if (text.empty() && IsKeyboardLayout(input_method.id())) {
488 std::map<std::string, std::string>::const_iterator it =
489 xkb_layout_to_indicator_.find(GetKeyboardLayoutName(input_method.id()));
490 if (it != xkb_layout_to_indicator_.end())
491 text = base::UTF8ToUTF16(it->second);
494 // TODO(yusukes): Some languages have two or more input methods. For example,
495 // Thai has 3, Vietnamese has 4. If these input methods could be activated at
496 // the same time, we should do either of the following:
497 // (1) Add mappings to |kMappingFromIdToIndicatorText|
498 // (2) Add suffix (1, 2, ...) to |text| when ambiguous.
500 if (text.empty()) {
501 const size_t kMaxLanguageNameLen = 2;
502 DCHECK(!input_method.language_codes().empty());
503 const std::string language_code = input_method.language_codes().at(0);
504 text = StringToUpperASCII(base::UTF8ToUTF16(language_code)).substr(
505 0, kMaxLanguageNameLen);
507 DCHECK(!text.empty()) << input_method.id();
508 return text;
511 base::string16 InputMethodUtil::GetInputMethodMediumName(
512 const InputMethodDescriptor& input_method) const {
513 // For the "Your input method has changed to..." bubble. In most cases
514 // it uses the same name as the short name, unless found in a table
515 // for medium length names.
516 for (size_t i = 0; i < kMappingImeIdToMediumLenNameResourceIdLen; ++i) {
517 if (kMappingImeIdToMediumLenNameResourceId[i].input_method_id ==
518 input_method.id()) {
519 return delegate_->GetLocalizedString(
520 kMappingImeIdToMediumLenNameResourceId[i].resource_id);
523 return GetInputMethodShortName(input_method);
526 base::string16 InputMethodUtil::GetInputMethodLongName(
527 const InputMethodDescriptor& input_method) const {
528 if (!input_method.name().empty() && !IsKeyboardLayout(input_method.id())) {
529 // If the descriptor has a name, use it.
530 return base::UTF8ToUTF16(input_method.name());
533 // We don't show language here. Name of keyboard layout or input method
534 // usually imply (or explicitly include) its language.
536 // Special case for German, French and Dutch: these languages have multiple
537 // keyboard layouts and share the same layout of keyboard (Belgian). We need
538 // to show explicitly the language for the layout. For Arabic, Amharic, and
539 // Indic languages: they share "Standard Input Method".
540 const base::string16 standard_input_method_text =
541 delegate_->GetLocalizedString(
542 IDS_OPTIONS_SETTINGS_LANGUAGES_M17N_STANDARD_INPUT_METHOD);
543 DCHECK(!input_method.language_codes().empty());
544 const std::string language_code = input_method.language_codes().at(0);
546 // Before translate the string, convert the input method id to legacy xkb id
547 // if possible.
548 // TODO(shuchen): the GetInputMethodLongName() method should be removed when
549 // finish the wrapping of xkb to extension.
550 base::string16 text = TranslateString(
551 extension_ime_util::MaybeGetLegacyXkbId(input_method.id()));
552 if (text == standard_input_method_text ||
553 language_code == "de" ||
554 language_code == "fr" ||
555 language_code == "nl") {
556 const base::string16 language_name = delegate_->GetDisplayLanguageName(
557 language_code);
559 text = language_name + base::UTF8ToUTF16(" - ") + text;
562 DCHECK(!text.empty());
563 return text;
566 const InputMethodDescriptor* InputMethodUtil::GetInputMethodDescriptorFromId(
567 const std::string& input_method_id) const {
568 InputMethodIdToDescriptorMap::const_iterator iter =
569 id_to_descriptor_.find(input_method_id);
570 if (iter == id_to_descriptor_.end())
571 return NULL;
572 return &(iter->second);
575 bool InputMethodUtil::GetInputMethodIdsFromLanguageCode(
576 const std::string& normalized_language_code,
577 InputMethodType type,
578 std::vector<std::string>* out_input_method_ids) const {
579 return GetInputMethodIdsFromLanguageCodeInternal(
580 language_code_to_ids_,
581 normalized_language_code, type, out_input_method_ids);
584 bool InputMethodUtil::GetInputMethodIdsFromLanguageCodeInternal(
585 const std::multimap<std::string, std::string>& language_code_to_ids,
586 const std::string& normalized_language_code,
587 InputMethodType type,
588 std::vector<std::string>* out_input_method_ids) const {
589 DCHECK(out_input_method_ids);
590 out_input_method_ids->clear();
592 bool result = false;
593 std::pair<LanguageCodeToIdsMap::const_iterator,
594 LanguageCodeToIdsMap::const_iterator> range =
595 language_code_to_ids.equal_range(normalized_language_code);
596 for (LanguageCodeToIdsMap::const_iterator iter = range.first;
597 iter != range.second; ++iter) {
598 const std::string& input_method_id = iter->second;
599 if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
600 out_input_method_ids->push_back(input_method_id);
601 result = true;
604 if ((type == kAllInputMethods) && !result) {
605 DVLOG(1) << "Unknown language code: " << normalized_language_code;
607 return result;
610 void InputMethodUtil::GetFirstLoginInputMethodIds(
611 const std::string& language_code,
612 const InputMethodDescriptor& current_input_method,
613 std::vector<std::string>* out_input_method_ids) const {
614 out_input_method_ids->clear();
616 // First, add the current keyboard layout (one used on the login screen).
617 out_input_method_ids->push_back(current_input_method.id());
619 const std::string current_layout
620 = current_input_method.GetPreferredKeyboardLayout();
621 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultInputMethodRecommendation);
622 ++i) {
623 if (kDefaultInputMethodRecommendation[i].locale == language_code &&
624 kDefaultInputMethodRecommendation[i].layout == current_layout) {
625 out_input_method_ids->push_back(
626 kDefaultInputMethodRecommendation[i].input_method_id);
627 return;
631 // Second, find the most popular input method associated with the
632 // current UI language. The input method IDs returned from
633 // GetInputMethodIdsFromLanguageCode() are sorted by popularity, hence
634 // our basic strategy is to pick the first one, but it's a bit more
635 // complicated as shown below.
636 std::string most_popular_id;
637 std::vector<std::string> input_method_ids;
638 // This returns the input methods sorted by popularity.
639 GetInputMethodIdsFromLanguageCode(
640 language_code, kAllInputMethods, &input_method_ids);
641 for (size_t i = 0; i < input_method_ids.size(); ++i) {
642 const std::string& input_method_id = input_method_ids[i];
643 // Pick the first one.
644 if (most_popular_id.empty())
645 most_popular_id = input_method_id;
647 // Check if there is one that matches the current keyboard layout, but
648 // not the current keyboard itself. This is useful if there are
649 // multiple keyboard layout choices for one input method. For
650 // instance, Mozc provides three choices: mozc (US keyboard), mozc-jp
651 // (JP keyboard), mozc-dv (Dvorak).
652 const InputMethodDescriptor* descriptor =
653 GetInputMethodDescriptorFromId(input_method_id);
654 if (descriptor &&
655 descriptor->id() != current_input_method.id() &&
656 descriptor->GetPreferredKeyboardLayout() ==
657 current_input_method.GetPreferredKeyboardLayout()) {
658 most_popular_id = input_method_id;
659 break;
662 // Add the most popular input method ID, if it's different from the
663 // current input method.
664 if (most_popular_id != current_input_method.id() &&
665 // TODO(yusukes): Remove this hack when we remove the "english-m" IME.
666 most_popular_id != "english-m") {
667 out_input_method_ids->push_back(most_popular_id);
671 void InputMethodUtil::GetLanguageCodesFromInputMethodIds(
672 const std::vector<std::string>& input_method_ids,
673 std::vector<std::string>* out_language_codes) const {
674 out_language_codes->clear();
676 for (size_t i = 0; i < input_method_ids.size(); ++i) {
677 const std::string& input_method_id = input_method_ids[i];
678 const InputMethodDescriptor* input_method =
679 GetInputMethodDescriptorFromId(input_method_id);
680 if (!input_method) {
681 DVLOG(1) << "Unknown input method ID: " << input_method_ids[i];
682 continue;
684 DCHECK(!input_method->language_codes().empty());
685 const std::string language_code = input_method->language_codes().at(0);
686 // Add it if it's not already present.
687 if (std::count(out_language_codes->begin(), out_language_codes->end(),
688 language_code) == 0) {
689 out_language_codes->push_back(language_code);
694 std::string InputMethodUtil::GetLanguageDefaultInputMethodId(
695 const std::string& language_code) {
696 std::vector<std::string> candidates;
697 GetInputMethodIdsFromLanguageCode(
698 language_code, input_method::kKeyboardLayoutsOnly, &candidates);
699 if (candidates.size())
700 return candidates.front();
702 return std::string();
705 bool InputMethodUtil::MigrateXkbInputMethods(
706 std::vector<std::string>* input_method_ids) {
707 bool rewritten = false;
708 std::vector<std::string>& ids = *input_method_ids;
709 for (size_t i = 0; i < ids.size(); ++i) {
710 std::string id =
711 extension_ime_util::GetInputMethodIDByKeyboardLayout(ids[i]);
712 // Migrates the old chinese ime id to new ones.
713 // TODO(shuchen): Change the function name to MigrateInputMethods,
714 // and create an abstract layer to map a comprehensive input method id to
715 // the real extension based input method id.
716 // e.g. "zh-t-i0-pinyin" maps to
717 // "_comp_id_gjaehgfemfahhmlgpdfknkhdnemmolopzh-t-i0-pinyin".
718 // See crbug.com/358083.
719 for (size_t j = 0; j < arraysize(kOldChineseExtensionIds); ++j) {
720 size_t pos = id.find(kOldChineseExtensionIds[j]);
721 if (pos != std::string::npos) {
722 id.replace(pos, kExtensionIdLen, kNewChineseExtensionId);
723 break;
726 if (id != ids[i]) {
727 ids[i] = id;
728 rewritten = true;
731 if (rewritten) {
732 // Removes the duplicates.
733 std::vector<std::string> new_ids;
734 for (size_t i = 0; i < ids.size(); ++i) {
735 if (std::find(new_ids.begin(), new_ids.end(), ids[i]) == new_ids.end())
736 new_ids.push_back(ids[i]);
738 ids.swap(new_ids);
740 return rewritten;
743 void InputMethodUtil::UpdateHardwareLayoutCache() {
744 DCHECK(thread_checker_.CalledOnValidThread());
745 hardware_layouts_.clear();
746 hardware_login_layouts_.clear();
747 if (cached_hardware_layouts_.empty())
748 Tokenize(delegate_->GetHardwareKeyboardLayouts(), ",",
749 &cached_hardware_layouts_);
750 hardware_layouts_ = cached_hardware_layouts_;
751 MigrateXkbInputMethods(&hardware_layouts_);
753 for (size_t i = 0; i < hardware_layouts_.size(); ++i) {
754 if (IsLoginKeyboard(hardware_layouts_[i]))
755 hardware_login_layouts_.push_back(hardware_layouts_[i]);
757 if (hardware_layouts_.empty()) {
758 // This is totally fine if it's empty. The hardware keyboard layout is
759 // not stored if startup_manifest.json (OEM customization data) is not
760 // present (ex. Cr48 doen't have that file).
761 hardware_layouts_.push_back(GetFallbackInputMethodDescriptor().id());
764 if (hardware_login_layouts_.empty())
765 hardware_login_layouts_.push_back(GetFallbackInputMethodDescriptor().id());
768 void InputMethodUtil::SetHardwareKeyboardLayoutForTesting(
769 const std::string& layout) {
770 delegate_->SetHardwareKeyboardLayoutForTesting(layout);
771 cached_hardware_layouts_.clear();
772 UpdateHardwareLayoutCache();
775 const std::vector<std::string>&
776 InputMethodUtil::GetHardwareInputMethodIds() {
777 DCHECK(thread_checker_.CalledOnValidThread());
778 UpdateHardwareLayoutCache();
779 return hardware_layouts_;
782 const std::vector<std::string>&
783 InputMethodUtil::GetHardwareLoginInputMethodIds() {
784 DCHECK(thread_checker_.CalledOnValidThread());
785 UpdateHardwareLayoutCache();
786 return hardware_login_layouts_;
789 bool InputMethodUtil::IsLoginKeyboard(const std::string& input_method_id)
790 const {
791 const InputMethodDescriptor* ime =
792 GetInputMethodDescriptorFromId(input_method_id);
793 return ime ? ime->is_login_keyboard() : false;
796 void InputMethodUtil::SetComponentExtensions(
797 const InputMethodDescriptors& imes) {
798 for (size_t i = 0; i < imes.size(); ++i) {
799 const InputMethodDescriptor& input_method = imes[i];
800 DCHECK(!input_method.language_codes().empty());
801 const std::vector<std::string>& language_codes =
802 input_method.language_codes();
803 id_to_language_code_[input_method.id()] = language_codes[0];
804 id_to_descriptor_[input_method.id()] = input_method;
806 typedef LanguageCodeToIdsMap::const_iterator It;
807 for (size_t j = 0; j < language_codes.size(); ++j) {
808 std::pair<It, It> range =
809 language_code_to_ids_.equal_range(language_codes[j]);
810 It it = range.first;
811 for (; it != range.second; ++it) {
812 if (it->second == input_method.id())
813 break;
815 if (it == range.second)
816 language_code_to_ids_.insert(
817 std::make_pair(language_codes[j], input_method.id()));
822 void InputMethodUtil::InitXkbInputMethodsForTesting() {
823 cached_hardware_layouts_.clear();
824 if (!extension_ime_util::UseWrappedExtensionKeyboardLayouts())
825 return;
826 scoped_ptr<InputMethodDescriptors> original_imes =
827 InputMethodWhitelist().GetSupportedInputMethods();
828 InputMethodDescriptors whitelist_imes;
829 for (size_t i = 0; i < original_imes->size(); ++i) {
830 const InputMethodDescriptor& ime = (*original_imes)[i];
831 whitelist_imes.push_back(InputMethodDescriptor(
832 extension_ime_util::GetInputMethodIDByKeyboardLayout(ime.id()),
834 ime.indicator(),
835 ime.keyboard_layouts(),
836 ime.language_codes(),
837 ime.is_login_keyboard(),
838 ime.options_page_url(),
839 ime.input_view_url()));
841 SetComponentExtensions(whitelist_imes);
844 InputMethodDescriptor InputMethodUtil::GetFallbackInputMethodDescriptor() {
845 std::vector<std::string> layouts;
846 layouts.push_back("us");
847 std::vector<std::string> languages;
848 languages.push_back("en-US");
849 return InputMethodDescriptor(
850 extension_ime_util::GetInputMethodIDByKeyboardLayout("xkb:us::eng"),
852 "US",
853 layouts,
854 languages,
855 true, // login keyboard.
856 GURL(), // options page, not available.
857 GURL()); // input view page, not available.
860 void InputMethodUtil::ReloadInternalMaps() {
861 if (supported_input_methods_->size() <= 1) {
862 DVLOG(1) << "GetSupportedInputMethods returned a fallback ID";
863 // TODO(yusukes): Handle this error in nicer way.
866 // Clear the existing maps.
867 language_code_to_ids_.clear();
868 id_to_language_code_.clear();
869 id_to_descriptor_.clear();
870 xkb_id_to_descriptor_.clear();
872 for (size_t i = 0; i < supported_input_methods_->size(); ++i) {
873 const InputMethodDescriptor& input_method =
874 supported_input_methods_->at(i);
875 const std::vector<std::string>& language_codes =
876 input_method.language_codes();
877 for (size_t i = 0; i < language_codes.size(); ++i) {
878 language_code_to_ids_.insert(
879 std::make_pair(language_codes[i], input_method.id()));
880 // Remember the pairs.
881 id_to_language_code_.insert(
882 std::make_pair(input_method.id(), language_codes[i]));
884 id_to_descriptor_.insert(
885 std::make_pair(input_method.id(), input_method));
886 if (IsKeyboardLayout(input_method.id())) {
887 xkb_id_to_descriptor_.insert(
888 std::make_pair(input_method.GetPreferredKeyboardLayout(),
889 input_method));
894 } // namespace input_method
895 } // namespace chromeos