Home | History | Annotate | Download | only in login
      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 
     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.
     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
     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.
     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|.
    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 
    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 
    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 
    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 
    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 
    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 
    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