• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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.
4 
5 #include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
6 
7 #include <algorithm>
8 #include <iterator>
9 #include <map>
10 #include <set>
11 #include <utility>
12 
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/i18n/rtl.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/memory/ref_counted.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task_runner_util.h"
24 #include "base/threading/sequenced_worker_pool.h"
25 #include "base/values.h"
26 #include "chrome/browser/browser_process.h"
27 #include "chrome/browser/chromeos/customization_document.h"
28 #include "chrome/browser/chromeos/input_method/input_method_util.h"
29 #include "chrome/grit/generated_resources.h"
30 #include "chromeos/ime/component_extension_ime_manager.h"
31 #include "chromeos/ime/input_method_descriptor.h"
32 #include "chromeos/ime/input_method_manager.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "ui/base/l10n/l10n_util.h"
35 
36 namespace chromeos {
37 
38 namespace {
39 
40 const char kSequenceToken[] = "chromeos_login_l10n_util";
41 
CreateInputMethodsEntry(const input_method::InputMethodDescriptor & method,const std::string selected)42 scoped_ptr<base::DictionaryValue> CreateInputMethodsEntry(
43     const input_method::InputMethodDescriptor& method,
44     const std::string selected) {
45   input_method::InputMethodUtil* util =
46       input_method::InputMethodManager::Get()->GetInputMethodUtil();
47   const std::string& ime_id = method.id();
48   scoped_ptr<base::DictionaryValue> input_method(new base::DictionaryValue);
49   input_method->SetString("value", ime_id);
50   input_method->SetString("title", util->GetInputMethodLongName(method));
51   input_method->SetBoolean("selected", ime_id == selected);
52   return input_method.Pass();
53 }
54 
55 // Returns true if element was inserted.
InsertString(const std::string & str,std::set<std::string> * to)56 bool InsertString(const std::string& str, std::set<std::string>* to) {
57   const std::pair<std::set<std::string>::iterator, bool> result =
58       to->insert(str);
59   return result.second;
60 }
61 
62 #if !defined(USE_ATHENA)
63 // TODO(dpolukhin): crbug.com/407579
AddOptgroupOtherLayouts(base::ListValue * input_methods_list)64 void AddOptgroupOtherLayouts(base::ListValue* input_methods_list) {
65   scoped_ptr<base::DictionaryValue> optgroup(new base::DictionaryValue);
66   optgroup->SetString(
67       "optionGroupName",
68       l10n_util::GetStringUTF16(IDS_OOBE_OTHER_KEYBOARD_LAYOUTS));
69   input_methods_list->Append(optgroup.release());
70 }
71 #endif
72 
73 // Gets the list of languages with |descriptors| based on |base_language_codes|.
74 // The |most_relevant_language_codes| will be first in the list. If
75 // |insert_divider| is true, an entry with its "code" attribute set to
76 // kMostRelevantLanguagesDivider is placed between the most relevant languages
77 // and all others.
GetLanguageList(const input_method::InputMethodDescriptors & descriptors,const std::vector<std::string> & base_language_codes,const std::vector<std::string> & most_relevant_language_codes,bool insert_divider)78 scoped_ptr<base::ListValue> GetLanguageList(
79     const input_method::InputMethodDescriptors& descriptors,
80     const std::vector<std::string>& base_language_codes,
81     const std::vector<std::string>& most_relevant_language_codes,
82     bool insert_divider) {
83   const std::string app_locale = g_browser_process->GetApplicationLocale();
84 
85   std::set<std::string> language_codes;
86   // Collect the language codes from the supported input methods.
87   for (size_t i = 0; i < descriptors.size(); ++i) {
88     const input_method::InputMethodDescriptor& descriptor = descriptors[i];
89     const std::vector<std::string>& languages = descriptor.language_codes();
90     for (size_t i = 0; i < languages.size(); ++i)
91       language_codes.insert(languages[i]);
92   }
93 
94   // Language sort order.
95   std::map<std::string, int /* index */> language_index;
96   for (size_t i = 0; i < most_relevant_language_codes.size(); ++i)
97     language_index[most_relevant_language_codes[i]] = i;
98 
99   // Map of display name -> {language code, native_display_name}.
100   // In theory, we should be able to create a map that is sorted by
101   // display names using ICU comparator, but doing it is hard, thus we'll
102   // use an auxiliary vector to achieve the same result.
103   typedef std::pair<std::string, base::string16> LanguagePair;
104   typedef std::map<base::string16, LanguagePair> LanguageMap;
105   LanguageMap language_map;
106 
107   // The auxiliary vector mentioned above (except the most relevant locales).
108   std::vector<base::string16> display_names;
109 
110   // Separate vector of the most relevant locales.
111   std::vector<base::string16> most_relevant_locales_display_names(
112       most_relevant_language_codes.size());
113 
114   size_t most_relevant_locales_count = 0;
115 
116   // Build the list of display names, and build the language map.
117 
118   // The list of configured locales might have entries not in
119   // base_language_codes. If there are unsupported language variants,
120   // but they resolve to backup locale within base_language_codes, also
121   // add them to the list.
122   for (std::map<std::string, int>::const_iterator it = language_index.begin();
123        it != language_index.end(); ++it) {
124     const std::string& language_id = it->first;
125 
126     const std::string lang = l10n_util::GetLanguage(language_id);
127 
128     // Ignore non-specific codes.
129     if (lang.empty() || lang == language_id)
130       continue;
131 
132     if (std::find(base_language_codes.begin(),
133                   base_language_codes.end(),
134                   language_id) != base_language_codes.end()) {
135       // Language is supported. No need to replace
136       continue;
137     }
138     std::string resolved_locale;
139     if (!l10n_util::CheckAndResolveLocale(language_id, &resolved_locale))
140       continue;
141 
142     if (std::find(base_language_codes.begin(),
143                   base_language_codes.end(),
144                   resolved_locale) == base_language_codes.end()) {
145       // Resolved locale is not supported.
146       continue;
147     }
148 
149     const base::string16 display_name =
150         l10n_util::GetDisplayNameForLocale(language_id, app_locale, true);
151     const base::string16 native_display_name =
152         l10n_util::GetDisplayNameForLocale(
153             language_id, language_id, true);
154 
155     language_map[display_name] =
156         std::make_pair(language_id, native_display_name);
157 
158     most_relevant_locales_display_names[it->second] = display_name;
159     ++most_relevant_locales_count;
160   }
161 
162   // Translate language codes, generated from input methods.
163   for (std::set<std::string>::const_iterator it = language_codes.begin();
164        it != language_codes.end(); ++it) {
165      // Exclude the language which is not in |base_langauge_codes| even it has
166      // input methods.
167     if (std::find(base_language_codes.begin(),
168                   base_language_codes.end(),
169                   *it) == base_language_codes.end()) {
170       continue;
171     }
172 
173     const base::string16 display_name =
174         l10n_util::GetDisplayNameForLocale(*it, app_locale, true);
175     const base::string16 native_display_name =
176         l10n_util::GetDisplayNameForLocale(*it, *it, true);
177 
178     language_map[display_name] =
179         std::make_pair(*it, native_display_name);
180 
181     const std::map<std::string, int>::const_iterator index_pos =
182         language_index.find(*it);
183     if (index_pos != language_index.end()) {
184       base::string16& stored_display_name =
185           most_relevant_locales_display_names[index_pos->second];
186       if (stored_display_name.empty()) {
187         stored_display_name = display_name;
188         ++most_relevant_locales_count;
189       }
190     } else {
191       display_names.push_back(display_name);
192     }
193   }
194   DCHECK_EQ(display_names.size() + most_relevant_locales_count,
195             language_map.size());
196 
197   // Build the list of display names, and build the language map.
198   for (size_t i = 0; i < base_language_codes.size(); ++i) {
199     // Skip this language if it was already added.
200     if (language_codes.find(base_language_codes[i]) != language_codes.end())
201       continue;
202 
203     base::string16 display_name =
204         l10n_util::GetDisplayNameForLocale(
205             base_language_codes[i], app_locale, false);
206     base::string16 native_display_name =
207         l10n_util::GetDisplayNameForLocale(
208             base_language_codes[i], base_language_codes[i], false);
209     language_map[display_name] =
210         std::make_pair(base_language_codes[i], native_display_name);
211 
212     const std::map<std::string, int>::const_iterator index_pos =
213         language_index.find(base_language_codes[i]);
214     if (index_pos != language_index.end()) {
215       most_relevant_locales_display_names[index_pos->second] = display_name;
216       ++most_relevant_locales_count;
217     } else {
218       display_names.push_back(display_name);
219     }
220   }
221 
222   // Sort display names using locale specific sorter.
223   l10n_util::SortStrings16(app_locale, &display_names);
224   // Concatenate most_relevant_locales_display_names and display_names.
225   // Insert special divider in between.
226   std::vector<base::string16> out_display_names;
227   for (size_t i = 0; i < most_relevant_locales_display_names.size(); ++i) {
228     if (most_relevant_locales_display_names[i].size() == 0)
229       continue;
230     out_display_names.push_back(most_relevant_locales_display_names[i]);
231   }
232 
233   base::string16 divider16;
234   if (insert_divider && !out_display_names.empty()) {
235     // Insert a divider if requested, but only if
236     // |most_relevant_locales_display_names| is not empty.
237     divider16 = base::ASCIIToUTF16(kMostRelevantLanguagesDivider);
238     out_display_names.push_back(divider16);
239   }
240 
241   std::copy(display_names.begin(),
242             display_names.end(),
243             std::back_inserter(out_display_names));
244 
245   // Build the language list from the language map.
246   scoped_ptr<base::ListValue> language_list(new base::ListValue());
247   for (size_t i = 0; i < out_display_names.size(); ++i) {
248     // Sets the directionality of the display language name.
249     base::string16 display_name(out_display_names[i]);
250     if (insert_divider && display_name == divider16) {
251       // Insert divider.
252       base::DictionaryValue* dictionary = new base::DictionaryValue();
253       dictionary->SetString("code", kMostRelevantLanguagesDivider);
254       language_list->Append(dictionary);
255       continue;
256     }
257     const bool markup_removal =
258         base::i18n::UnadjustStringForLocaleDirection(&display_name);
259     DCHECK(markup_removal);
260     const bool has_rtl_chars =
261         base::i18n::StringContainsStrongRTLChars(display_name);
262     const std::string directionality = has_rtl_chars ? "rtl" : "ltr";
263 
264     const LanguagePair& pair = language_map[out_display_names[i]];
265     base::DictionaryValue* dictionary = new base::DictionaryValue();
266     dictionary->SetString("code", pair.first);
267     dictionary->SetString("displayName", out_display_names[i]);
268     dictionary->SetString("textDirection", directionality);
269     dictionary->SetString("nativeDisplayName", pair.second);
270     language_list->Append(dictionary);
271   }
272 
273   return language_list.Pass();
274 }
275 
276 // Invokes |callback| with a list of keyboard layouts that can be used for
277 // |resolved_locale|.
GetKeyboardLayoutsForResolvedLocale(const GetKeyboardLayoutsForLocaleCallback & callback,const std::string & resolved_locale)278 void GetKeyboardLayoutsForResolvedLocale(
279     const GetKeyboardLayoutsForLocaleCallback& callback,
280     const std::string& resolved_locale) {
281   input_method::InputMethodUtil* util =
282       input_method::InputMethodManager::Get()->GetInputMethodUtil();
283   std::vector<std::string> layouts = util->GetHardwareInputMethodIds();
284   std::vector<std::string> layouts_from_locale;
285   util->GetInputMethodIdsFromLanguageCode(
286       resolved_locale,
287       input_method::kKeyboardLayoutsOnly,
288       &layouts_from_locale);
289   layouts.insert(layouts.end(), layouts_from_locale.begin(),
290                  layouts_from_locale.end());
291 
292   std::string selected;
293   if (!layouts_from_locale.empty()) {
294     selected =
295         util->GetInputMethodDescriptorFromId(layouts_from_locale[0])->id();
296   }
297 
298   scoped_ptr<base::ListValue> input_methods_list(new base::ListValue);
299   std::set<std::string> input_methods_added;
300   for (std::vector<std::string>::const_iterator it = layouts.begin();
301        it != layouts.end(); ++it) {
302     const input_method::InputMethodDescriptor* ime =
303         util->GetInputMethodDescriptorFromId(*it);
304     if (!InsertString(ime->id(), &input_methods_added))
305       continue;
306     input_methods_list->Append(
307         CreateInputMethodsEntry(*ime, selected).release());
308   }
309 
310   callback.Run(input_methods_list.Pass());
311 }
312 
313 }  // namespace
314 
315 const char kMostRelevantLanguagesDivider[] = "MOST_RELEVANT_LANGUAGES_DIVIDER";
316 
GetUILanguageList(const std::vector<std::string> * most_relevant_language_codes,const std::string & selected)317 scoped_ptr<base::ListValue> GetUILanguageList(
318     const std::vector<std::string>* most_relevant_language_codes,
319     const std::string& selected) {
320   ComponentExtensionIMEManager* manager =
321       input_method::InputMethodManager::Get()->
322           GetComponentExtensionIMEManager();
323   input_method::InputMethodDescriptors descriptors =
324       manager->GetXkbIMEAsInputMethodDescriptor();
325   scoped_ptr<base::ListValue> languages_list(GetLanguageList(
326       descriptors,
327       l10n_util::GetAvailableLocales(),
328       most_relevant_language_codes
329           ? *most_relevant_language_codes
330           : StartupCustomizationDocument::GetInstance()->configured_locales(),
331       true));
332 
333   for (size_t i = 0; i < languages_list->GetSize(); ++i) {
334     base::DictionaryValue* language_info = NULL;
335     if (!languages_list->GetDictionary(i, &language_info))
336       NOTREACHED();
337 
338     std::string value;
339     language_info->GetString("code", &value);
340     std::string display_name;
341     language_info->GetString("displayName", &display_name);
342     std::string native_name;
343     language_info->GetString("nativeDisplayName", &native_name);
344 
345     // If it's an option group divider, add field name.
346     if (value == kMostRelevantLanguagesDivider) {
347       language_info->SetString(
348           "optionGroupName",
349           l10n_util::GetStringUTF16(IDS_OOBE_OTHER_LANGUAGES));
350     }
351     if (display_name != native_name) {
352       display_name = base::StringPrintf("%s - %s",
353                                         display_name.c_str(),
354                                         native_name.c_str());
355     }
356 
357     language_info->SetString("value", value);
358     language_info->SetString("title", display_name);
359     if (value == selected)
360       language_info->SetBoolean("selected", true);
361   }
362   return languages_list.Pass();
363 }
364 
FindMostRelevantLocale(const std::vector<std::string> & most_relevant_language_codes,const base::ListValue & available_locales,const std::string & fallback_locale)365 std::string FindMostRelevantLocale(
366     const std::vector<std::string>& most_relevant_language_codes,
367     const base::ListValue& available_locales,
368     const std::string& fallback_locale) {
369   for (std::vector<std::string>::const_iterator most_relevant_it =
370           most_relevant_language_codes.begin();
371        most_relevant_it != most_relevant_language_codes.end();
372        ++most_relevant_it) {
373     for (base::ListValue::const_iterator available_it =
374              available_locales.begin();
375          available_it != available_locales.end(); ++available_it) {
376       base::DictionaryValue* dict;
377       std::string available_locale;
378       if (!(*available_it)->GetAsDictionary(&dict) ||
379           !dict->GetString("value", &available_locale)) {
380         NOTREACHED();
381         continue;
382       }
383       if (available_locale == *most_relevant_it)
384         return *most_relevant_it;
385     }
386   }
387 
388   return fallback_locale;
389 }
390 
GetAcceptLanguageList()391 scoped_ptr<base::ListValue> GetAcceptLanguageList() {
392   // Collect the language codes from the supported accept-languages.
393   const std::string app_locale = g_browser_process->GetApplicationLocale();
394   std::vector<std::string> accept_language_codes;
395   l10n_util::GetAcceptLanguagesForLocale(app_locale, &accept_language_codes);
396   return GetLanguageList(
397       *input_method::InputMethodManager::Get()->GetSupportedInputMethods(),
398       accept_language_codes,
399       StartupCustomizationDocument::GetInstance()->configured_locales(),
400       false);
401 }
402 
GetAndActivateLoginKeyboardLayouts(const std::string & locale,const std::string & selected)403 scoped_ptr<base::ListValue> GetAndActivateLoginKeyboardLayouts(
404     const std::string& locale,
405     const std::string& selected) {
406   scoped_ptr<base::ListValue> input_methods_list(new base::ListValue);
407 #if !defined(USE_ATHENA)
408   // TODO(dpolukhin): crbug.com/407579
409   input_method::InputMethodManager* manager =
410       input_method::InputMethodManager::Get();
411   input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
412 
413   const std::vector<std::string>& hardware_login_input_methods =
414       util->GetHardwareLoginInputMethodIds();
415 
416   manager->GetActiveIMEState()->EnableLoginLayouts(
417       locale, hardware_login_input_methods);
418 
419   scoped_ptr<input_method::InputMethodDescriptors> input_methods(
420       manager->GetActiveIMEState()->GetActiveInputMethods());
421   std::set<std::string> input_methods_added;
422 
423   for (std::vector<std::string>::const_iterator i =
424            hardware_login_input_methods.begin();
425        i != hardware_login_input_methods.end();
426        ++i) {
427     const input_method::InputMethodDescriptor* ime =
428         util->GetInputMethodDescriptorFromId(*i);
429     // Do not crash in case of misconfiguration.
430     if (ime) {
431       input_methods_added.insert(*i);
432       input_methods_list->Append(
433           CreateInputMethodsEntry(*ime, selected).release());
434     } else {
435       NOTREACHED();
436     }
437   }
438 
439   bool optgroup_added = false;
440   for (size_t i = 0; i < input_methods->size(); ++i) {
441     // Makes sure the id is in legacy xkb id format.
442     const std::string& ime_id = (*input_methods)[i].id();
443     if (!InsertString(ime_id, &input_methods_added))
444       continue;
445     if (!optgroup_added) {
446       optgroup_added = true;
447       AddOptgroupOtherLayouts(input_methods_list.get());
448     }
449     input_methods_list->Append(CreateInputMethodsEntry((*input_methods)[i],
450                                                        selected).release());
451   }
452 
453   // "xkb:us::eng" should always be in the list of available layouts.
454   const std::string us_keyboard_id =
455       util->GetFallbackInputMethodDescriptor().id();
456   if (input_methods_added.find(us_keyboard_id) == input_methods_added.end()) {
457     const input_method::InputMethodDescriptor* us_eng_descriptor =
458         util->GetInputMethodDescriptorFromId(us_keyboard_id);
459     DCHECK(us_eng_descriptor);
460     if (!optgroup_added) {
461       optgroup_added = true;
462       AddOptgroupOtherLayouts(input_methods_list.get());
463     }
464     input_methods_list->Append(CreateInputMethodsEntry(*us_eng_descriptor,
465                                                        selected).release());
466   }
467 #endif
468   return input_methods_list.Pass();
469 }
470 
GetKeyboardLayoutsForLocale(const GetKeyboardLayoutsForLocaleCallback & callback,const std::string & locale)471 void GetKeyboardLayoutsForLocale(
472     const GetKeyboardLayoutsForLocaleCallback& callback,
473     const std::string& locale) {
474   base::SequencedWorkerPool* worker_pool =
475       content::BrowserThread::GetBlockingPool();
476   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
477       worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
478           worker_pool->GetNamedSequenceToken(kSequenceToken),
479           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
480 
481   // Resolve |locale| on a background thread, then continue on the current
482   // thread.
483   std::string (*get_application_locale)(const std::string&, bool) =
484       &l10n_util::GetApplicationLocale;
485   base::PostTaskAndReplyWithResult(
486       background_task_runner.get(),
487       FROM_HERE,
488       base::Bind(get_application_locale, locale, false /* set_icu_locale */),
489       base::Bind(&GetKeyboardLayoutsForResolvedLocale, callback));
490 }
491 
GetCurrentKeyboardLayout()492 scoped_ptr<base::DictionaryValue> GetCurrentKeyboardLayout() {
493   const input_method::InputMethodDescriptor current_input_method =
494       input_method::InputMethodManager::Get()
495           ->GetActiveIMEState()
496           ->GetCurrentInputMethod();
497   return CreateInputMethodsEntry(current_input_method,
498                                  current_input_method.id());
499 }
500 
501 }  // namespace chromeos
502