cros_language_options_handler.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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. 4 5#include "chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h" 6 7#include <map> 8#include <set> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/bind_helpers.h" 13#include "base/i18n/rtl.h" 14#include "base/strings/stringprintf.h" 15#include "base/strings/utf_string_conversions.h" 16#include "base/values.h" 17#include "chrome/app/chrome_command_ids.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/chromeos/input_method/input_method_util.h" 20#include "chrome/browser/lifetime/application_lifetime.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/ui/browser.h" 23#include "chrome/browser/ui/browser_finder.h" 24#include "chromeos/ime/component_extension_ime_manager.h" 25#include "chromeos/ime/input_method_manager.h" 26#include "content/public/browser/navigation_controller.h" 27#include "content/public/browser/user_metrics.h" 28#include "content/public/browser/web_contents.h" 29#include "grit/chromium_strings.h" 30#include "grit/generated_resources.h" 31#include "ui/base/l10n/l10n_util.h" 32 33using content::UserMetricsAction; 34 35namespace { 36// TODO(zork): Remove this blacklist when fonts are added to Chrome OS. 37// see: crbug.com/240586 38 39bool IsBlacklisted(const std::string& language_code) { 40 return language_code == "si"; // Sinhala 41} 42 43} // namespace 44 45namespace chromeos { 46namespace options { 47 48CrosLanguageOptionsHandler::CrosLanguageOptionsHandler() 49 : composition_extension_appended_(false), 50 is_page_initialized_(false) { 51 input_method::InputMethodManager::Get()->GetComponentExtensionIMEManager()-> 52 AddObserver(this); 53} 54 55CrosLanguageOptionsHandler::~CrosLanguageOptionsHandler() { 56 input_method::InputMethodManager::Get()->GetComponentExtensionIMEManager()-> 57 RemoveObserver(this); 58} 59 60void CrosLanguageOptionsHandler::GetLocalizedValues( 61 DictionaryValue* localized_strings) { 62 ::options::LanguageOptionsHandlerCommon::GetLocalizedValues( 63 localized_strings); 64 65 RegisterTitle(localized_strings, "languagePage", 66 IDS_OPTIONS_SETTINGS_LANGUAGES_AND_INPUT_DIALOG_TITLE); 67 localized_strings->SetString("okButton", l10n_util::GetStringUTF16(IDS_OK)); 68 localized_strings->SetString("configure", 69 l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_CONFIGURE)); 70 localized_strings->SetString("inputMethod", 71 l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD)); 72 localized_strings->SetString("pleaseAddAnotherInputMethod", 73 l10n_util::GetStringUTF16( 74 IDS_OPTIONS_SETTINGS_LANGUAGES_PLEASE_ADD_ANOTHER_INPUT_METHOD)); 75 localized_strings->SetString("inputMethodInstructions", 76 l10n_util::GetStringUTF16( 77 IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_INSTRUCTIONS)); 78 localized_strings->SetString("switchInputMethodsHint", 79 l10n_util::GetStringUTF16( 80 IDS_OPTIONS_SETTINGS_LANGUAGES_SWITCH_INPUT_METHODS_HINT)); 81 localized_strings->SetString("selectPreviousInputMethodHint", 82 l10n_util::GetStringUTF16( 83 IDS_OPTIONS_SETTINGS_LANGUAGES_SELECT_PREVIOUS_INPUT_METHOD_HINT)); 84 localized_strings->SetString("restartButton", 85 l10n_util::GetStringUTF16( 86 IDS_OPTIONS_SETTINGS_LANGUAGES_SIGN_OUT_BUTTON)); 87 localized_strings->SetString("extensionImeLable", 88 l10n_util::GetStringUTF16( 89 IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_EXTENSION_IME)); 90 localized_strings->SetString("extensionImeDescription", 91 l10n_util::GetStringUTF16( 92 IDS_OPTIONS_SETTINGS_LANGUAGES_INPUT_METHOD_EXTENSION_DESCRIPTION)); 93 localized_strings->SetString("noInputMethods", 94 l10n_util::GetStringUTF16( 95 IDS_OPTIONS_SETTINGS_LANGUAGES_NO_INPUT_METHODS)); 96 97 input_method::InputMethodManager* manager = 98 input_method::InputMethodManager::Get(); 99 // GetSupportedInputMethods() never return NULL. 100 scoped_ptr<input_method::InputMethodDescriptors> descriptors( 101 manager->GetSupportedInputMethods()); 102 localized_strings->Set("languageList", GetAcceptLanguageList(*descriptors)); 103 localized_strings->Set("inputMethodList", GetInputMethodList(*descriptors)); 104 105 input_method::InputMethodDescriptors ext_ime_descriptors; 106 manager->GetInputMethodExtensions(&ext_ime_descriptors); 107 localized_strings->Set("extensionImeList", 108 ConvertInputMethodDescriptosToIMEList( 109 ext_ime_descriptors)); 110 111 ComponentExtensionIMEManager* component_extension_manager = 112 input_method::InputMethodManager::Get() 113 ->GetComponentExtensionIMEManager(); 114 if (component_extension_manager->IsInitialized()) { 115 localized_strings->Set( 116 "componentExtensionImeList", 117 ConvertInputMethodDescriptosToIMEList( 118 component_extension_manager->GetAllIMEAsInputMethodDescriptor())); 119 composition_extension_appended_ = true; 120 } else { 121 // If component extension IME manager is not ready for use, it will be 122 // added in |InitializePage()|. 123 localized_strings->Set("componentExtensionImeList", 124 new ListValue()); 125 } 126} 127 128void CrosLanguageOptionsHandler::RegisterMessages() { 129 ::options::LanguageOptionsHandlerCommon::RegisterMessages(); 130 131 web_ui()->RegisterMessageCallback("inputMethodDisable", 132 base::Bind(&CrosLanguageOptionsHandler::InputMethodDisableCallback, 133 base::Unretained(this))); 134 web_ui()->RegisterMessageCallback("inputMethodEnable", 135 base::Bind(&CrosLanguageOptionsHandler::InputMethodEnableCallback, 136 base::Unretained(this))); 137 web_ui()->RegisterMessageCallback("inputMethodOptionsOpen", 138 base::Bind(&CrosLanguageOptionsHandler::InputMethodOptionsOpenCallback, 139 base::Unretained(this))); 140 web_ui()->RegisterMessageCallback("uiLanguageRestart", 141 base::Bind(&CrosLanguageOptionsHandler::RestartCallback, 142 base::Unretained(this))); 143} 144 145ListValue* CrosLanguageOptionsHandler::GetInputMethodList( 146 const input_method::InputMethodDescriptors& descriptors) { 147 input_method::InputMethodManager* manager = 148 input_method::InputMethodManager::Get(); 149 150 ListValue* input_method_list = new ListValue(); 151 152 for (size_t i = 0; i < descriptors.size(); ++i) { 153 const input_method::InputMethodDescriptor& descriptor = 154 descriptors[i]; 155 const std::string display_name = 156 manager->GetInputMethodUtil()->GetInputMethodDisplayNameFromId( 157 descriptor.id()); 158 DictionaryValue* dictionary = new DictionaryValue(); 159 dictionary->SetString("id", descriptor.id()); 160 dictionary->SetString("displayName", display_name); 161 162 // One input method can be associated with multiple languages, hence 163 // we use a dictionary here. 164 DictionaryValue* languages = new DictionaryValue(); 165 for (size_t i = 0; i < descriptor.language_codes().size(); ++i) { 166 languages->SetBoolean(descriptor.language_codes().at(i), true); 167 } 168 // Check extra languages to see if there are languages associated with 169 // this input method. If these are present, add these. 170 const std::vector<std::string> extra_language_codes = 171 manager->GetInputMethodUtil()->GetExtraLanguageCodesFromId( 172 descriptor.id()); 173 for (size_t j = 0; j < extra_language_codes.size(); ++j) 174 languages->SetBoolean(extra_language_codes[j], true); 175 dictionary->Set("languageCodeSet", languages); 176 177 input_method_list->Append(dictionary); 178 } 179 180 return input_method_list; 181} 182 183// static 184ListValue* CrosLanguageOptionsHandler::GetLanguageListInternal( 185 const input_method::InputMethodDescriptors& descriptors, 186 const std::vector<std::string>& base_language_codes) { 187 const std::string app_locale = g_browser_process->GetApplicationLocale(); 188 189 std::set<std::string> language_codes; 190 // Collect the language codes from the supported input methods. 191 for (size_t i = 0; i < descriptors.size(); ++i) { 192 const input_method::InputMethodDescriptor& descriptor = descriptors[i]; 193 const std::vector<std::string>& languages = 194 descriptor.language_codes(); 195 for (size_t i = 0; i < languages.size(); ++i) 196 language_codes.insert(languages[i]); 197 } 198 // Collect the language codes from extra languages. 199 const std::vector<std::string> extra_language_codes = 200 input_method::InputMethodManager::Get()->GetInputMethodUtil() 201 ->GetExtraLanguageCodeList(); 202 for (size_t i = 0; i < extra_language_codes.size(); ++i) 203 language_codes.insert(extra_language_codes[i]); 204 205 // Map of display name -> {language code, native_display_name}. 206 // In theory, we should be able to create a map that is sorted by 207 // display names using ICU comparator, but doing it is hard, thus we'll 208 // use an auxiliary vector to achieve the same result. 209 typedef std::pair<std::string, string16> LanguagePair; 210 typedef std::map<string16, LanguagePair> LanguageMap; 211 LanguageMap language_map; 212 // The auxiliary vector mentioned above. 213 std::vector<string16> display_names; 214 215 // Build the list of display names, and build the language map. 216 for (std::set<std::string>::const_iterator iter = language_codes.begin(); 217 iter != language_codes.end(); ++iter) { 218 // Exclude the language which is not in |base_langauge_codes| even it has 219 // input methods. 220 if (std::find(base_language_codes.begin(), 221 base_language_codes.end(), 222 *iter) == base_language_codes.end()) { 223 continue; 224 } 225 226 const string16 display_name = 227 l10n_util::GetDisplayNameForLocale(*iter, app_locale, true); 228 const string16 native_display_name = 229 l10n_util::GetDisplayNameForLocale(*iter, *iter, true); 230 231 display_names.push_back(display_name); 232 language_map[display_name] = 233 std::make_pair(*iter, native_display_name); 234 } 235 DCHECK_EQ(display_names.size(), language_map.size()); 236 237 // Build the list of display names, and build the language map. 238 for (size_t i = 0; i < base_language_codes.size(); ++i) { 239 // Skip this language if it was already added. 240 if (language_codes.find(base_language_codes[i]) != language_codes.end()) 241 continue; 242 243 // TODO(zork): Remove this blacklist when fonts are added to Chrome OS. 244 // see: crbug.com/240586 245 if (IsBlacklisted(base_language_codes[i])) 246 continue; 247 248 string16 display_name = 249 l10n_util::GetDisplayNameForLocale( 250 base_language_codes[i], app_locale, false); 251 string16 native_display_name = 252 l10n_util::GetDisplayNameForLocale( 253 base_language_codes[i], base_language_codes[i], false); 254 display_names.push_back(display_name); 255 language_map[display_name] = 256 std::make_pair(base_language_codes[i], native_display_name); 257 } 258 259 // Sort display names using locale specific sorter. 260 l10n_util::SortStrings16(app_locale, &display_names); 261 262 // Build the language list from the language map. 263 ListValue* language_list = new ListValue(); 264 for (size_t i = 0; i < display_names.size(); ++i) { 265 // Sets the directionality of the display language name. 266 string16 display_name(display_names[i]); 267 bool markup_removal = 268 base::i18n::UnadjustStringForLocaleDirection(&display_name); 269 DCHECK(markup_removal); 270 bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(display_name); 271 std::string directionality = has_rtl_chars ? "rtl" : "ltr"; 272 273 const LanguagePair& pair = language_map[display_names[i]]; 274 DictionaryValue* dictionary = new DictionaryValue(); 275 dictionary->SetString("code", pair.first); 276 dictionary->SetString("displayName", display_names[i]); 277 dictionary->SetString("textDirection", directionality); 278 dictionary->SetString("nativeDisplayName", pair.second); 279 language_list->Append(dictionary); 280 } 281 282 return language_list; 283} 284 285// static 286base::ListValue* CrosLanguageOptionsHandler::GetAcceptLanguageList( 287 const input_method::InputMethodDescriptors& descriptors) { 288 // Collect the language codes from the supported accept-languages. 289 const std::string app_locale = g_browser_process->GetApplicationLocale(); 290 std::vector<std::string> accept_language_codes; 291 l10n_util::GetAcceptLanguagesForLocale(app_locale, &accept_language_codes); 292 return GetLanguageListInternal(descriptors, accept_language_codes); 293} 294 295// static 296base::ListValue* CrosLanguageOptionsHandler::GetUILanguageList( 297 const input_method::InputMethodDescriptors& descriptors) { 298 // Collect the language codes from the available locales. 299 return GetLanguageListInternal(descriptors, l10n_util::GetAvailableLocales()); 300} 301 302base::ListValue* 303 CrosLanguageOptionsHandler::ConvertInputMethodDescriptosToIMEList( 304 const input_method::InputMethodDescriptors& descriptors) { 305 scoped_ptr<ListValue> ime_ids_list(new ListValue()); 306 for (size_t i = 0; i < descriptors.size(); ++i) { 307 const input_method::InputMethodDescriptor& descriptor = descriptors[i]; 308 scoped_ptr<DictionaryValue> dictionary(new DictionaryValue()); 309 dictionary->SetString("id", descriptor.id()); 310 dictionary->SetString("displayName", descriptor.name()); 311 dictionary->SetString("optionsPage", descriptor.options_page_url().spec()); 312 scoped_ptr<DictionaryValue> language_codes(new DictionaryValue()); 313 for (size_t i = 0; i < descriptor.language_codes().size(); ++i) 314 language_codes->SetBoolean(descriptor.language_codes().at(i), true); 315 dictionary->Set("languageCodeSet", language_codes.release()); 316 ime_ids_list->Append(dictionary.release()); 317 } 318 return ime_ids_list.release(); 319} 320 321string16 CrosLanguageOptionsHandler::GetProductName() { 322 return l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME); 323} 324 325void CrosLanguageOptionsHandler::SetApplicationLocale( 326 const std::string& language_code) { 327 Profile::FromWebUI(web_ui())->ChangeAppLocale( 328 language_code, Profile::APP_LOCALE_CHANGED_VIA_SETTINGS); 329} 330 331void CrosLanguageOptionsHandler::RestartCallback(const ListValue* args) { 332 content::RecordAction(UserMetricsAction("LanguageOptions_SignOut")); 333 chrome::AttemptUserExit(); 334} 335 336void CrosLanguageOptionsHandler::InputMethodDisableCallback( 337 const ListValue* args) { 338 const std::string input_method_id = UTF16ToASCII(ExtractStringValue(args)); 339 const std::string action = base::StringPrintf( 340 "LanguageOptions_DisableInputMethod_%s", input_method_id.c_str()); 341 content::RecordComputedAction(action); 342} 343 344void CrosLanguageOptionsHandler::InputMethodEnableCallback( 345 const ListValue* args) { 346 const std::string input_method_id = UTF16ToASCII(ExtractStringValue(args)); 347 const std::string action = base::StringPrintf( 348 "LanguageOptions_EnableInputMethod_%s", input_method_id.c_str()); 349 content::RecordComputedAction(action); 350} 351 352void CrosLanguageOptionsHandler::InputMethodOptionsOpenCallback( 353 const ListValue* args) { 354 const std::string input_method_id = UTF16ToASCII(ExtractStringValue(args)); 355 const std::string action = base::StringPrintf( 356 "InputMethodOptions_Open_%s", input_method_id.c_str()); 357 content::RecordComputedAction(action); 358} 359 360void CrosLanguageOptionsHandler::OnInitialized() { 361 if (composition_extension_appended_ || !is_page_initialized_) { 362 // If an option page is not ready to call JavaScript, appending component 363 // extension IMEs will be done in InitializePage function later. 364 return; 365 } 366 367 ComponentExtensionIMEManager* manager = 368 input_method::InputMethodManager::Get() 369 ->GetComponentExtensionIMEManager(); 370 371 DCHECK(manager->IsInitialized()); 372 scoped_ptr<ListValue> ime_list( 373 ConvertInputMethodDescriptosToIMEList( 374 manager->GetAllIMEAsInputMethodDescriptor())); 375 web_ui()->CallJavascriptFunction( 376 "options.LanguageOptions.onComponentManagerInitialized", 377 *ime_list); 378 composition_extension_appended_ = true; 379} 380 381void CrosLanguageOptionsHandler::InitializePage() { 382 is_page_initialized_ = true; 383 if (composition_extension_appended_) 384 return; 385 386 ComponentExtensionIMEManager* component_extension_manager = 387 input_method::InputMethodManager::Get() 388 ->GetComponentExtensionIMEManager(); 389 if (!component_extension_manager->IsInitialized()) { 390 // If the component extension IME manager is not available yet, append the 391 // component extension list in |OnInitialized()|. 392 return; 393 } 394 395 scoped_ptr<ListValue> ime_list( 396 ConvertInputMethodDescriptosToIMEList( 397 component_extension_manager->GetAllIMEAsInputMethodDescriptor())); 398 web_ui()->CallJavascriptFunction( 399 "options.LanguageOptions.onComponentManagerInitialized", 400 *ime_list); 401 composition_extension_appended_ = true; 402} 403 404} // namespace options 405} // namespace chromeos 406