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/ui/webui/options/chromeos/cros_language_options_handler.h"
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"
33 using content::UserMetricsAction;
36 // TODO(zork): Remove this blacklist when fonts are added to Chrome OS.
37 // see: crbug.com/240586
39 bool IsBlacklisted(const std::string& language_code) {
40 return language_code == "si"; // Sinhala
48 CrosLanguageOptionsHandler::CrosLanguageOptionsHandler()
49 : composition_extension_appended_(false),
50 is_page_initialized_(false) {
51 input_method::InputMethodManager::Get()->GetComponentExtensionIMEManager()->
55 CrosLanguageOptionsHandler::~CrosLanguageOptionsHandler() {
56 input_method::InputMethodManager::Get()->GetComponentExtensionIMEManager()->
60 void CrosLanguageOptionsHandler::GetLocalizedValues(
61 DictionaryValue* localized_strings) {
62 ::options::LanguageOptionsHandlerCommon::GetLocalizedValues(
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));
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));
105 input_method::InputMethodDescriptors ext_ime_descriptors;
106 manager->GetInputMethodExtensions(&ext_ime_descriptors);
107 localized_strings->Set("extensionImeList",
108 ConvertInputMethodDescriptosToIMEList(
109 ext_ime_descriptors));
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;
121 // If component extension IME manager is not ready for use, it will be
122 // added in |InitializePage()|.
123 localized_strings->Set("componentExtensionImeList",
128 void CrosLanguageOptionsHandler::RegisterMessages() {
129 ::options::LanguageOptionsHandlerCommon::RegisterMessages();
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)));
145 ListValue* CrosLanguageOptionsHandler::GetInputMethodList(
146 const input_method::InputMethodDescriptors& descriptors) {
147 input_method::InputMethodManager* manager =
148 input_method::InputMethodManager::Get();
150 ListValue* input_method_list = new ListValue();
152 for (size_t i = 0; i < descriptors.size(); ++i) {
153 const input_method::InputMethodDescriptor& descriptor =
155 const std::string display_name =
156 manager->GetInputMethodUtil()->GetInputMethodDisplayNameFromId(
158 DictionaryValue* dictionary = new DictionaryValue();
159 dictionary->SetString("id", descriptor.id());
160 dictionary->SetString("displayName", display_name);
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);
168 dictionary->Set("languageCodeSet", languages);
170 input_method_list->Append(dictionary);
173 return input_method_list;
177 ListValue* CrosLanguageOptionsHandler::GetLanguageListInternal(
178 const input_method::InputMethodDescriptors& descriptors,
179 const std::vector<std::string>& base_language_codes) {
180 const std::string app_locale = g_browser_process->GetApplicationLocale();
182 std::set<std::string> language_codes;
183 // Collect the language codes from the supported input methods.
184 for (size_t i = 0; i < descriptors.size(); ++i) {
185 const input_method::InputMethodDescriptor& descriptor = descriptors[i];
186 const std::vector<std::string>& languages =
187 descriptor.language_codes();
188 for (size_t i = 0; i < languages.size(); ++i)
189 language_codes.insert(languages[i]);
192 // Map of display name -> {language code, native_display_name}.
193 // In theory, we should be able to create a map that is sorted by
194 // display names using ICU comparator, but doing it is hard, thus we'll
195 // use an auxiliary vector to achieve the same result.
196 typedef std::pair<std::string, string16> LanguagePair;
197 typedef std::map<string16, LanguagePair> LanguageMap;
198 LanguageMap language_map;
199 // The auxiliary vector mentioned above.
200 std::vector<string16> display_names;
202 // Build the list of display names, and build the language map.
203 for (std::set<std::string>::const_iterator iter = language_codes.begin();
204 iter != language_codes.end(); ++iter) {
205 // Exclude the language which is not in |base_langauge_codes| even it has
207 if (std::find(base_language_codes.begin(),
208 base_language_codes.end(),
209 *iter) == base_language_codes.end()) {
213 const string16 display_name =
214 l10n_util::GetDisplayNameForLocale(*iter, app_locale, true);
215 const string16 native_display_name =
216 l10n_util::GetDisplayNameForLocale(*iter, *iter, true);
218 display_names.push_back(display_name);
219 language_map[display_name] =
220 std::make_pair(*iter, native_display_name);
222 DCHECK_EQ(display_names.size(), language_map.size());
224 // Build the list of display names, and build the language map.
225 for (size_t i = 0; i < base_language_codes.size(); ++i) {
226 // Skip this language if it was already added.
227 if (language_codes.find(base_language_codes[i]) != language_codes.end())
230 // TODO(zork): Remove this blacklist when fonts are added to Chrome OS.
231 // see: crbug.com/240586
232 if (IsBlacklisted(base_language_codes[i]))
235 string16 display_name =
236 l10n_util::GetDisplayNameForLocale(
237 base_language_codes[i], app_locale, false);
238 string16 native_display_name =
239 l10n_util::GetDisplayNameForLocale(
240 base_language_codes[i], base_language_codes[i], false);
241 display_names.push_back(display_name);
242 language_map[display_name] =
243 std::make_pair(base_language_codes[i], native_display_name);
246 // Sort display names using locale specific sorter.
247 l10n_util::SortStrings16(app_locale, &display_names);
249 // Build the language list from the language map.
250 ListValue* language_list = new ListValue();
251 for (size_t i = 0; i < display_names.size(); ++i) {
252 // Sets the directionality of the display language name.
253 string16 display_name(display_names[i]);
254 bool markup_removal =
255 base::i18n::UnadjustStringForLocaleDirection(&display_name);
256 DCHECK(markup_removal);
257 bool has_rtl_chars = base::i18n::StringContainsStrongRTLChars(display_name);
258 std::string directionality = has_rtl_chars ? "rtl" : "ltr";
260 const LanguagePair& pair = language_map[display_names[i]];
261 DictionaryValue* dictionary = new DictionaryValue();
262 dictionary->SetString("code", pair.first);
263 dictionary->SetString("displayName", display_names[i]);
264 dictionary->SetString("textDirection", directionality);
265 dictionary->SetString("nativeDisplayName", pair.second);
266 language_list->Append(dictionary);
269 return language_list;
273 base::ListValue* CrosLanguageOptionsHandler::GetAcceptLanguageList(
274 const input_method::InputMethodDescriptors& descriptors) {
275 // Collect the language codes from the supported accept-languages.
276 const std::string app_locale = g_browser_process->GetApplicationLocale();
277 std::vector<std::string> accept_language_codes;
278 l10n_util::GetAcceptLanguagesForLocale(app_locale, &accept_language_codes);
279 return GetLanguageListInternal(descriptors, accept_language_codes);
283 base::ListValue* CrosLanguageOptionsHandler::GetUILanguageList(
284 const input_method::InputMethodDescriptors& descriptors) {
285 // Collect the language codes from the available locales.
286 return GetLanguageListInternal(descriptors, l10n_util::GetAvailableLocales());
290 CrosLanguageOptionsHandler::ConvertInputMethodDescriptosToIMEList(
291 const input_method::InputMethodDescriptors& descriptors) {
292 scoped_ptr<ListValue> ime_ids_list(new ListValue());
293 for (size_t i = 0; i < descriptors.size(); ++i) {
294 const input_method::InputMethodDescriptor& descriptor = descriptors[i];
295 scoped_ptr<DictionaryValue> dictionary(new DictionaryValue());
296 dictionary->SetString("id", descriptor.id());
297 dictionary->SetString("displayName", descriptor.name());
298 dictionary->SetString("optionsPage", descriptor.options_page_url().spec());
299 scoped_ptr<DictionaryValue> language_codes(new DictionaryValue());
300 for (size_t i = 0; i < descriptor.language_codes().size(); ++i)
301 language_codes->SetBoolean(descriptor.language_codes().at(i), true);
302 dictionary->Set("languageCodeSet", language_codes.release());
303 ime_ids_list->Append(dictionary.release());
305 return ime_ids_list.release();
308 string16 CrosLanguageOptionsHandler::GetProductName() {
309 return l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME);
312 void CrosLanguageOptionsHandler::SetApplicationLocale(
313 const std::string& language_code) {
314 Profile::FromWebUI(web_ui())->ChangeAppLocale(
315 language_code, Profile::APP_LOCALE_CHANGED_VIA_SETTINGS);
318 void CrosLanguageOptionsHandler::RestartCallback(const ListValue* args) {
319 content::RecordAction(UserMetricsAction("LanguageOptions_SignOut"));
320 chrome::AttemptUserExit();
323 void CrosLanguageOptionsHandler::InputMethodDisableCallback(
324 const ListValue* args) {
325 const std::string input_method_id = UTF16ToASCII(ExtractStringValue(args));
326 const std::string action = base::StringPrintf(
327 "LanguageOptions_DisableInputMethod_%s", input_method_id.c_str());
328 content::RecordComputedAction(action);
331 void CrosLanguageOptionsHandler::InputMethodEnableCallback(
332 const ListValue* args) {
333 const std::string input_method_id = UTF16ToASCII(ExtractStringValue(args));
334 const std::string action = base::StringPrintf(
335 "LanguageOptions_EnableInputMethod_%s", input_method_id.c_str());
336 content::RecordComputedAction(action);
339 void CrosLanguageOptionsHandler::InputMethodOptionsOpenCallback(
340 const ListValue* args) {
341 const std::string input_method_id = UTF16ToASCII(ExtractStringValue(args));
342 const std::string action = base::StringPrintf(
343 "InputMethodOptions_Open_%s", input_method_id.c_str());
344 content::RecordComputedAction(action);
347 void CrosLanguageOptionsHandler::OnInitialized() {
348 if (composition_extension_appended_ || !is_page_initialized_) {
349 // If an option page is not ready to call JavaScript, appending component
350 // extension IMEs will be done in InitializePage function later.
354 ComponentExtensionIMEManager* manager =
355 input_method::InputMethodManager::Get()
356 ->GetComponentExtensionIMEManager();
358 DCHECK(manager->IsInitialized());
359 scoped_ptr<ListValue> ime_list(
360 ConvertInputMethodDescriptosToIMEList(
361 manager->GetAllIMEAsInputMethodDescriptor()));
362 web_ui()->CallJavascriptFunction(
363 "options.LanguageOptions.onComponentManagerInitialized",
365 composition_extension_appended_ = true;
368 void CrosLanguageOptionsHandler::InitializePage() {
369 is_page_initialized_ = true;
370 if (composition_extension_appended_)
373 ComponentExtensionIMEManager* component_extension_manager =
374 input_method::InputMethodManager::Get()
375 ->GetComponentExtensionIMEManager();
376 if (!component_extension_manager->IsInitialized()) {
377 // If the component extension IME manager is not available yet, append the
378 // component extension list in |OnInitialized()|.
382 scoped_ptr<ListValue> ime_list(
383 ConvertInputMethodDescriptosToIMEList(
384 component_extension_manager->GetAllIMEAsInputMethodDescriptor()));
385 web_ui()->CallJavascriptFunction(
386 "options.LanguageOptions.onComponentManagerInitialized",
388 composition_extension_appended_ = true;
391 } // namespace options
392 } // namespace chromeos