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/chromeos/login/network_screen_handler.h" 6 7 #include <algorithm> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/command_line.h" 12 #include "base/logging.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/prefs/pref_registry_simple.h" 15 #include "base/prefs/pref_service.h" 16 #include "base/strings/stringprintf.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "base/sys_info.h" 19 #include "base/values.h" 20 #include "chrome/browser/browser_process.h" 21 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 22 #include "chrome/browser/chromeos/base/locale_util.h" 23 #include "chrome/browser/chromeos/customization_document.h" 24 #include "chrome/browser/chromeos/idle_detector.h" 25 #include "chrome/browser/chromeos/input_method/input_method_util.h" 26 #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h" 27 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h" 28 #include "chrome/browser/chromeos/system/input_device_settings.h" 29 #include "chrome/browser/chromeos/system/timezone_util.h" 30 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 31 #include "chrome/browser/ui/webui/options/chromeos/cros_language_options_handler.h" 32 #include "chrome/common/pref_names.h" 33 #include "chromeos/chromeos_switches.h" 34 #include "chromeos/ime/extension_ime_util.h" 35 #include "chromeos/network/network_handler.h" 36 #include "chromeos/network/network_state_handler.h" 37 #include "grit/chromium_strings.h" 38 #include "grit/generated_resources.h" 39 #include "ui/base/l10n/l10n_util.h" 40 #include "ui/gfx/rect.h" 41 #include "ui/views/layout/fill_layout.h" 42 #include "ui/views/widget/widget.h" 43 44 namespace { 45 46 const char kJsScreenPath[] = "login.NetworkScreen"; 47 48 // JS API callbacks names. 49 const char kJsApiNetworkOnExit[] = "networkOnExit"; 50 const char kJsApiNetworkOnLanguageChanged[] = "networkOnLanguageChanged"; 51 const char kJsApiNetworkOnInputMethodChanged[] = "networkOnInputMethodChanged"; 52 const char kJsApiNetworkOnTimezoneChanged[] = "networkOnTimezoneChanged"; 53 54 // Returns true if element was inserted. 55 bool InsertString(const std::string& str, std::set<std::string>& to) { 56 const std::pair<std::set<std::string>::iterator, bool> result = 57 to.insert(str); 58 return result.second; 59 } 60 61 void AddOptgroupOtherLayouts(base::ListValue* input_methods_list) { 62 base::DictionaryValue* optgroup = new base::DictionaryValue; 63 optgroup->SetString( 64 "optionGroupName", 65 l10n_util::GetStringUTF16(IDS_OOBE_OTHER_KEYBOARD_LAYOUTS)); 66 input_methods_list->Append(optgroup); 67 } 68 69 // For "UI Language" drop-down menu at OOBE screen we need to decide which 70 // entry to mark "selected". If user has just selected "requested_locale", 71 // but "loaded_locale" was actually loaded, we mark original user choice 72 // "selected" only if loaded_locale is a backup for "requested_locale". 73 std::string CalculateSelectedLanguage(const std::string& requested_locale, 74 const std::string& loaded_locale) { 75 76 std::string resolved_locale; 77 if (!l10n_util::CheckAndResolveLocale(requested_locale, &resolved_locale)) 78 return loaded_locale; 79 80 if (resolved_locale == loaded_locale) 81 return requested_locale; 82 83 return loaded_locale; 84 } 85 86 } // namespace 87 88 namespace chromeos { 89 90 // NetworkScreenHandler, public: ----------------------------------------------- 91 92 NetworkScreenHandler::NetworkScreenHandler(CoreOobeActor* core_oobe_actor) 93 : BaseScreenHandler(kJsScreenPath), 94 screen_(NULL), 95 core_oobe_actor_(core_oobe_actor), 96 is_continue_enabled_(false), 97 show_on_init_(false), 98 should_reinitialize_language_keyboard_list_(false), 99 weak_ptr_factory_(this) { 100 DCHECK(core_oobe_actor_); 101 102 input_method::InputMethodManager* manager = 103 input_method::InputMethodManager::Get(); 104 manager->AddObserver(this); 105 manager->GetComponentExtensionIMEManager()->AddObserver(this); 106 } 107 108 NetworkScreenHandler::~NetworkScreenHandler() { 109 if (screen_) 110 screen_->OnActorDestroyed(this); 111 112 input_method::InputMethodManager* manager = 113 input_method::InputMethodManager::Get(); 114 manager->RemoveObserver(this); 115 manager->GetComponentExtensionIMEManager()->RemoveObserver(this); 116 } 117 118 // NetworkScreenHandler, NetworkScreenActor implementation: -------------------- 119 120 void NetworkScreenHandler::SetDelegate(NetworkScreenActor::Delegate* screen) { 121 screen_ = screen; 122 } 123 124 void NetworkScreenHandler::PrepareToShow() { 125 } 126 127 void NetworkScreenHandler::Show() { 128 if (!page_is_ready()) { 129 show_on_init_ = true; 130 return; 131 } 132 133 // Here we should handle default locales, for which we do not have UI 134 // resources. This would load fallback, but properly show "selected" locale 135 // in the UI. 136 if (selected_language_code_.empty()) { 137 const StartupCustomizationDocument* startup_manifest = 138 StartupCustomizationDocument::GetInstance(); 139 HandleOnLanguageChanged(startup_manifest->initial_locale_default()); 140 } 141 142 PrefService* prefs = g_browser_process->local_state(); 143 if (prefs->GetBoolean(prefs::kFactoryResetRequested)) { 144 if (core_oobe_actor_) 145 core_oobe_actor_->ShowDeviceResetScreen(); 146 return; 147 } 148 149 // Make sure all our network technologies are turned on. On OOBE, the user 150 // should be able to select any of the available networks on the device. 151 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 152 handler->SetTechnologyEnabled(NetworkTypePattern::NonVirtual(), 153 true, 154 chromeos::network_handler::ErrorCallback()); 155 ShowScreen(OobeUI::kScreenOobeNetwork, NULL); 156 157 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableDemoMode)) 158 return; 159 if (base::SysInfo::IsRunningOnChromeOS()) { 160 std::string track; 161 // We're running on an actual device; if we cannot find our release track 162 // value or if the track contains "testimage", don't start demo mode. 163 if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &track) || 164 track.find("testimage") != std::string::npos) 165 return; 166 } 167 core_oobe_actor_->InitDemoModeDetection(); 168 } 169 170 void NetworkScreenHandler::Hide() { 171 } 172 173 void NetworkScreenHandler::ShowError(const base::string16& message) { 174 CallJS("showError", message); 175 } 176 177 void NetworkScreenHandler::ClearErrors() { 178 if (page_is_ready()) 179 core_oobe_actor_->ClearErrors(); 180 } 181 182 void NetworkScreenHandler::ShowConnectingStatus( 183 bool connecting, 184 const base::string16& network_id) { 185 } 186 187 void NetworkScreenHandler::EnableContinue(bool enabled) { 188 is_continue_enabled_ = enabled; 189 if (page_is_ready()) 190 CallJS("enableContinueButton", enabled); 191 } 192 193 // NetworkScreenHandler, BaseScreenHandler implementation: -------------------- 194 195 void NetworkScreenHandler::DeclareLocalizedValues( 196 LocalizedValuesBuilder* builder) { 197 if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation()) 198 builder->Add("networkScreenGreeting", IDS_REMORA_CONFIRM_MESSAGE); 199 else 200 builder->Add("networkScreenGreeting", IDS_WELCOME_SCREEN_GREETING); 201 202 builder->Add("networkScreenTitle", IDS_WELCOME_SCREEN_TITLE); 203 builder->Add("networkScreenAccessibleTitle", 204 IDS_NETWORK_SCREEN_ACCESSIBLE_TITLE); 205 builder->Add("selectLanguage", IDS_LANGUAGE_SELECTION_SELECT); 206 builder->Add("selectKeyboard", IDS_KEYBOARD_SELECTION_SELECT); 207 builder->Add("selectNetwork", IDS_NETWORK_SELECTION_SELECT); 208 builder->Add("selectTimezone", IDS_OPTIONS_SETTINGS_TIMEZONE_DESCRIPTION); 209 builder->Add("proxySettings", IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON); 210 builder->Add("continueButton", IDS_NETWORK_SELECTION_CONTINUE_BUTTON); 211 } 212 213 void NetworkScreenHandler::GetAdditionalParameters( 214 base::DictionaryValue* dict) { 215 dict->Set("languageList", GetLanguageList()); 216 dict->Set("inputMethodsList", GetInputMethods()); 217 dict->Set("timezoneList", GetTimezoneList()); 218 } 219 220 void NetworkScreenHandler::Initialize() { 221 EnableContinue(is_continue_enabled_); 222 if (show_on_init_) { 223 show_on_init_ = false; 224 Show(); 225 } 226 227 if (should_reinitialize_language_keyboard_list_) { 228 should_reinitialize_language_keyboard_list_ = false; 229 ReloadLocalizedContent(); 230 } 231 232 timezone_subscription_ = CrosSettings::Get()->AddSettingsObserver( 233 kSystemTimezone, 234 base::Bind(&NetworkScreenHandler::OnSystemTimezoneChanged, 235 base::Unretained(this))); 236 OnSystemTimezoneChanged(); 237 } 238 239 // NetworkScreenHandler, WebUIMessageHandler implementation: ------------------- 240 241 void NetworkScreenHandler::RegisterMessages() { 242 AddCallback(kJsApiNetworkOnExit, &NetworkScreenHandler::HandleOnExit); 243 AddCallback(kJsApiNetworkOnLanguageChanged, 244 &NetworkScreenHandler::HandleOnLanguageChanged); 245 AddCallback(kJsApiNetworkOnInputMethodChanged, 246 &NetworkScreenHandler::HandleOnInputMethodChanged); 247 AddCallback(kJsApiNetworkOnTimezoneChanged, 248 &NetworkScreenHandler::HandleOnTimezoneChanged); 249 } 250 251 252 // static 253 void NetworkScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) { 254 registry->RegisterInt64Pref(prefs::kTimeOnOobe, 0); 255 } 256 257 // NetworkScreenHandler, private: ---------------------------------------------- 258 259 void NetworkScreenHandler::HandleOnExit() { 260 core_oobe_actor_->StopDemoModeDetection(); 261 ClearErrors(); 262 if (screen_) 263 screen_->OnContinuePressed(); 264 } 265 266 struct NetworkScreenHandlerOnLanguageChangedCallbackData { 267 explicit NetworkScreenHandlerOnLanguageChangedCallbackData( 268 base::WeakPtr<NetworkScreenHandler>& handler) 269 : handler_(handler) {} 270 271 base::WeakPtr<NetworkScreenHandler> handler_; 272 273 // Block UI while resource bundle is being reloaded. 274 chromeos::InputEventsBlocker input_events_blocker; 275 }; 276 277 // static 278 void NetworkScreenHandler::OnLanguageChangedCallback( 279 scoped_ptr<NetworkScreenHandlerOnLanguageChangedCallbackData> context, 280 const std::string& requested_locale, 281 const std::string& loaded_locale, 282 const bool success) { 283 if (!context or !context->handler_) 284 return; 285 286 NetworkScreenHandler* const self = context->handler_.get(); 287 288 if (success) { 289 if (requested_locale == loaded_locale) { 290 self->selected_language_code_ = requested_locale; 291 } else { 292 self->selected_language_code_ = 293 CalculateSelectedLanguage(requested_locale, loaded_locale); 294 } 295 } else { 296 self->selected_language_code_ = loaded_locale; 297 } 298 299 self->ReloadLocalizedContent(); 300 301 // We still do not have device owner, so owner settings are not applied. 302 // But Guest session can be started before owner is created, so we need to 303 // save locale settings directly here. 304 g_browser_process->local_state()->SetString(prefs::kApplicationLocale, 305 self->selected_language_code_); 306 307 AccessibilityManager::Get()->OnLocaleChanged(); 308 } 309 310 void NetworkScreenHandler::HandleOnLanguageChanged(const std::string& locale) { 311 const std::string app_locale = g_browser_process->GetApplicationLocale(); 312 if (app_locale == locale) 313 return; 314 315 base::WeakPtr<NetworkScreenHandler> weak_self = 316 weak_ptr_factory_.GetWeakPtr(); 317 scoped_ptr<NetworkScreenHandlerOnLanguageChangedCallbackData> callback_data( 318 new NetworkScreenHandlerOnLanguageChangedCallbackData(weak_self)); 319 scoped_ptr<locale_util::SwitchLanguageCallback> callback( 320 new locale_util::SwitchLanguageCallback( 321 base::Bind(&NetworkScreenHandler::OnLanguageChangedCallback, 322 base::Passed(callback_data.Pass())))); 323 locale_util::SwitchLanguage(locale, 324 true /* enableLocaleKeyboardLayouts */, 325 true /* login_layouts_only */, 326 callback.Pass()); 327 } 328 329 void NetworkScreenHandler::HandleOnInputMethodChanged(const std::string& id) { 330 input_method::InputMethodManager::Get()->ChangeInputMethod(id); 331 } 332 333 void NetworkScreenHandler::HandleOnTimezoneChanged( 334 const std::string& timezone_id) { 335 std::string current_timezone_id; 336 CrosSettings::Get()->GetString(kSystemTimezone, ¤t_timezone_id); 337 if (current_timezone_id == timezone_id) 338 return; 339 340 CrosSettings::Get()->SetString(kSystemTimezone, timezone_id); 341 } 342 343 void NetworkScreenHandler::OnSystemTimezoneChanged() { 344 std::string current_timezone_id; 345 CrosSettings::Get()->GetString(kSystemTimezone, ¤t_timezone_id); 346 CallJS("setTimezone", current_timezone_id); 347 } 348 349 base::ListValue* NetworkScreenHandler::GetLanguageList() { 350 const std::string app_locale = g_browser_process->GetApplicationLocale(); 351 input_method::InputMethodManager* manager = 352 input_method::InputMethodManager::Get(); 353 ComponentExtensionIMEManager* comp_manager = 354 manager->GetComponentExtensionIMEManager(); 355 input_method::InputMethodDescriptors descriptors; 356 if (comp_manager->IsInitialized()) 357 descriptors = comp_manager->GetXkbIMEAsInputMethodDescriptor(); 358 base::ListValue* languages_list = 359 options::CrosLanguageOptionsHandler::GetUILanguageList(descriptors); 360 for (size_t i = 0; i < languages_list->GetSize(); ++i) { 361 base::DictionaryValue* language_info = NULL; 362 if (!languages_list->GetDictionary(i, &language_info)) 363 NOTREACHED(); 364 365 std::string value; 366 language_info->GetString("code", &value); 367 std::string display_name; 368 language_info->GetString("displayName", &display_name); 369 std::string native_name; 370 language_info->GetString("nativeDisplayName", &native_name); 371 372 // If it's option group divider, add field name. 373 if (value == options::kVendorOtherLanguagesListDivider) { 374 language_info->SetString( 375 "optionGroupName", 376 l10n_util::GetStringUTF16(IDS_OOBE_OTHER_LANGUAGES)); 377 } 378 if (display_name != native_name) { 379 display_name = base::StringPrintf("%s - %s", 380 display_name.c_str(), 381 native_name.c_str()); 382 } 383 384 language_info->SetString("value", value); 385 language_info->SetString("title", display_name); 386 if (selected_language_code_.empty()) { 387 if (value == app_locale) 388 language_info->SetBoolean("selected", true); 389 } else { 390 if (value == selected_language_code_) 391 language_info->SetBoolean("selected", true); 392 } 393 } 394 return languages_list; 395 } 396 397 base::DictionaryValue* CreateInputMethodsEntry( 398 const input_method::InputMethodDescriptor& method, 399 const std::string current_input_method_id) { 400 input_method::InputMethodUtil* util = 401 input_method::InputMethodManager::Get()->GetInputMethodUtil(); 402 const std::string& ime_id = method.id(); 403 scoped_ptr<base::DictionaryValue> input_method(new base::DictionaryValue); 404 input_method->SetString("value", ime_id); 405 input_method->SetString("title", util->GetInputMethodLongName(method)); 406 input_method->SetBoolean("selected", ime_id == current_input_method_id); 407 return input_method.release(); 408 } 409 410 void NetworkScreenHandler::OnImeComponentExtensionInitialized() { 411 // Refreshes the language and keyboard list once the component extension 412 // IMEs are initialized. 413 if (page_is_ready()) 414 ReloadLocalizedContent(); 415 else 416 should_reinitialize_language_keyboard_list_ = true; 417 } 418 419 void NetworkScreenHandler::InputMethodChanged( 420 input_method::InputMethodManager* manager, bool show_message) { 421 CallJS("setInputMethod", manager->GetCurrentInputMethod().id()); 422 } 423 424 void NetworkScreenHandler::ReloadLocalizedContent() { 425 base::DictionaryValue localized_strings; 426 static_cast<OobeUI*>(web_ui()->GetController()) 427 ->GetLocalizedStrings(&localized_strings); 428 core_oobe_actor_->ReloadContent(localized_strings); 429 430 // Buttons are recreated, updated "Continue" button state. 431 EnableContinue(is_continue_enabled_); 432 } 433 434 // static 435 base::ListValue* NetworkScreenHandler::GetInputMethods() { 436 base::ListValue* input_methods_list = new base::ListValue; 437 input_method::InputMethodManager* manager = 438 input_method::InputMethodManager::Get(); 439 input_method::InputMethodUtil* util = manager->GetInputMethodUtil(); 440 ComponentExtensionIMEManager* comp_manager = 441 manager->GetComponentExtensionIMEManager(); 442 if (!comp_manager->IsInitialized()) { 443 input_method::InputMethodDescriptor fallback = 444 util->GetFallbackInputMethodDescriptor(); 445 input_methods_list->Append( 446 CreateInputMethodsEntry(fallback, fallback.id())); 447 return input_methods_list; 448 } 449 450 const std::vector<std::string>& hardware_login_input_methods = 451 util->GetHardwareLoginInputMethodIds(); 452 manager->EnableLoginLayouts(g_browser_process->GetApplicationLocale(), 453 hardware_login_input_methods); 454 455 scoped_ptr<input_method::InputMethodDescriptors> input_methods( 456 manager->GetActiveInputMethods()); 457 const std::string current_input_method_id = 458 manager->GetCurrentInputMethod().id(); 459 std::set<std::string> input_methods_added; 460 461 for (std::vector<std::string>::const_iterator i = 462 hardware_login_input_methods.begin(); 463 i != hardware_login_input_methods.end(); 464 ++i) { 465 const input_method::InputMethodDescriptor* ime = 466 util->GetInputMethodDescriptorFromId(*i); 467 DCHECK(ime != NULL); 468 // Do not crash in case of misconfiguration. 469 if (ime != NULL) { 470 input_methods_added.insert(*i); 471 input_methods_list->Append( 472 CreateInputMethodsEntry(*ime, current_input_method_id)); 473 } 474 } 475 476 bool optgroup_added = false; 477 for (size_t i = 0; i < input_methods->size(); ++i) { 478 // Makes sure the id is in legacy xkb id format. 479 const std::string& ime_id = (*input_methods)[i].id(); 480 if (!InsertString(ime_id, input_methods_added)) 481 continue; 482 if (!optgroup_added) { 483 optgroup_added = true; 484 AddOptgroupOtherLayouts(input_methods_list); 485 } 486 input_methods_list->Append( 487 CreateInputMethodsEntry((*input_methods)[i], current_input_method_id)); 488 } 489 // "xkb:us::eng" should always be in the list of available layouts. 490 const std::string us_keyboard_id = 491 util->GetFallbackInputMethodDescriptor().id(); 492 if (input_methods_added.find(us_keyboard_id) == input_methods_added.end()) { 493 const input_method::InputMethodDescriptor* us_eng_descriptor = 494 util->GetInputMethodDescriptorFromId(us_keyboard_id); 495 DCHECK(us_eng_descriptor != NULL); 496 if (!optgroup_added) { 497 optgroup_added = true; 498 AddOptgroupOtherLayouts(input_methods_list); 499 } 500 input_methods_list->Append( 501 CreateInputMethodsEntry(*us_eng_descriptor, current_input_method_id)); 502 } 503 return input_methods_list; 504 } 505 506 // static 507 base::ListValue* NetworkScreenHandler::GetTimezoneList() { 508 std::string current_timezone_id; 509 CrosSettings::Get()->GetString(kSystemTimezone, ¤t_timezone_id); 510 511 scoped_ptr<base::ListValue> timezone_list(new base::ListValue); 512 scoped_ptr<base::ListValue> timezones = system::GetTimezoneList().Pass(); 513 for (size_t i = 0; i < timezones->GetSize(); ++i) { 514 const base::ListValue* timezone = NULL; 515 CHECK(timezones->GetList(i, &timezone)); 516 517 std::string timezone_id; 518 CHECK(timezone->GetString(0, &timezone_id)); 519 520 std::string timezone_name; 521 CHECK(timezone->GetString(1, &timezone_name)); 522 523 scoped_ptr<base::DictionaryValue> timezone_option( 524 new base::DictionaryValue); 525 timezone_option->SetString("value", timezone_id); 526 timezone_option->SetString("title", timezone_name); 527 timezone_option->SetBoolean("selected", timezone_id == current_timezone_id); 528 timezone_list->Append(timezone_option.release()); 529 } 530 531 return timezone_list.release(); 532 } 533 534 } // namespace chromeos 535