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/chromeos/login/login_display_host_impl.h" 6 7 #include <vector> 8 9 #include "ash/desktop_background/desktop_background_controller.h" 10 #include "ash/desktop_background/user_wallpaper_delegate.h" 11 #include "ash/shell.h" 12 #include "ash/shell_window_ids.h" 13 #include "ash/wm/solo_window_tracker.h" 14 #include "base/bind.h" 15 #include "base/command_line.h" 16 #include "base/debug/trace_event.h" 17 #include "base/logging.h" 18 #include "base/prefs/pref_service.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/threading/thread_restrictions.h" 21 #include "base/time/time.h" 22 #include "base/values.h" 23 #include "chrome/browser/browser_process.h" 24 #include "chrome/browser/browser_shutdown.h" 25 #include "chrome/browser/chrome_notification_types.h" 26 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" 27 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" 28 #include "chrome/browser/chromeos/base/locale_util.h" 29 #include "chrome/browser/chromeos/boot_times_loader.h" 30 #include "chrome/browser/chromeos/charger_replace/charger_replacement_dialog.h" 31 #include "chrome/browser/chromeos/customization_document.h" 32 #include "chrome/browser/chromeos/first_run/drive_first_run_controller.h" 33 #include "chrome/browser/chromeos/first_run/first_run.h" 34 #include "chrome/browser/chromeos/input_method/input_method_util.h" 35 #include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h" 36 #include "chrome/browser/chromeos/language_preferences.h" 37 #include "chrome/browser/chromeos/login/existing_user_controller.h" 38 #include "chrome/browser/chromeos/login/helper.h" 39 #include "chrome/browser/chromeos/login/input_events_blocker.h" 40 #include "chrome/browser/chromeos/login/keyboard_driven_oobe_key_handler.h" 41 #include "chrome/browser/chromeos/login/login_utils.h" 42 #include "chrome/browser/chromeos/login/login_wizard.h" 43 #include "chrome/browser/chromeos/login/oobe_display.h" 44 #include "chrome/browser/chromeos/login/startup_utils.h" 45 #include "chrome/browser/chromeos/login/user_manager.h" 46 #include "chrome/browser/chromeos/login/webui_login_display.h" 47 #include "chrome/browser/chromeos/login/webui_login_view.h" 48 #include "chrome/browser/chromeos/login/wizard_controller.h" 49 #include "chrome/browser/chromeos/mobile_config.h" 50 #include "chrome/browser/chromeos/policy/auto_enrollment_client.h" 51 #include "chrome/browser/chromeos/system/input_device_settings.h" 52 #include "chrome/browser/chromeos/ui/focus_ring_controller.h" 53 #include "chrome/browser/lifetime/application_lifetime.h" 54 #include "chrome/browser/policy/browser_policy_connector.h" 55 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 56 #include "chrome/common/chrome_constants.h" 57 #include "chrome/common/chrome_switches.h" 58 #include "chrome/common/pref_names.h" 59 #include "chromeos/audio/chromeos_sounds.h" 60 #include "chromeos/chromeos_constants.h" 61 #include "chromeos/chromeos_switches.h" 62 #include "chromeos/dbus/dbus_thread_manager.h" 63 #include "chromeos/dbus/session_manager_client.h" 64 #include "chromeos/ime/input_method_manager.h" 65 #include "chromeos/login/login_state.h" 66 #include "chromeos/settings/timezone_settings.h" 67 #include "content/public/browser/notification_service.h" 68 #include "content/public/browser/notification_types.h" 69 #include "content/public/browser/web_contents.h" 70 #include "content/public/browser/web_contents_view.h" 71 #include "content/public/browser/web_ui.h" 72 #include "grit/browser_resources.h" 73 #include "media/audio/sounds/sounds_manager.h" 74 #include "ui/aura/window.h" 75 #include "ui/base/resource/resource_bundle.h" 76 #include "ui/compositor/layer.h" 77 #include "ui/compositor/layer_animation_observer.h" 78 #include "ui/compositor/scoped_layer_animation_settings.h" 79 #include "ui/events/event_utils.h" 80 #include "ui/gfx/rect.h" 81 #include "ui/gfx/transform.h" 82 #include "ui/views/focus/focus_manager.h" 83 #include "ui/views/widget/widget.h" 84 #include "url/gurl.h" 85 86 namespace { 87 88 // Maximum delay for startup sound after 'loginPromptVisible' signal. 89 const int kStartupSoundMaxDelayMs = 2000; 90 91 // URL which corresponds to the login WebUI. 92 const char kLoginURL[] = "chrome://oobe/login"; 93 94 // URL which corresponds to the OOBE WebUI. 95 const char kOobeURL[] = "chrome://oobe/oobe"; 96 97 // URL which corresponds to the user adding WebUI. 98 const char kUserAddingURL[] = "chrome://oobe/user-adding"; 99 100 // URL which corresponds to the app launch splash WebUI. 101 const char kAppLaunchSplashURL[] = "chrome://oobe/app-launch-splash"; 102 103 // Duration of sign-in transition animation. 104 const int kLoginFadeoutTransitionDurationMs = 700; 105 106 // Number of times we try to reload OOBE/login WebUI if it crashes. 107 const int kCrashCountLimit = 5; 108 109 // Whether to enable tnitializing WebUI in hidden state (see 110 // |initialize_webui_hidden_|) by default. 111 const bool kHiddenWebUIInitializationDefault = true; 112 113 // Switch values that might be used to override WebUI init type. 114 const char kWebUIInitParallel[] = "parallel"; 115 const char kWebUIInitPostpone[] = "postpone"; 116 117 // The delay of triggering initialization of the device policy subsystem 118 // after the login screen is initialized. This makes sure that device policy 119 // network requests are made while the system is idle waiting for user input. 120 const int64 kPolicyServiceInitializationDelayMilliseconds = 100; 121 122 // Determines the hardware keyboard from the given locale code 123 // and the OEM layout information, and saves it to "Locale State". 124 // The information will be used in InputMethodUtil::GetHardwareInputMethodId(). 125 void DetermineAndSaveHardwareKeyboard(const std::string& locale, 126 const std::string& oem_layout) { 127 std::string layout; 128 if (!oem_layout.empty()) { 129 // If the OEM layout information is provided, use it. 130 layout = oem_layout; 131 } else { 132 chromeos::input_method::InputMethodManager* manager = 133 chromeos::input_method::InputMethodManager::Get(); 134 // Otherwise, determine the hardware keyboard from the locale. 135 std::vector<std::string> input_method_ids; 136 if (manager->GetInputMethodUtil()->GetInputMethodIdsFromLanguageCode( 137 locale, 138 chromeos::input_method::kKeyboardLayoutsOnly, 139 &input_method_ids)) { 140 // The output list |input_method_ids| is sorted by popularity, hence 141 // input_method_ids[0] now contains the most popular keyboard layout 142 // for the given locale. 143 layout = input_method_ids[0]; 144 } 145 } 146 147 if (!layout.empty()) { 148 PrefService* prefs = g_browser_process->local_state(); 149 prefs->SetString(prefs::kHardwareKeyboardLayout, layout); 150 // This asks the file thread to save the prefs (i.e. doesn't block). 151 // The latest values of Local State reside in memory so we can safely 152 // get the value of kHardwareKeyboardLayout even if the data is not 153 // yet saved to disk. 154 prefs->CommitPendingWrite(); 155 } 156 } 157 158 // A class to observe an implicit animation and invokes the callback after the 159 // animation is completed. 160 class AnimationObserver : public ui::ImplicitAnimationObserver { 161 public: 162 explicit AnimationObserver(const base::Closure& callback) 163 : callback_(callback) {} 164 virtual ~AnimationObserver() {} 165 166 private: 167 // ui::ImplicitAnimationObserver implementation: 168 virtual void OnImplicitAnimationsCompleted() OVERRIDE { 169 callback_.Run(); 170 delete this; 171 } 172 173 base::Closure callback_; 174 175 DISALLOW_COPY_AND_ASSIGN(AnimationObserver); 176 }; 177 178 // ShowLoginWizard is split into two parts. This function is sometimes called 179 // from ShowLoginWizard(), and sometimes from OnLanguageSwitchedCallback() 180 // (if locale was updated). 181 void ShowLoginWizardFinish( 182 const std::string& first_screen_name, 183 const chromeos::StartupCustomizationDocument* startup_manifest, 184 chromeos::LoginDisplayHost* display_host) { 185 scoped_ptr<DictionaryValue> params; 186 display_host->StartWizard(first_screen_name, params.Pass()); 187 188 chromeos::DBusThreadManager::Get()-> 189 GetSessionManagerClient()-> 190 EmitLoginPromptReady(); 191 TRACE_EVENT0("chromeos", "ShowLoginWizard::EmitLoginPromptReady"); 192 193 // Set initial timezone if specified by customization. 194 const std::string timezone_name = startup_manifest->initial_timezone(); 195 VLOG(1) << "Initial time zone: " << timezone_name; 196 // Apply locale customizations only once to preserve whatever locale 197 // user has changed to during OOBE. 198 if (!timezone_name.empty()) { 199 chromeos::system::TimezoneSettings::GetInstance()->SetTimezoneFromID( 200 UTF8ToUTF16(timezone_name)); 201 } 202 } 203 204 struct ShowLoginWizardSwitchLanguageCallbackData { 205 explicit ShowLoginWizardSwitchLanguageCallbackData( 206 const std::string& first_screen_name, 207 const chromeos::StartupCustomizationDocument* startup_manifest, 208 chromeos::LoginDisplayHost* display_host) 209 : first_screen_name(first_screen_name), 210 startup_manifest(startup_manifest), 211 display_host(display_host) {} 212 213 const std::string first_screen_name; 214 const chromeos::StartupCustomizationDocument* const startup_manifest; 215 chromeos::LoginDisplayHost* const display_host; 216 217 // lock UI while resource bundle is being reloaded. 218 chromeos::InputEventsBlocker events_blocker; 219 }; 220 221 void OnLanguageSwitchedCallback( 222 scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> self, 223 const std::string& locale, 224 const std::string& loaded_locale, 225 const bool success) { 226 if (!success) 227 LOG(WARNING) << "Locale could not be found for '" << locale << "'"; 228 229 ShowLoginWizardFinish( 230 self->first_screen_name, self->startup_manifest, self->display_host); 231 } 232 233 void EnableSystemSoundsForAccessibility() { 234 chromeos::AccessibilityManager::Get()->EnableSystemSounds(true); 235 } 236 237 } // namespace 238 239 namespace chromeos { 240 241 // static 242 LoginDisplayHost* LoginDisplayHostImpl::default_host_ = NULL; 243 244 // static 245 const int LoginDisplayHostImpl::kShowLoginWebUIid = 0x1111; 246 247 //////////////////////////////////////////////////////////////////////////////// 248 // LoginDisplayHostImpl, public 249 250 LoginDisplayHostImpl::LoginDisplayHostImpl(const gfx::Rect& background_bounds) 251 : background_bounds_(background_bounds), 252 pointer_factory_(this), 253 shutting_down_(false), 254 oobe_progress_bar_visible_(false), 255 session_starting_(false), 256 login_window_(NULL), 257 login_view_(NULL), 258 webui_login_display_(NULL), 259 is_showing_login_(false), 260 is_wallpaper_loaded_(false), 261 status_area_saved_visibility_(false), 262 crash_count_(0), 263 restore_path_(RESTORE_UNKNOWN), 264 auto_enrollment_check_done_(false), 265 finalize_animation_type_(ANIMATION_WORKSPACE), 266 animation_weak_ptr_factory_(this), 267 startup_sound_played_(false), 268 startup_sound_honors_spoken_feedback_(false) { 269 DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this); 270 CrasAudioHandler::Get()->AddAudioObserver(this); 271 272 // We need to listen to CLOSE_ALL_BROWSERS_REQUEST but not APP_TERMINATING 273 // because/ APP_TERMINATING will never be fired as long as this keeps 274 // ref-count. CLOSE_ALL_BROWSERS_REQUEST is safe here because there will be no 275 // browser instance that will block the shutdown. 276 registrar_.Add(this, 277 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, 278 content::NotificationService::AllSources()); 279 280 // NOTIFICATION_BROWSER_OPENED is issued after browser is created, but 281 // not shown yet. Lock window has to be closed at this point so that 282 // a browser window exists and the window can acquire input focus. 283 registrar_.Add(this, 284 chrome::NOTIFICATION_BROWSER_OPENED, 285 content::NotificationService::AllSources()); 286 287 // Login screen is moved to lock screen container when user logs in. 288 registrar_.Add(this, 289 chrome::NOTIFICATION_LOGIN_USER_CHANGED, 290 content::NotificationService::AllSources()); 291 292 DCHECK(default_host_ == NULL); 293 default_host_ = this; 294 295 // Make sure chrome won't exit while we are at login/oobe screen. 296 chrome::StartKeepAlive(); 297 298 bool is_registered = StartupUtils::IsDeviceRegistered(); 299 bool zero_delay_enabled = WizardController::IsZeroDelayEnabled(); 300 bool disable_boot_animation = CommandLine::ForCurrentProcess()->HasSwitch( 301 switches::kDisableBootAnimation); 302 bool disable_oobe_animation = CommandLine::ForCurrentProcess()->HasSwitch( 303 switches::kDisableOobeAnimation); 304 305 waiting_for_wallpaper_load_ = !zero_delay_enabled && 306 (is_registered || !disable_oobe_animation) && 307 (!is_registered || !disable_boot_animation); 308 309 // For slower hardware we have boot animation disabled so 310 // we'll be initializing WebUI hidden, waiting for user pods to load and then 311 // show WebUI at once. 312 waiting_for_user_pods_ = !zero_delay_enabled && !waiting_for_wallpaper_load_; 313 314 initialize_webui_hidden_ = 315 kHiddenWebUIInitializationDefault && !zero_delay_enabled; 316 317 // Check if WebUI init type is overriden. 318 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshWebUIInit)) { 319 const std::string override_type = 320 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 321 switches::kAshWebUIInit); 322 if (override_type == kWebUIInitParallel) 323 initialize_webui_hidden_ = true; 324 else if (override_type == kWebUIInitPostpone) 325 initialize_webui_hidden_ = false; 326 } 327 328 // Always postpone WebUI initialization on first boot, otherwise we miss 329 // initial animation. 330 if (!StartupUtils::IsOobeCompleted()) 331 initialize_webui_hidden_ = false; 332 333 // There is no wallpaper for KioskMode, don't initialize the webui hidden. 334 if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled()) 335 initialize_webui_hidden_ = false; 336 337 if (waiting_for_wallpaper_load_) { 338 registrar_.Add(this, 339 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED, 340 content::NotificationService::AllSources()); 341 } 342 343 // When we wait for WebUI to be initialized we wait for one of 344 // these notifications. 345 if ((waiting_for_user_pods_ || waiting_for_wallpaper_load_) && 346 initialize_webui_hidden_) { 347 registrar_.Add(this, 348 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 349 content::NotificationService::AllSources()); 350 registrar_.Add(this, 351 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN, 352 content::NotificationService::AllSources()); 353 } 354 LOG(WARNING) << "Login WebUI >> " 355 << "zero_delay: " << zero_delay_enabled 356 << " wait_for_wp_load_: " << waiting_for_wallpaper_load_ 357 << " wait_for_pods_: " << waiting_for_user_pods_ 358 << " init_webui_hidden_: " << initialize_webui_hidden_; 359 360 media::SoundsManager* manager = media::SoundsManager::Get(); 361 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 362 manager->Initialize(chromeos::SOUND_STARTUP, 363 bundle.GetRawDataResource(IDR_SOUND_STARTUP_WAV)); 364 } 365 366 LoginDisplayHostImpl::~LoginDisplayHostImpl() { 367 DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this); 368 CrasAudioHandler::Get()->RemoveAudioObserver(this); 369 370 views::FocusManager::set_arrow_key_traversal_enabled(false); 371 ResetLoginWindowAndView(); 372 373 // Let chrome process exit after login/oobe screen if needed. 374 chrome::EndKeepAlive(); 375 376 default_host_ = NULL; 377 // TODO(tengs): This should be refactored. See crbug.com/314934. 378 if (CommandLine::ForCurrentProcess()->HasSwitch( 379 switches::kEnableDriveOfflineFirstRun)) { 380 if (UserManager::Get()->IsCurrentUserNew()) { 381 // DriveOptInController will delete itself when finished. 382 (new DriveFirstRunController( 383 ProfileManager::GetActiveUserProfile()))->EnableOfflineMode(); 384 } 385 } 386 } 387 388 //////////////////////////////////////////////////////////////////////////////// 389 // LoginDisplayHostImpl, LoginDisplayHost implementation: 390 391 LoginDisplay* LoginDisplayHostImpl::CreateLoginDisplay( 392 LoginDisplay::Delegate* delegate) { 393 webui_login_display_ = new WebUILoginDisplay(delegate); 394 webui_login_display_->set_background_bounds(background_bounds()); 395 return webui_login_display_; 396 } 397 398 gfx::NativeWindow LoginDisplayHostImpl::GetNativeWindow() const { 399 return login_window_ ? login_window_->GetNativeWindow() : NULL; 400 } 401 402 WebUILoginView* LoginDisplayHostImpl::GetWebUILoginView() const { 403 return login_view_; 404 } 405 406 void LoginDisplayHostImpl::BeforeSessionStart() { 407 session_starting_ = true; 408 } 409 410 void LoginDisplayHostImpl::Finalize() { 411 DVLOG(1) << "Session starting"; 412 if (ash::Shell::HasInstance()) { 413 ash::Shell::GetInstance()-> 414 desktop_background_controller()->MoveDesktopToUnlockedContainer(); 415 } 416 if (wizard_controller_.get()) 417 wizard_controller_->OnSessionStart(); 418 419 switch (finalize_animation_type_) { 420 case ANIMATION_NONE: 421 ShutdownDisplayHost(false); 422 break; 423 case ANIMATION_WORKSPACE: 424 if (ash::Shell::HasInstance()) 425 ScheduleWorkspaceAnimation(); 426 427 ShutdownDisplayHost(false); 428 break; 429 case ANIMATION_FADE_OUT: 430 // Display host is deleted once animation is completed 431 // since sign in screen widget has to stay alive. 432 ScheduleFadeOutAnimation(); 433 break; 434 } 435 } 436 437 void LoginDisplayHostImpl::OnCompleteLogin() { 438 // Cancelling the |auto_enrollment_client_| now allows it to determine whether 439 // its protocol finished before login was complete. 440 if (auto_enrollment_client_.get()) 441 auto_enrollment_client_.release()->CancelAndDeleteSoon(); 442 } 443 444 void LoginDisplayHostImpl::OpenProxySettings() { 445 if (login_view_) 446 login_view_->OpenProxySettings(); 447 } 448 449 void LoginDisplayHostImpl::SetStatusAreaVisible(bool visible) { 450 if (initialize_webui_hidden_) 451 status_area_saved_visibility_ = visible; 452 else if (login_view_) 453 login_view_->SetStatusAreaVisible(visible); 454 } 455 456 void LoginDisplayHostImpl::CheckForAutoEnrollment() { 457 // This method is called when the controller determines that the 458 // auto-enrollment check can start. This happens either after the EULA is 459 // accepted, or right after a reboot if the EULA has already been accepted. 460 461 if (policy::AutoEnrollmentClient::IsDisabled()) { 462 VLOG(1) << "CheckForAutoEnrollment: auto-enrollment disabled"; 463 auto_enrollment_check_done_ = true; 464 return; 465 } 466 467 // Start by checking if the device has already been owned. 468 pointer_factory_.InvalidateWeakPtrs(); 469 DeviceSettingsService::Get()->GetOwnershipStatusAsync( 470 base::Bind(&LoginDisplayHostImpl::OnOwnershipStatusCheckDone, 471 pointer_factory_.GetWeakPtr())); 472 } 473 474 void LoginDisplayHostImpl::GetAutoEnrollmentCheckResult( 475 const GetAutoEnrollmentCheckResultCallback& callback) { 476 DCHECK(!callback.is_null()); 477 478 if (auto_enrollment_check_done_) { 479 callback.Run(auto_enrollment_client_ && 480 auto_enrollment_client_->should_auto_enroll()); 481 return; 482 } 483 484 get_auto_enrollment_result_callbacks_.push_back(callback); 485 } 486 487 void LoginDisplayHostImpl::StartWizard( 488 const std::string& first_screen_name, 489 scoped_ptr<DictionaryValue> screen_parameters) { 490 startup_sound_honors_spoken_feedback_ = false; 491 TryToPlayStartupSound(); 492 493 // Keep parameters to restore if renderer crashes. 494 restore_path_ = RESTORE_WIZARD; 495 wizard_first_screen_name_ = first_screen_name; 496 if (screen_parameters.get()) 497 wizard_screen_parameters_.reset(screen_parameters->DeepCopy()); 498 else 499 wizard_screen_parameters_.reset(); 500 is_showing_login_ = false; 501 502 if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) { 503 LOG(WARNING) << "Login WebUI >> wizard postponed"; 504 return; 505 } 506 LOG(WARNING) << "Login WebUI >> wizard"; 507 508 if (!login_window_) 509 LoadURL(GURL(kOobeURL)); 510 511 DVLOG(1) << "Starting wizard, first_screen_name: " << first_screen_name; 512 // Create and show the wizard. 513 // Note, dtor of the old WizardController should be called before ctor of the 514 // new one, because "default_controller()" is updated there. So pure "reset()" 515 // is done before new controller creation. 516 wizard_controller_.reset(); 517 wizard_controller_.reset(CreateWizardController()); 518 519 oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered(); 520 SetOobeProgressBarVisible(oobe_progress_bar_visible_); 521 wizard_controller_->Init(first_screen_name, screen_parameters.Pass()); 522 } 523 524 WizardController* LoginDisplayHostImpl::GetWizardController() { 525 return wizard_controller_.get(); 526 } 527 528 AppLaunchController* LoginDisplayHostImpl::GetAppLaunchController() { 529 return app_launch_controller_.get(); 530 } 531 532 void LoginDisplayHostImpl::StartUserAdding( 533 const base::Closure& completion_callback) { 534 restore_path_ = RESTORE_ADD_USER_INTO_SESSION; 535 completion_callback_ = completion_callback; 536 finalize_animation_type_ = ANIMATION_NONE; 537 LOG(WARNING) << "Login WebUI >> user adding"; 538 if (!login_window_) 539 LoadURL(GURL(kUserAddingURL)); 540 // We should emit this signal only at login screen (after reboot or sign out). 541 login_view_->set_should_emit_login_prompt_visible(false); 542 543 // Lock container can be transparent after lock screen animation. 544 aura::Window* lock_container = ash::Shell::GetContainer( 545 ash::Shell::GetPrimaryRootWindow(), 546 ash::internal::kShellWindowId_LockScreenContainersContainer); 547 lock_container->layer()->SetOpacity(1.0); 548 549 ash::Shell::GetInstance()-> 550 desktop_background_controller()->MoveDesktopToLockedContainer(); 551 552 sign_in_controller_.reset(); // Only one controller in a time. 553 sign_in_controller_.reset(new chromeos::ExistingUserController(this)); 554 SetOobeProgressBarVisible(oobe_progress_bar_visible_ = false); 555 SetStatusAreaVisible(true); 556 sign_in_controller_->Init( 557 chromeos::UserManager::Get()->GetUsersAdmittedForMultiProfile()); 558 CHECK(webui_login_display_); 559 GetOobeUI()->ShowSigninScreen(LoginScreenContext(), 560 webui_login_display_, 561 webui_login_display_); 562 } 563 564 void LoginDisplayHostImpl::StartSignInScreen( 565 const LoginScreenContext& context) { 566 startup_sound_honors_spoken_feedback_ = true; 567 TryToPlayStartupSound(); 568 569 restore_path_ = RESTORE_SIGN_IN; 570 is_showing_login_ = true; 571 finalize_animation_type_ = ANIMATION_WORKSPACE; 572 573 PrewarmAuthentication(); 574 575 if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) { 576 LOG(WARNING) << "Login WebUI >> sign in postponed"; 577 return; 578 } 579 LOG(WARNING) << "Login WebUI >> sign in"; 580 581 if (!login_window_) { 582 TRACE_EVENT_ASYNC_BEGIN0("ui", "ShowLoginWebUI", kShowLoginWebUIid); 583 TRACE_EVENT_ASYNC_STEP_INTO0( 584 "ui", "ShowLoginWebUI", kShowLoginWebUIid, "StartSignInScreen"); 585 BootTimesLoader::Get()->RecordCurrentStats("login-start-signin-screen"); 586 LoadURL(GURL(kLoginURL)); 587 } 588 589 DVLOG(1) << "Starting sign in screen"; 590 const chromeos::UserList& users = chromeos::UserManager::Get()->GetUsers(); 591 592 // Fix for users who updated device and thus never passed register screen. 593 // If we already have users, we assume that it is not a second part of 594 // OOBE. See http://crosbug.com/6289 595 if (!StartupUtils::IsDeviceRegistered() && !users.empty()) { 596 VLOG(1) << "Mark device registered because there are remembered users: " 597 << users.size(); 598 StartupUtils::MarkDeviceRegistered(); 599 } 600 601 sign_in_controller_.reset(); // Only one controller in a time. 602 sign_in_controller_.reset(new chromeos::ExistingUserController(this)); 603 oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered(); 604 SetOobeProgressBarVisible(oobe_progress_bar_visible_); 605 SetStatusAreaVisible(true); 606 sign_in_controller_->Init(users); 607 608 // We might be here after a reboot that was triggered after OOBE was complete, 609 // so check for auto-enrollment again. This might catch a cached decision from 610 // a previous oobe flow, or might start a new check with the server. 611 CheckForAutoEnrollment(); 612 613 // Initiate services customization manifest fetching. 614 ServicesCustomizationDocument::GetInstance()->StartFetching(); 615 616 // Initiate mobile config load. 617 MobileConfig::GetInstance(); 618 619 // Initiate device policy fetching. 620 g_browser_process->browser_policy_connector()->ScheduleServiceInitialization( 621 kPolicyServiceInitializationDelayMilliseconds); 622 623 CHECK(webui_login_display_); 624 GetOobeUI()->ShowSigninScreen(context, 625 webui_login_display_, 626 webui_login_display_); 627 if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled()) 628 SetStatusAreaVisible(false); 629 TRACE_EVENT_ASYNC_STEP_INTO0("ui", 630 "ShowLoginWebUI", 631 kShowLoginWebUIid, 632 "WaitForScreenStateInitialize"); 633 BootTimesLoader::Get()->RecordCurrentStats( 634 "login-wait-for-signin-state-initialize"); 635 } 636 637 void LoginDisplayHostImpl::ResumeSignInScreen() { 638 // We only get here after a previous call the StartSignInScreen. That sign-in 639 // was successful but was interrupted by an auto-enrollment execution; once 640 // auto-enrollment is complete we resume the normal login flow from here. 641 DVLOG(1) << "Resuming sign in screen"; 642 CHECK(sign_in_controller_.get()); 643 SetOobeProgressBarVisible(oobe_progress_bar_visible_); 644 SetStatusAreaVisible(true); 645 sign_in_controller_->ResumeLogin(); 646 } 647 648 649 void LoginDisplayHostImpl::OnPreferencesChanged() { 650 if (is_showing_login_) 651 webui_login_display_->OnPreferencesChanged(); 652 } 653 654 void LoginDisplayHostImpl::PrewarmAuthentication() { 655 auth_prewarmer_.reset(new AuthPrewarmer()); 656 auth_prewarmer_->PrewarmAuthentication( 657 base::Bind(&LoginDisplayHostImpl::OnAuthPrewarmDone, 658 pointer_factory_.GetWeakPtr())); 659 } 660 661 void LoginDisplayHostImpl::StartAppLaunch(const std::string& app_id) { 662 LOG(WARNING) << "Login WebUI >> start app launch."; 663 SetStatusAreaVisible(false); 664 finalize_animation_type_ = ANIMATION_FADE_OUT; 665 if (!login_window_) 666 LoadURL(GURL(kAppLaunchSplashURL)); 667 668 login_view_->set_should_emit_login_prompt_visible(false); 669 670 app_launch_controller_.reset(new AppLaunchController( 671 app_id, this, GetOobeUI())); 672 673 app_launch_controller_->StartAppLaunch(); 674 } 675 676 //////////////////////////////////////////////////////////////////////////////// 677 // LoginDisplayHostImpl, public 678 679 WizardController* LoginDisplayHostImpl::CreateWizardController() { 680 // TODO(altimofeev): ensure that WebUI is ready. 681 OobeDisplay* oobe_display = GetOobeUI(); 682 return new WizardController(this, oobe_display); 683 } 684 685 void LoginDisplayHostImpl::OnBrowserCreated() { 686 // Close lock window now so that the launched browser can receive focus. 687 ResetLoginWindowAndView(); 688 } 689 690 OobeUI* LoginDisplayHostImpl::GetOobeUI() const { 691 if (!login_view_) 692 return NULL; 693 return static_cast<OobeUI*>(login_view_->GetWebUI()->GetController()); 694 } 695 696 //////////////////////////////////////////////////////////////////////////////// 697 // LoginDisplayHostImpl, content:NotificationObserver implementation: 698 699 void LoginDisplayHostImpl::Observe( 700 int type, 701 const content::NotificationSource& source, 702 const content::NotificationDetails& details) { 703 if (chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED == type) { 704 LOG(WARNING) << "Login WebUI >> wp animation done"; 705 is_wallpaper_loaded_ = true; 706 ash::Shell::GetInstance()->user_wallpaper_delegate() 707 ->OnWallpaperBootAnimationFinished(); 708 if (waiting_for_wallpaper_load_) { 709 // StartWizard / StartSignInScreen could be called multiple times through 710 // the lifetime of host. 711 // Make sure that subsequent calls are not postponed. 712 waiting_for_wallpaper_load_ = false; 713 if (initialize_webui_hidden_) 714 ShowWebUI(); 715 else 716 StartPostponedWebUI(); 717 } 718 registrar_.Remove(this, 719 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED, 720 content::NotificationService::AllSources()); 721 } else if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type || 722 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN == type) { 723 LOG(WARNING) << "Login WebUI >> WEBUI_VISIBLE"; 724 if (waiting_for_user_pods_ && initialize_webui_hidden_) { 725 waiting_for_user_pods_ = false; 726 ShowWebUI(); 727 } else if (waiting_for_wallpaper_load_ && initialize_webui_hidden_) { 728 // Reduce time till login UI is shown - show it as soon as possible. 729 waiting_for_wallpaper_load_ = false; 730 ShowWebUI(); 731 } 732 registrar_.Remove(this, 733 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 734 content::NotificationService::AllSources()); 735 registrar_.Remove(this, 736 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN, 737 content::NotificationService::AllSources()); 738 } else if (type == chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST) { 739 ShutdownDisplayHost(true); 740 } else if (type == chrome::NOTIFICATION_BROWSER_OPENED && session_starting_) { 741 // Browsers created before session start (windows opened by extensions, for 742 // example) are ignored. 743 OnBrowserCreated(); 744 registrar_.Remove(this, 745 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST, 746 content::NotificationService::AllSources()); 747 registrar_.Remove(this, 748 chrome::NOTIFICATION_BROWSER_OPENED, 749 content::NotificationService::AllSources()); 750 } else if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED && 751 chromeos::UserManager::Get()->IsCurrentUserNew()) { 752 // For new user, move desktop to locker container so that windows created 753 // during the user image picker step are below it. 754 ash::Shell::GetInstance()-> 755 desktop_background_controller()->MoveDesktopToLockedContainer(); 756 registrar_.Remove(this, 757 chrome::NOTIFICATION_LOGIN_USER_CHANGED, 758 content::NotificationService::AllSources()); 759 } 760 } 761 762 //////////////////////////////////////////////////////////////////////////////// 763 // LoginDisplayHostImpl, WebContentsObserver implementation: 764 765 void LoginDisplayHostImpl::RenderProcessGone(base::TerminationStatus status) { 766 // Do not try to restore on shutdown 767 if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID) 768 return; 769 770 crash_count_++; 771 if (crash_count_ > kCrashCountLimit) 772 return; 773 774 if (status != base::TERMINATION_STATUS_NORMAL_TERMINATION) { 775 // Render with login screen crashed. Let's crash browser process to let 776 // session manager restart it properly. It is hard to reload the page 777 // and get to controlled state that is fully functional. 778 // If you see check, search for renderer crash for the same client. 779 LOG(FATAL) << "Renderer crash on login window"; 780 } 781 } 782 783 //////////////////////////////////////////////////////////////////////////////// 784 // LoginDisplayHostImpl, chromeos::SessionManagerClient::Observer 785 // implementation: 786 787 void LoginDisplayHostImpl::EmitLoginPromptVisibleCalled() { 788 OnLoginPromptVisible(); 789 } 790 791 void LoginDisplayHostImpl::OnActiveOutputNodeChanged() { 792 TryToPlayStartupSound(); 793 } 794 795 //////////////////////////////////////////////////////////////////////////////// 796 // LoginDisplayHostImpl, private 797 798 void LoginDisplayHostImpl::ShutdownDisplayHost(bool post_quit_task) { 799 if (shutting_down_) 800 return; 801 802 shutting_down_ = true; 803 registrar_.RemoveAll(); 804 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 805 if (post_quit_task) 806 base::MessageLoop::current()->Quit(); 807 808 if (!completion_callback_.is_null()) 809 completion_callback_.Run(); 810 } 811 812 void LoginDisplayHostImpl::ScheduleWorkspaceAnimation() { 813 if (ash::Shell::GetContainer( 814 ash::Shell::GetPrimaryRootWindow(), 815 ash::internal::kShellWindowId_DesktopBackgroundContainer)-> 816 children().empty()) { 817 // If there is no background window, don't perform any animation on the 818 // default and background layer because there is nothing behind it. 819 return; 820 } 821 822 if (!CommandLine::ForCurrentProcess()->HasSwitch( 823 switches::kDisableLoginAnimations)) 824 ash::Shell::GetInstance()->DoInitialWorkspaceAnimation(); 825 } 826 827 void LoginDisplayHostImpl::ScheduleFadeOutAnimation() { 828 ui::Layer* layer = login_window_->GetLayer(); 829 ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); 830 animation.AddObserver(new AnimationObserver( 831 base::Bind(&LoginDisplayHostImpl::ShutdownDisplayHost, 832 animation_weak_ptr_factory_.GetWeakPtr(), 833 false))); 834 layer->SetOpacity(0); 835 } 836 837 void LoginDisplayHostImpl::OnOwnershipStatusCheckDone( 838 DeviceSettingsService::OwnershipStatus status) { 839 if (status != DeviceSettingsService::OWNERSHIP_NONE) { 840 // The device is already owned. No need for auto-enrollment checks. 841 VLOG(1) << "CheckForAutoEnrollment: device already owned"; 842 auto_enrollment_check_done_ = true; 843 NotifyAutoEnrollmentCheckResult(false); 844 return; 845 } 846 847 // Kick off the auto-enrollment client. 848 if (auto_enrollment_client_.get()) { 849 // They client might have been started after the EULA screen, but we made 850 // it to the login screen before it finished. In that case let the current 851 // client proceed. 852 // 853 // CheckForAutoEnrollment() is also called when we reach the sign-in screen, 854 // because that's what happens after an auto-update. 855 VLOG(1) << "CheckForAutoEnrollment: client already started"; 856 857 // If the client already started and already finished too, pass the decision 858 // to the |sign_in_controller_| now. 859 if (auto_enrollment_client_->should_auto_enroll()) 860 ForceAutoEnrollment(); 861 } else { 862 VLOG(1) << "CheckForAutoEnrollment: starting auto-enrollment client"; 863 auto_enrollment_client_.reset(policy::AutoEnrollmentClient::Create( 864 base::Bind(&LoginDisplayHostImpl::OnAutoEnrollmentClientDone, 865 base::Unretained(this)))); 866 auto_enrollment_client_->Start(); 867 } 868 } 869 870 void LoginDisplayHostImpl::OnAutoEnrollmentClientDone() { 871 bool auto_enroll = auto_enrollment_client_->should_auto_enroll(); 872 VLOG(1) << "OnAutoEnrollmentClientDone, decision is " << auto_enroll; 873 874 if (auto_enroll) 875 ForceAutoEnrollment(); 876 877 auto_enrollment_check_done_ = true; 878 NotifyAutoEnrollmentCheckResult(auto_enroll); 879 } 880 881 void LoginDisplayHostImpl::ForceAutoEnrollment() { 882 if (sign_in_controller_.get()) 883 sign_in_controller_->DoAutoEnrollment(); 884 } 885 886 void LoginDisplayHostImpl::LoadURL(const GURL& url) { 887 InitLoginWindowAndView(); 888 // Subscribe to crash events. 889 content::WebContentsObserver::Observe(login_view_->GetWebContents()); 890 login_view_->LoadURL(url); 891 892 // LoadURL could be called after the spring charger dialog shows, and 893 // take away the focus from it. Set the focus back to the charger dialog 894 // if it is visible. 895 // See crbug.com/328538. 896 ChargerReplacementDialog::SetFocusOnChargerDialogIfVisible(); 897 } 898 899 void LoginDisplayHostImpl::ShowWebUI() { 900 if (!login_window_ || !login_view_) { 901 NOTREACHED(); 902 return; 903 } 904 LOG(WARNING) << "Login WebUI >> Show already initialized UI"; 905 login_window_->Show(); 906 login_view_->GetWebContents()->GetView()->Focus(); 907 login_view_->SetStatusAreaVisible(status_area_saved_visibility_); 908 login_view_->OnPostponedShow(); 909 910 // Login window could be shown after the spring charger dialog shows, and 911 // take away the focus from it. Set the focus back to the charger dialog 912 // if it is visible. 913 // See crbug.com/328538. 914 ChargerReplacementDialog::SetFocusOnChargerDialogIfVisible(); 915 916 // We should reset this flag to allow changing of status area visibility. 917 initialize_webui_hidden_ = false; 918 } 919 920 void LoginDisplayHostImpl::StartPostponedWebUI() { 921 if (!is_wallpaper_loaded_) { 922 NOTREACHED(); 923 return; 924 } 925 LOG(WARNING) << "Login WebUI >> Init postponed WebUI"; 926 927 // Wallpaper has finished loading before StartWizard/StartSignInScreen has 928 // been called. In general this should not happen. 929 // Let go through normal code path when one of those will be called. 930 if (restore_path_ == RESTORE_UNKNOWN) { 931 NOTREACHED(); 932 return; 933 } 934 935 switch (restore_path_) { 936 case RESTORE_WIZARD: 937 StartWizard(wizard_first_screen_name_, 938 wizard_screen_parameters_.Pass()); 939 break; 940 case RESTORE_SIGN_IN: 941 StartSignInScreen(LoginScreenContext()); 942 break; 943 case RESTORE_ADD_USER_INTO_SESSION: 944 StartUserAdding(completion_callback_); 945 break; 946 default: 947 NOTREACHED(); 948 break; 949 } 950 } 951 952 void LoginDisplayHostImpl::InitLoginWindowAndView() { 953 if (login_window_) 954 return; 955 ash::SoloWindowTracker::SetSoloHeaderEnabled(false); 956 957 if (system::keyboard_settings::ForceKeyboardDrivenUINavigation()) { 958 views::FocusManager::set_arrow_key_traversal_enabled(true); 959 960 focus_ring_controller_.reset(new FocusRingController); 961 focus_ring_controller_->SetVisible(true); 962 963 keyboard_driven_oobe_key_handler_.reset(new KeyboardDrivenOobeKeyHandler); 964 } 965 966 views::Widget::InitParams params( 967 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); 968 params.bounds = background_bounds(); 969 params.show_state = ui::SHOW_STATE_FULLSCREEN; 970 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 971 params.parent = 972 ash::Shell::GetContainer( 973 ash::Shell::GetPrimaryRootWindow(), 974 ash::internal::kShellWindowId_LockScreenContainer); 975 976 login_window_ = new views::Widget; 977 login_window_->Init(params); 978 login_view_ = new WebUILoginView(); 979 login_view_->Init(); 980 if (login_view_->webui_visible()) 981 OnLoginPromptVisible(); 982 983 views::corewm::SetWindowVisibilityAnimationDuration( 984 login_window_->GetNativeView(), 985 base::TimeDelta::FromMilliseconds(kLoginFadeoutTransitionDurationMs)); 986 views::corewm::SetWindowVisibilityAnimationTransition( 987 login_window_->GetNativeView(), 988 views::corewm::ANIMATE_HIDE); 989 990 login_window_->SetContentsView(login_view_); 991 992 // If WebUI is initialized in hidden state, show it only if we're no 993 // longer waiting for wallpaper animation/user images loading. Otherwise, 994 // always show it. 995 if (!initialize_webui_hidden_ || 996 (!waiting_for_wallpaper_load_ && !waiting_for_user_pods_)) { 997 LOG(WARNING) << "Login WebUI >> show login wnd on create"; 998 login_window_->Show(); 999 } else { 1000 LOG(WARNING) << "Login WebUI >> login wnd is hidden on create"; 1001 login_view_->set_is_hidden(true); 1002 } 1003 login_window_->GetNativeView()->SetName("WebUILoginView"); 1004 } 1005 1006 void LoginDisplayHostImpl::ResetLoginWindowAndView() { 1007 if (!login_window_) 1008 return; 1009 ash::SoloWindowTracker::SetSoloHeaderEnabled(true); 1010 login_window_->Close(); 1011 login_window_ = NULL; 1012 login_view_ = NULL; 1013 } 1014 1015 void LoginDisplayHostImpl::OnAuthPrewarmDone() { 1016 auth_prewarmer_.reset(); 1017 } 1018 1019 void LoginDisplayHostImpl::SetOobeProgressBarVisible(bool visible) { 1020 GetOobeUI()->ShowOobeUI(visible); 1021 } 1022 1023 void LoginDisplayHostImpl::NotifyAutoEnrollmentCheckResult( 1024 bool should_auto_enroll) { 1025 std::vector<GetAutoEnrollmentCheckResultCallback> callbacks; 1026 callbacks.swap(get_auto_enrollment_result_callbacks_); 1027 for (size_t i = 0; i < callbacks.size(); ++i) 1028 callbacks[i].Run(should_auto_enroll); 1029 } 1030 1031 void LoginDisplayHostImpl::TryToPlayStartupSound() { 1032 if (startup_sound_played_ || login_prompt_visible_time_.is_null() || 1033 !CrasAudioHandler::Get()->GetActiveOutputNode()) { 1034 return; 1035 } 1036 1037 // Don't play startup sound if login prompt is already visible for a 1038 // long time. 1039 if (base::TimeTicks::Now() - login_prompt_visible_time_ > 1040 base::TimeDelta::FromMilliseconds(kStartupSoundMaxDelayMs)) { 1041 return; 1042 } 1043 1044 AccessibilityManager* accessibility_manager = AccessibilityManager::Get(); 1045 media::SoundsManager* sounds_manager = media::SoundsManager::Get(); 1046 startup_sound_played_ = true; 1047 if (!startup_sound_honors_spoken_feedback_ || 1048 accessibility_manager->IsSpokenFeedbackEnabled()) { 1049 sounds_manager->Play(SOUND_STARTUP); 1050 base::MessageLoop::current()->PostDelayedTask( 1051 FROM_HERE, 1052 base::Bind(&EnableSystemSoundsForAccessibility), 1053 sounds_manager->GetDuration(SOUND_STARTUP)); 1054 } else { 1055 accessibility_manager->EnableSystemSounds(true); 1056 } 1057 } 1058 1059 void LoginDisplayHostImpl::OnLoginPromptVisible() { 1060 if (!login_prompt_visible_time_.is_null()) 1061 return; 1062 login_prompt_visible_time_ = base::TimeTicks::Now(); 1063 TryToPlayStartupSound(); 1064 } 1065 1066 //////////////////////////////////////////////////////////////////////////////// 1067 // external 1068 1069 // Declared in login_wizard.h so that others don't need to depend on our .h. 1070 // TODO(nkostylev): Split this into a smaller functions. 1071 void ShowLoginWizard(const std::string& first_screen_name) { 1072 if (browser_shutdown::IsTryingToQuit()) 1073 return; 1074 1075 VLOG(1) << "Showing OOBE screen: " << first_screen_name; 1076 1077 chromeos::input_method::InputMethodManager* manager = 1078 chromeos::input_method::InputMethodManager::Get(); 1079 1080 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty 1081 // and US dvorak keyboard layouts. 1082 if (g_browser_process && g_browser_process->local_state()) { 1083 manager->SetInputMethodDefault(); 1084 1085 PrefService* prefs = g_browser_process->local_state(); 1086 // Apply owner preferences for tap-to-click and mouse buttons swap for 1087 // login screen. 1088 system::mouse_settings::SetPrimaryButtonRight( 1089 prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight)); 1090 system::touchpad_settings::SetTapToClick( 1091 prefs->GetBoolean(prefs::kOwnerTapToClickEnabled)); 1092 } 1093 1094 ui::SetNaturalScroll(CommandLine::ForCurrentProcess()->HasSwitch( 1095 switches::kNaturalScrollDefault)); 1096 1097 gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size())); 1098 1099 // Check whether we need to execute OOBE process. 1100 bool oobe_complete = chromeos::StartupUtils::IsOobeCompleted(); 1101 if (!oobe_complete) { 1102 LoginState::Get()->SetLoggedInState( 1103 LoginState::LOGGED_IN_OOBE, LoginState::LOGGED_IN_USER_NONE); 1104 } else { 1105 LoginState::Get()->SetLoggedInState( 1106 LoginState::LOGGED_IN_NONE, LoginState::LOGGED_IN_USER_NONE); 1107 } 1108 1109 LoginDisplayHost* display_host = new LoginDisplayHostImpl(screen_bounds); 1110 1111 bool show_app_launch_splash_screen = (first_screen_name == 1112 chromeos::WizardController::kAppLaunchSplashScreenName); 1113 if (show_app_launch_splash_screen) { 1114 const std::string& auto_launch_app_id = 1115 chromeos::KioskAppManager::Get()->GetAutoLaunchApp(); 1116 display_host->StartAppLaunch(auto_launch_app_id); 1117 return; 1118 } 1119 1120 bool should_show_enrollment_screen = 1121 first_screen_name.empty() && oobe_complete && 1122 chromeos::WizardController::ShouldAutoStartEnrollment() && 1123 !g_browser_process->browser_policy_connector()->IsEnterpriseManaged(); 1124 if (should_show_enrollment_screen) { 1125 // Shows networks screen instead of enrollment screen to resume the 1126 // interrupted auto start enrollment flow because enrollment screen does 1127 // not handle flaky network. See http://crbug.com/332572 1128 display_host->StartWizard(chromeos::WizardController::kNetworkScreenName, 1129 scoped_ptr<DictionaryValue>()); 1130 return; 1131 } 1132 1133 bool show_login_screen = 1134 (first_screen_name.empty() && oobe_complete) || 1135 first_screen_name == chromeos::WizardController::kLoginScreenName; 1136 1137 if (show_login_screen) { 1138 // R11 > R12 migration fix. See http://crosbug.com/p/4898. 1139 // If user has manually changed locale during R11 OOBE, locale will be set. 1140 // On R12 > R12|R13 etc. this fix won't get activated since 1141 // OOBE process has set kApplicationLocale to non-default value. 1142 PrefService* prefs = g_browser_process->local_state(); 1143 if (!prefs->HasPrefPath(prefs::kApplicationLocale)) { 1144 std::string locale = chromeos::StartupUtils::GetInitialLocale(); 1145 prefs->SetString(prefs::kApplicationLocale, locale); 1146 manager->EnableLayouts( 1147 locale, 1148 manager->GetInputMethodUtil()->GetHardwareInputMethodId()); 1149 base::ThreadRestrictions::ScopedAllowIO allow_io; 1150 const std::string loaded_locale = 1151 ResourceBundle::GetSharedInstance().ReloadLocaleResources(locale); 1152 g_browser_process->SetApplicationLocale(loaded_locale); 1153 } 1154 display_host->StartSignInScreen(LoginScreenContext()); 1155 return; 1156 } 1157 1158 // Load startup manifest. 1159 const chromeos::StartupCustomizationDocument* startup_manifest = 1160 chromeos::StartupCustomizationDocument::GetInstance(); 1161 1162 // Switch to initial locale if specified by customization 1163 // and has not been set yet. We cannot call 1164 // chromeos::LanguageSwitchMenu::SwitchLanguage here before 1165 // EmitLoginPromptReady. 1166 PrefService* prefs = g_browser_process->local_state(); 1167 const std::string current_locale = 1168 prefs->GetString(prefs::kApplicationLocale); 1169 VLOG(1) << "Current locale: " << current_locale; 1170 std::string locale = startup_manifest->initial_locale(); 1171 1172 if (!current_locale.empty() || locale.empty()) { 1173 ShowLoginWizardFinish(first_screen_name, startup_manifest, display_host); 1174 return; 1175 } 1176 1177 std::string layout = startup_manifest->keyboard_layout(); 1178 VLOG(1) << "Initial locale: " << locale << "keyboard layout " << layout; 1179 1180 // Save initial locale from VPD/customization manifest as current 1181 // Chrome locale. Otherwise it will be lost if Chrome restarts. 1182 // Don't need to schedule pref save because setting initial local 1183 // will enforce preference saving. 1184 prefs->SetString(prefs::kApplicationLocale, locale); 1185 chromeos::StartupUtils::SetInitialLocale(locale); 1186 1187 // Determine keyboard layout from OEM customization (if provided) or 1188 // initial locale and save it in preferences. 1189 DetermineAndSaveHardwareKeyboard(locale, layout); 1190 1191 // Then, enable the hardware keyboard. 1192 manager->EnableLayouts( 1193 locale, manager->GetInputMethodUtil()->GetHardwareInputMethodId()); 1194 1195 scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> data( 1196 new ShowLoginWizardSwitchLanguageCallbackData( 1197 first_screen_name, startup_manifest, display_host)); 1198 1199 scoped_ptr<locale_util::SwitchLanguageCallback> callback( 1200 new locale_util::SwitchLanguageCallback( 1201 base::Bind(&OnLanguageSwitchedCallback, base::Passed(data.Pass())))); 1202 1203 // Do not load locale keyboards here. 1204 locale_util::SwitchLanguage(locale, false, callback.Pass()); 1205 } 1206 1207 } // namespace chromeos 1208