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