- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / options / chromeos / cros_language_options_handler.cc
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
33 using content::UserMetricsAction;
34
35 namespace {
36 // TODO(zork): Remove this blacklist when fonts are added to Chrome OS.
37 // see: crbug.com/240586
38
39 bool IsBlacklisted(const std::string& language_code) {
40   return language_code == "si"; // Sinhala
41 }
42
43 } // namespace
44
45 namespace chromeos {
46 namespace options {
47
48 CrosLanguageOptionsHandler::CrosLanguageOptionsHandler()
49     : composition_extension_appended_(false),
50       is_page_initialized_(false) {
51   input_method::InputMethodManager::Get()->GetComponentExtensionIMEManager()->
52       AddObserver(this);
53 }
54
55 CrosLanguageOptionsHandler::~CrosLanguageOptionsHandler() {
56   input_method::InputMethodManager::Get()->GetComponentExtensionIMEManager()->
57       RemoveObserver(this);
58 }
59
60 void 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
128 void 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
145 ListValue* 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     dictionary->Set("languageCodeSet", languages);
169
170     input_method_list->Append(dictionary);
171   }
172
173   return input_method_list;
174 }
175
176 // static
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();
181
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]);
190   }
191
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;
201
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
206      // input methods.
207     if (std::find(base_language_codes.begin(),
208                   base_language_codes.end(),
209                   *iter) == base_language_codes.end()) {
210       continue;
211     }
212
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);
217
218     display_names.push_back(display_name);
219     language_map[display_name] =
220         std::make_pair(*iter, native_display_name);
221   }
222   DCHECK_EQ(display_names.size(), language_map.size());
223
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())
228       continue;
229
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]))
233       continue;
234
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);
244   }
245
246   // Sort display names using locale specific sorter.
247   l10n_util::SortStrings16(app_locale, &display_names);
248
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";
259
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);
267   }
268
269   return language_list;
270 }
271
272 // static
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);
280 }
281
282 // static
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());
287 }
288
289 base::ListValue*
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());
304   }
305   return ime_ids_list.release();
306 }
307
308 string16 CrosLanguageOptionsHandler::GetProductName() {
309   return l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME);
310 }
311
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);
316 }
317
318 void CrosLanguageOptionsHandler::RestartCallback(const ListValue* args) {
319   content::RecordAction(UserMetricsAction("LanguageOptions_SignOut"));
320   chrome::AttemptUserExit();
321 }
322
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);
329 }
330
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);
337 }
338
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);
345 }
346
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.
351     return;
352   }
353
354   ComponentExtensionIMEManager* manager =
355       input_method::InputMethodManager::Get()
356           ->GetComponentExtensionIMEManager();
357
358   DCHECK(manager->IsInitialized());
359   scoped_ptr<ListValue> ime_list(
360       ConvertInputMethodDescriptosToIMEList(
361           manager->GetAllIMEAsInputMethodDescriptor()));
362   web_ui()->CallJavascriptFunction(
363       "options.LanguageOptions.onComponentManagerInitialized",
364       *ime_list);
365   composition_extension_appended_ = true;
366 }
367
368 void CrosLanguageOptionsHandler::InitializePage() {
369   is_page_initialized_ = true;
370   if (composition_extension_appended_)
371     return;
372
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()|.
379     return;
380   }
381
382   scoped_ptr<ListValue> ime_list(
383       ConvertInputMethodDescriptosToIMEList(
384           component_extension_manager->GetAllIMEAsInputMethodDescriptor()));
385   web_ui()->CallJavascriptFunction(
386       "options.LanguageOptions.onComponentManagerInitialized",
387       *ime_list);
388   composition_extension_appended_ = true;
389 }
390
391 }  // namespace options
392 }  // namespace chromeos