1 // Copyright 2014 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/chromeos/login/ui/login_display_host_impl.h"
9 #include "ash/audio/sounds.h"
10 #include "ash/desktop_background/desktop_background_controller.h"
11 #include "ash/desktop_background/user_wallpaper_delegate.h"
12 #include "ash/shell.h"
13 #include "ash/shell_window_ids.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/string_split.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/browser_shutdown.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
28 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
29 #include "chrome/browser/chromeos/base/locale_util.h"
30 #include "chrome/browser/chromeos/boot_times_loader.h"
31 #include "chrome/browser/chromeos/charger_replace/charger_replacement_dialog.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/demo_mode/demo_app_launcher.h"
38 #include "chrome/browser/chromeos/login/existing_user_controller.h"
39 #include "chrome/browser/chromeos/login/helper.h"
40 #include "chrome/browser/chromeos/login/login_utils.h"
41 #include "chrome/browser/chromeos/login/login_wizard.h"
42 #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
43 #include "chrome/browser/chromeos/login/startup_utils.h"
44 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
45 #include "chrome/browser/chromeos/login/ui/keyboard_driven_oobe_key_handler.h"
46 #include "chrome/browser/chromeos/login/ui/oobe_display.h"
47 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
48 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
49 #include "chrome/browser/chromeos/login/users/user_manager.h"
50 #include "chrome/browser/chromeos/login/wizard_controller.h"
51 #include "chrome/browser/chromeos/mobile_config.h"
52 #include "chrome/browser/chromeos/net/delay_network_call.h"
53 #include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
54 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
55 #include "chrome/browser/chromeos/system/input_device_settings.h"
56 #include "chrome/browser/chromeos/ui/focus_ring_controller.h"
57 #include "chrome/browser/lifetime/application_lifetime.h"
58 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
59 #include "chrome/common/chrome_constants.h"
60 #include "chrome/common/chrome_switches.h"
61 #include "chrome/common/pref_names.h"
62 #include "chromeos/audio/chromeos_sounds.h"
63 #include "chromeos/chromeos_constants.h"
64 #include "chromeos/chromeos_switches.h"
65 #include "chromeos/dbus/dbus_thread_manager.h"
66 #include "chromeos/dbus/session_manager_client.h"
67 #include "chromeos/ime/extension_ime_util.h"
68 #include "chromeos/ime/input_method_manager.h"
69 #include "chromeos/login/login_state.h"
70 #include "chromeos/settings/timezone_settings.h"
71 #include "content/public/browser/notification_service.h"
72 #include "content/public/browser/notification_types.h"
73 #include "content/public/browser/render_frame_host.h"
74 #include "content/public/browser/web_contents.h"
75 #include "content/public/browser/web_ui.h"
76 #include "grit/browser_resources.h"
77 #include "media/audio/sounds/sounds_manager.h"
78 #include "ui/aura/window.h"
79 #include "ui/base/resource/resource_bundle.h"
80 #include "ui/base/ui_base_switches_util.h"
81 #include "ui/compositor/layer.h"
82 #include "ui/compositor/layer_animation_observer.h"
83 #include "ui/compositor/scoped_layer_animation_settings.h"
84 #include "ui/events/event_utils.h"
85 #include "ui/gfx/display.h"
86 #include "ui/gfx/rect.h"
87 #include "ui/gfx/screen.h"
88 #include "ui/gfx/size.h"
89 #include "ui/gfx/transform.h"
90 #include "ui/keyboard/keyboard_controller.h"
91 #include "ui/keyboard/keyboard_util.h"
92 #include "ui/views/focus/focus_manager.h"
93 #include "ui/views/widget/widget.h"
94 #include "ui/views/widget/widget_delegate.h"
95 #include "ui/wm/core/window_animations.h"
100 // Maximum delay for startup sound after 'loginPromptVisible' signal.
101 const int kStartupSoundMaxDelayMs = 2000;
103 // URL which corresponds to the login WebUI.
104 const char kLoginURL[] = "chrome://oobe/login";
106 // URL which corresponds to the OOBE WebUI.
107 const char kOobeURL[] = "chrome://oobe/oobe";
109 // URL which corresponds to the user adding WebUI.
110 const char kUserAddingURL[] = "chrome://oobe/user-adding";
112 // URL which corresponds to the app launch splash WebUI.
113 const char kAppLaunchSplashURL[] = "chrome://oobe/app-launch-splash";
115 // Duration of sign-in transition animation.
116 const int kLoginFadeoutTransitionDurationMs = 700;
118 // Number of times we try to reload OOBE/login WebUI if it crashes.
119 const int kCrashCountLimit = 5;
121 // Whether to enable tnitializing WebUI in hidden state (see
122 // |initialize_webui_hidden_|) by default.
123 const bool kHiddenWebUIInitializationDefault = true;
125 // Switch values that might be used to override WebUI init type.
126 const char kWebUIInitParallel[] = "parallel";
127 const char kWebUIInitPostpone[] = "postpone";
129 // The delay of triggering initialization of the device policy subsystem
130 // after the login screen is initialized. This makes sure that device policy
131 // network requests are made while the system is idle waiting for user input.
132 const int64 kPolicyServiceInitializationDelayMilliseconds = 100;
134 // A class to observe an implicit animation and invokes the callback after the
135 // animation is completed.
136 class AnimationObserver : public ui::ImplicitAnimationObserver {
138 explicit AnimationObserver(const base::Closure& callback)
139 : callback_(callback) {}
140 virtual ~AnimationObserver() {}
143 // ui::ImplicitAnimationObserver implementation:
144 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
149 base::Closure callback_;
151 DISALLOW_COPY_AND_ASSIGN(AnimationObserver);
154 // ShowLoginWizard is split into two parts. This function is sometimes called
155 // from ShowLoginWizard(), and sometimes from OnLanguageSwitchedCallback()
156 // (if locale was updated).
157 void ShowLoginWizardFinish(
158 const std::string& first_screen_name,
159 const chromeos::StartupCustomizationDocument* startup_manifest,
160 chromeos::LoginDisplayHost* display_host) {
161 TRACE_EVENT0("chromeos", "ShowLoginWizard::ShowLoginWizardFinish");
163 scoped_ptr<base::DictionaryValue> params;
164 display_host->StartWizard(first_screen_name, params.Pass());
166 // Set initial timezone if specified by customization.
167 const std::string timezone_name = startup_manifest->initial_timezone();
168 VLOG(1) << "Initial time zone: " << timezone_name;
169 // Apply locale customizations only once to preserve whatever locale
170 // user has changed to during OOBE.
171 if (!timezone_name.empty()) {
172 chromeos::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
173 base::UTF8ToUTF16(timezone_name));
177 struct ShowLoginWizardSwitchLanguageCallbackData {
178 explicit ShowLoginWizardSwitchLanguageCallbackData(
179 const std::string& first_screen_name,
180 const chromeos::StartupCustomizationDocument* startup_manifest,
181 chromeos::LoginDisplayHost* display_host)
182 : first_screen_name(first_screen_name),
183 startup_manifest(startup_manifest),
184 display_host(display_host) {}
186 const std::string first_screen_name;
187 const chromeos::StartupCustomizationDocument* const startup_manifest;
188 chromeos::LoginDisplayHost* const display_host;
190 // lock UI while resource bundle is being reloaded.
191 chromeos::InputEventsBlocker events_blocker;
194 void OnLanguageSwitchedCallback(
195 scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> self,
196 const std::string& locale,
197 const std::string& loaded_locale,
198 const bool success) {
200 LOG(WARNING) << "Locale could not be found for '" << locale << "'";
202 ShowLoginWizardFinish(
203 self->first_screen_name, self->startup_manifest, self->display_host);
206 void EnableSystemSoundsForAccessibility() {
207 chromeos::AccessibilityManager::Get()->EnableSystemSounds(true);
210 void AddToSetIfIsGaiaAuthIframe(std::set<content::RenderFrameHost*>* frame_set,
211 content::RenderFrameHost* frame) {
212 content::RenderFrameHost* parent = frame->GetParent();
213 if (parent && parent->GetFrameName() == "signin-frame")
214 frame_set->insert(frame);
217 // A login implementation of WidgetDelegate.
218 class LoginWidgetDelegate : public views::WidgetDelegate {
220 explicit LoginWidgetDelegate(views::Widget* widget) : widget_(widget) {
222 virtual ~LoginWidgetDelegate() {}
224 // Overridden from WidgetDelegate:
225 virtual void DeleteDelegate() OVERRIDE {
228 virtual views::Widget* GetWidget() OVERRIDE {
231 virtual const views::Widget* GetWidget() const OVERRIDE {
234 virtual bool CanActivate() const OVERRIDE {
237 virtual bool ShouldAdvanceFocusToTopLevelWidget() const OVERRIDE {
242 views::Widget* widget_;
244 DISALLOW_COPY_AND_ASSIGN(LoginWidgetDelegate);
247 // Disables virtual keyboard overscroll. Login UI will scroll user pods
248 // into view on JS side when virtual keyboard is shown.
249 void DisableKeyboardOverscroll() {
250 keyboard::SetKeyboardOverscrollOverride(
251 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
254 void ResetKeyboardOverscrollOverride() {
255 keyboard::SetKeyboardOverscrollOverride(
256 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE);
264 LoginDisplayHost* LoginDisplayHostImpl::default_host_ = NULL;
267 const int LoginDisplayHostImpl::kShowLoginWebUIid = 0x1111;
270 content::RenderFrameHost* LoginDisplayHostImpl::GetGaiaAuthIframe(
271 content::WebContents* web_contents) {
272 std::set<content::RenderFrameHost*> frame_set;
273 web_contents->ForEachFrame(
274 base::Bind(&AddToSetIfIsGaiaAuthIframe, &frame_set));
275 DCHECK_EQ(1U, frame_set.size());
276 return *frame_set.begin();
279 ////////////////////////////////////////////////////////////////////////////////
280 // LoginDisplayHostImpl, public
282 LoginDisplayHostImpl::LoginDisplayHostImpl(const gfx::Rect& background_bounds)
283 : background_bounds_(background_bounds),
284 pointer_factory_(this),
285 shutting_down_(false),
286 oobe_progress_bar_visible_(false),
287 session_starting_(false),
290 webui_login_display_(NULL),
291 is_showing_login_(false),
292 is_wallpaper_loaded_(false),
293 status_area_saved_visibility_(false),
295 restore_path_(RESTORE_UNKNOWN),
296 finalize_animation_type_(ANIMATION_WORKSPACE),
297 animation_weak_ptr_factory_(this),
298 startup_sound_played_(false),
299 startup_sound_honors_spoken_feedback_(false),
300 is_observing_keyboard_(false) {
301 DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this);
302 CrasAudioHandler::Get()->AddAudioObserver(this);
303 if (keyboard::KeyboardController::GetInstance()) {
304 keyboard::KeyboardController::GetInstance()->AddObserver(this);
305 is_observing_keyboard_ = true;
308 ash::Shell::GetInstance()->delegate()->AddVirtualKeyboardStateObserver(this);
309 ash::Shell::GetScreen()->AddObserver(this);
311 // We need to listen to CLOSE_ALL_BROWSERS_REQUEST but not APP_TERMINATING
312 // because/ APP_TERMINATING will never be fired as long as this keeps
313 // ref-count. CLOSE_ALL_BROWSERS_REQUEST is safe here because there will be no
314 // browser instance that will block the shutdown.
316 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
317 content::NotificationService::AllSources());
319 // NOTIFICATION_BROWSER_OPENED is issued after browser is created, but
320 // not shown yet. Lock window has to be closed at this point so that
321 // a browser window exists and the window can acquire input focus.
323 chrome::NOTIFICATION_BROWSER_OPENED,
324 content::NotificationService::AllSources());
326 // Login screen is moved to lock screen container when user logs in.
328 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
329 content::NotificationService::AllSources());
331 DCHECK(default_host_ == NULL);
332 default_host_ = this;
334 // Make sure chrome won't exit while we are at login/oobe screen.
335 chrome::IncrementKeepAliveCount();
337 bool is_registered = StartupUtils::IsDeviceRegistered();
338 bool zero_delay_enabled = WizardController::IsZeroDelayEnabled();
339 bool disable_boot_animation = CommandLine::ForCurrentProcess()->HasSwitch(
340 switches::kDisableBootAnimation);
342 waiting_for_wallpaper_load_ = !zero_delay_enabled &&
343 (!is_registered || !disable_boot_animation);
345 // For slower hardware we have boot animation disabled so
346 // we'll be initializing WebUI hidden, waiting for user pods to load and then
347 // show WebUI at once.
348 waiting_for_user_pods_ = !zero_delay_enabled && !waiting_for_wallpaper_load_;
350 initialize_webui_hidden_ =
351 kHiddenWebUIInitializationDefault && !zero_delay_enabled;
353 // Check if WebUI init type is overriden.
354 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshWebUIInit)) {
355 const std::string override_type =
356 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
357 switches::kAshWebUIInit);
358 if (override_type == kWebUIInitParallel)
359 initialize_webui_hidden_ = true;
360 else if (override_type == kWebUIInitPostpone)
361 initialize_webui_hidden_ = false;
364 // Always postpone WebUI initialization on first boot, otherwise we miss
365 // initial animation.
366 if (!StartupUtils::IsOobeCompleted())
367 initialize_webui_hidden_ = false;
369 // There is no wallpaper for KioskMode, don't initialize the webui hidden.
370 if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
371 initialize_webui_hidden_ = false;
373 if (waiting_for_wallpaper_load_) {
375 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
376 content::NotificationService::AllSources());
379 // When we wait for WebUI to be initialized we wait for one of
380 // these notifications.
381 if ((waiting_for_user_pods_ || waiting_for_wallpaper_load_) &&
382 initialize_webui_hidden_) {
384 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
385 content::NotificationService::AllSources());
387 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
388 content::NotificationService::AllSources());
390 LOG(WARNING) << "Login WebUI >> "
391 << "zero_delay: " << zero_delay_enabled
392 << " wait_for_wp_load_: " << waiting_for_wallpaper_load_
393 << " wait_for_pods_: " << waiting_for_user_pods_
394 << " init_webui_hidden_: " << initialize_webui_hidden_;
396 media::SoundsManager* manager = media::SoundsManager::Get();
397 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
398 manager->Initialize(chromeos::SOUND_STARTUP,
399 bundle.GetRawDataResource(IDR_SOUND_STARTUP_WAV));
402 LoginDisplayHostImpl::~LoginDisplayHostImpl() {
403 DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
404 CrasAudioHandler::Get()->RemoveAudioObserver(this);
405 if (keyboard::KeyboardController::GetInstance() && is_observing_keyboard_) {
406 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
407 is_observing_keyboard_ = false;
410 ash::Shell::GetInstance()->delegate()->
411 RemoveVirtualKeyboardStateObserver(this);
412 ash::Shell::GetScreen()->RemoveObserver(this);
414 if (login::LoginScrollIntoViewEnabled())
415 ResetKeyboardOverscrollOverride();
417 views::FocusManager::set_arrow_key_traversal_enabled(false);
418 ResetLoginWindowAndView();
420 // Let chrome process exit after login/oobe screen if needed.
421 chrome::DecrementKeepAliveCount();
423 default_host_ = NULL;
424 // TODO(tengs): This should be refactored. See crbug.com/314934.
425 if (UserManager::Get()->IsCurrentUserNew()) {
426 // DriveOptInController will delete itself when finished.
427 (new DriveFirstRunController(
428 ProfileManager::GetActiveUserProfile()))->EnableOfflineMode();
432 ////////////////////////////////////////////////////////////////////////////////
433 // LoginDisplayHostImpl, LoginDisplayHost implementation:
435 LoginDisplay* LoginDisplayHostImpl::CreateLoginDisplay(
436 LoginDisplay::Delegate* delegate) {
437 webui_login_display_ = new WebUILoginDisplay(delegate);
438 webui_login_display_->set_background_bounds(background_bounds());
439 return webui_login_display_;
442 gfx::NativeWindow LoginDisplayHostImpl::GetNativeWindow() const {
443 return login_window_ ? login_window_->GetNativeWindow() : NULL;
446 WebUILoginView* LoginDisplayHostImpl::GetWebUILoginView() const {
450 void LoginDisplayHostImpl::BeforeSessionStart() {
451 session_starting_ = true;
454 void LoginDisplayHostImpl::Finalize() {
455 DVLOG(1) << "Session starting";
456 if (ash::Shell::HasInstance()) {
457 ash::Shell::GetInstance()->
458 desktop_background_controller()->MoveDesktopToUnlockedContainer();
460 if (wizard_controller_.get())
461 wizard_controller_->OnSessionStart();
463 switch (finalize_animation_type_) {
465 ShutdownDisplayHost(false);
467 case ANIMATION_WORKSPACE:
468 if (ash::Shell::HasInstance())
469 ScheduleWorkspaceAnimation();
471 ShutdownDisplayHost(false);
473 case ANIMATION_FADE_OUT:
474 // Display host is deleted once animation is completed
475 // since sign in screen widget has to stay alive.
476 ScheduleFadeOutAnimation();
481 void LoginDisplayHostImpl::OnCompleteLogin() {
482 if (auto_enrollment_controller_)
483 auto_enrollment_controller_->Cancel();
486 void LoginDisplayHostImpl::OpenProxySettings() {
488 login_view_->OpenProxySettings();
491 void LoginDisplayHostImpl::SetStatusAreaVisible(bool visible) {
492 if (initialize_webui_hidden_)
493 status_area_saved_visibility_ = visible;
494 else if (login_view_)
495 login_view_->SetStatusAreaVisible(visible);
498 AutoEnrollmentController* LoginDisplayHostImpl::GetAutoEnrollmentController() {
499 if (!auto_enrollment_controller_) {
500 auto_enrollment_controller_.reset(new AutoEnrollmentController());
501 auto_enrollment_progress_subscription_ =
502 auto_enrollment_controller_->RegisterProgressCallback(
503 base::Bind(&LoginDisplayHostImpl::OnAutoEnrollmentProgress,
504 base::Unretained(this)));
506 return auto_enrollment_controller_.get();
509 void LoginDisplayHostImpl::StartWizard(
510 const std::string& first_screen_name,
511 scoped_ptr<base::DictionaryValue> screen_parameters) {
512 if (login::LoginScrollIntoViewEnabled())
513 DisableKeyboardOverscroll();
515 startup_sound_honors_spoken_feedback_ = true;
516 TryToPlayStartupSound();
518 // Keep parameters to restore if renderer crashes.
519 restore_path_ = RESTORE_WIZARD;
520 wizard_first_screen_name_ = first_screen_name;
521 if (screen_parameters.get())
522 wizard_screen_parameters_.reset(screen_parameters->DeepCopy());
524 wizard_screen_parameters_.reset();
525 is_showing_login_ = false;
527 if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
528 LOG(WARNING) << "Login WebUI >> wizard postponed";
531 LOG(WARNING) << "Login WebUI >> wizard";
534 LoadURL(GURL(kOobeURL));
536 DVLOG(1) << "Starting wizard, first_screen_name: " << first_screen_name;
537 // Create and show the wizard.
538 // Note, dtor of the old WizardController should be called before ctor of the
539 // new one, because "default_controller()" is updated there. So pure "reset()"
540 // is done before new controller creation.
541 wizard_controller_.reset();
542 wizard_controller_.reset(CreateWizardController());
544 oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
545 SetOobeProgressBarVisible(oobe_progress_bar_visible_);
546 wizard_controller_->Init(first_screen_name, screen_parameters.Pass());
549 WizardController* LoginDisplayHostImpl::GetWizardController() {
550 return wizard_controller_.get();
553 AppLaunchController* LoginDisplayHostImpl::GetAppLaunchController() {
554 return app_launch_controller_.get();
557 void LoginDisplayHostImpl::StartUserAdding(
558 const base::Closure& completion_callback) {
559 if (login::LoginScrollIntoViewEnabled())
560 DisableKeyboardOverscroll();
562 restore_path_ = RESTORE_ADD_USER_INTO_SESSION;
563 completion_callback_ = completion_callback;
564 finalize_animation_type_ = ANIMATION_NONE;
565 LOG(WARNING) << "Login WebUI >> user adding";
567 LoadURL(GURL(kUserAddingURL));
568 // We should emit this signal only at login screen (after reboot or sign out).
569 login_view_->set_should_emit_login_prompt_visible(false);
571 // Lock container can be transparent after lock screen animation.
572 aura::Window* lock_container = ash::Shell::GetContainer(
573 ash::Shell::GetPrimaryRootWindow(),
574 ash::kShellWindowId_LockScreenContainersContainer);
575 lock_container->layer()->SetOpacity(1.0);
577 ash::Shell::GetInstance()->
578 desktop_background_controller()->MoveDesktopToLockedContainer();
580 sign_in_controller_.reset(); // Only one controller in a time.
581 sign_in_controller_.reset(new chromeos::ExistingUserController(this));
582 SetOobeProgressBarVisible(oobe_progress_bar_visible_ = false);
583 SetStatusAreaVisible(true);
584 sign_in_controller_->Init(
585 chromeos::UserManager::Get()->GetUsersAdmittedForMultiProfile());
586 CHECK(webui_login_display_);
587 GetOobeUI()->ShowSigninScreen(LoginScreenContext(),
588 webui_login_display_,
589 webui_login_display_);
592 void LoginDisplayHostImpl::StartSignInScreen(
593 const LoginScreenContext& context) {
594 if (login::LoginScrollIntoViewEnabled())
595 DisableKeyboardOverscroll();
597 startup_sound_honors_spoken_feedback_ = true;
598 TryToPlayStartupSound();
600 restore_path_ = RESTORE_SIGN_IN;
601 is_showing_login_ = true;
602 finalize_animation_type_ = ANIMATION_WORKSPACE;
604 PrewarmAuthentication();
606 if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
607 LOG(WARNING) << "Login WebUI >> sign in postponed";
610 LOG(WARNING) << "Login WebUI >> sign in";
612 if (!login_window_) {
613 TRACE_EVENT_ASYNC_BEGIN0("ui", "ShowLoginWebUI", kShowLoginWebUIid);
614 TRACE_EVENT_ASYNC_STEP_INTO0(
615 "ui", "ShowLoginWebUI", kShowLoginWebUIid, "StartSignInScreen");
616 BootTimesLoader::Get()->RecordCurrentStats("login-start-signin-screen");
617 LoadURL(GURL(kLoginURL));
620 DVLOG(1) << "Starting sign in screen";
621 const chromeos::UserList& users = chromeos::UserManager::Get()->GetUsers();
623 // Fix for users who updated device and thus never passed register screen.
624 // If we already have users, we assume that it is not a second part of
625 // OOBE. See http://crosbug.com/6289
626 if (!StartupUtils::IsDeviceRegistered() && !users.empty()) {
627 VLOG(1) << "Mark device registered because there are remembered users: "
629 StartupUtils::MarkDeviceRegistered(base::Closure());
632 sign_in_controller_.reset(); // Only one controller in a time.
633 sign_in_controller_.reset(new chromeos::ExistingUserController(this));
634 oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
635 SetOobeProgressBarVisible(oobe_progress_bar_visible_);
636 SetStatusAreaVisible(true);
637 sign_in_controller_->Init(users);
639 // We might be here after a reboot that was triggered after OOBE was complete,
640 // so check for auto-enrollment again. This might catch a cached decision from
641 // a previous oobe flow, or might start a new check with the server.
642 if (GetAutoEnrollmentController()->ShouldEnrollSilently())
643 sign_in_controller_->DoAutoEnrollment();
645 GetAutoEnrollmentController()->Start();
647 // Initiate mobile config load.
648 MobileConfig::GetInstance();
650 // Initiate device policy fetching.
651 policy::BrowserPolicyConnectorChromeOS* connector =
652 g_browser_process->platform_part()->browser_policy_connector_chromeos();
653 connector->ScheduleServiceInitialization(
654 kPolicyServiceInitializationDelayMilliseconds);
656 CHECK(webui_login_display_);
657 GetOobeUI()->ShowSigninScreen(context,
658 webui_login_display_,
659 webui_login_display_);
660 if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
661 SetStatusAreaVisible(false);
662 TRACE_EVENT_ASYNC_STEP_INTO0("ui",
665 "WaitForScreenStateInitialize");
666 BootTimesLoader::Get()->RecordCurrentStats(
667 "login-wait-for-signin-state-initialize");
670 void LoginDisplayHostImpl::ResumeSignInScreen() {
671 // We only get here after a previous call the StartSignInScreen. That sign-in
672 // was successful but was interrupted by an auto-enrollment execution; once
673 // auto-enrollment is complete we resume the normal login flow from here.
674 DVLOG(1) << "Resuming sign in screen";
675 CHECK(sign_in_controller_.get());
676 SetOobeProgressBarVisible(oobe_progress_bar_visible_);
677 SetStatusAreaVisible(true);
678 sign_in_controller_->ResumeLogin();
682 void LoginDisplayHostImpl::OnPreferencesChanged() {
683 if (is_showing_login_)
684 webui_login_display_->OnPreferencesChanged();
687 void LoginDisplayHostImpl::PrewarmAuthentication() {
688 auth_prewarmer_.reset(new AuthPrewarmer());
689 auth_prewarmer_->PrewarmAuthentication(
690 base::Bind(&LoginDisplayHostImpl::OnAuthPrewarmDone,
691 pointer_factory_.GetWeakPtr()));
694 void LoginDisplayHostImpl::StartDemoAppLaunch() {
695 LOG(WARNING) << "Login WebUI >> starting demo app.";
696 SetStatusAreaVisible(false);
698 demo_app_launcher_.reset(new DemoAppLauncher());
699 demo_app_launcher_->StartDemoAppLaunch();
702 void LoginDisplayHostImpl::StartAppLaunch(const std::string& app_id,
703 bool diagnostic_mode) {
704 LOG(WARNING) << "Login WebUI >> start app launch.";
705 SetStatusAreaVisible(false);
706 finalize_animation_type_ = ANIMATION_FADE_OUT;
708 LoadURL(GURL(kAppLaunchSplashURL));
710 login_view_->set_should_emit_login_prompt_visible(false);
712 app_launch_controller_.reset(new AppLaunchController(
713 app_id, diagnostic_mode, this, GetOobeUI()));
715 app_launch_controller_->StartAppLaunch();
718 ////////////////////////////////////////////////////////////////////////////////
719 // LoginDisplayHostImpl, public
721 WizardController* LoginDisplayHostImpl::CreateWizardController() {
722 // TODO(altimofeev): ensure that WebUI is ready.
723 OobeDisplay* oobe_display = GetOobeUI();
724 return new WizardController(this, oobe_display);
727 void LoginDisplayHostImpl::OnBrowserCreated() {
728 // Close lock window now so that the launched browser can receive focus.
729 ResetLoginWindowAndView();
732 OobeUI* LoginDisplayHostImpl::GetOobeUI() const {
735 return static_cast<OobeUI*>(login_view_->GetWebUI()->GetController());
738 ////////////////////////////////////////////////////////////////////////////////
739 // LoginDisplayHostImpl, content:NotificationObserver implementation:
741 void LoginDisplayHostImpl::Observe(
743 const content::NotificationSource& source,
744 const content::NotificationDetails& details) {
745 if (chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED == type) {
746 LOG(WARNING) << "Login WebUI >> wp animation done";
747 is_wallpaper_loaded_ = true;
748 ash::Shell::GetInstance()->user_wallpaper_delegate()
749 ->OnWallpaperBootAnimationFinished();
750 if (waiting_for_wallpaper_load_) {
751 // StartWizard / StartSignInScreen could be called multiple times through
752 // the lifetime of host.
753 // Make sure that subsequent calls are not postponed.
754 waiting_for_wallpaper_load_ = false;
755 if (initialize_webui_hidden_)
758 StartPostponedWebUI();
760 registrar_.Remove(this,
761 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
762 content::NotificationService::AllSources());
763 } else if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type ||
764 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN == type) {
765 LOG(WARNING) << "Login WebUI >> WEBUI_VISIBLE";
766 if (waiting_for_user_pods_ && initialize_webui_hidden_) {
767 waiting_for_user_pods_ = false;
769 } else if (waiting_for_wallpaper_load_ && initialize_webui_hidden_) {
770 // Reduce time till login UI is shown - show it as soon as possible.
771 waiting_for_wallpaper_load_ = false;
774 registrar_.Remove(this,
775 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
776 content::NotificationService::AllSources());
777 registrar_.Remove(this,
778 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
779 content::NotificationService::AllSources());
780 } else if (type == chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST) {
781 ShutdownDisplayHost(true);
782 } else if (type == chrome::NOTIFICATION_BROWSER_OPENED && session_starting_) {
783 // Browsers created before session start (windows opened by extensions, for
784 // example) are ignored.
786 registrar_.Remove(this,
787 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
788 content::NotificationService::AllSources());
789 registrar_.Remove(this,
790 chrome::NOTIFICATION_BROWSER_OPENED,
791 content::NotificationService::AllSources());
792 } else if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED &&
793 chromeos::UserManager::Get()->IsCurrentUserNew()) {
794 // For new user, move desktop to locker container so that windows created
795 // during the user image picker step are below it.
796 ash::Shell::GetInstance()->
797 desktop_background_controller()->MoveDesktopToLockedContainer();
798 registrar_.Remove(this,
799 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
800 content::NotificationService::AllSources());
804 ////////////////////////////////////////////////////////////////////////////////
805 // LoginDisplayHostImpl, WebContentsObserver implementation:
807 void LoginDisplayHostImpl::RenderProcessGone(base::TerminationStatus status) {
808 // Do not try to restore on shutdown
809 if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID)
813 if (crash_count_ > kCrashCountLimit)
816 if (status != base::TERMINATION_STATUS_NORMAL_TERMINATION) {
817 // Render with login screen crashed. Let's crash browser process to let
818 // session manager restart it properly. It is hard to reload the page
819 // and get to controlled state that is fully functional.
820 // If you see check, search for renderer crash for the same client.
821 LOG(FATAL) << "Renderer crash on login window";
825 ////////////////////////////////////////////////////////////////////////////////
826 // LoginDisplayHostImpl, chromeos::SessionManagerClient::Observer
829 void LoginDisplayHostImpl::EmitLoginPromptVisibleCalled() {
830 OnLoginPromptVisible();
833 ////////////////////////////////////////////////////////////////////////////////
834 // LoginDisplayHostImpl, chromeos::CrasAudioHandler::AudioObserver
837 void LoginDisplayHostImpl::OnActiveOutputNodeChanged() {
838 TryToPlayStartupSound();
841 ////////////////////////////////////////////////////////////////////////////////
842 // LoginDisplayHostImpl, ash::KeyboardStateObserver:
845 void LoginDisplayHostImpl::OnVirtualKeyboardStateChanged(bool activated) {
846 if (keyboard::KeyboardController::GetInstance()) {
848 if (!is_observing_keyboard_) {
849 keyboard::KeyboardController::GetInstance()->AddObserver(this);
850 is_observing_keyboard_ = true;
853 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
854 is_observing_keyboard_ = false;
859 ////////////////////////////////////////////////////////////////////////////////
860 // LoginDisplayHostImpl, keyboard::KeyboardControllerObserver:
863 void LoginDisplayHostImpl::OnKeyboardBoundsChanging(
864 const gfx::Rect& new_bounds) {
865 if (new_bounds.IsEmpty() && !keyboard_bounds_.IsEmpty()) {
866 // Keyboard has been hidden.
868 GetOobeUI()->GetCoreOobeActor()->ShowControlBar(true);
869 if (login::LoginScrollIntoViewEnabled())
870 GetOobeUI()->GetCoreOobeActor()->SetKeyboardState(false, new_bounds);
872 } else if (!new_bounds.IsEmpty() && keyboard_bounds_.IsEmpty()) {
873 // Keyboard has been shown.
875 GetOobeUI()->GetCoreOobeActor()->ShowControlBar(false);
876 if (login::LoginScrollIntoViewEnabled())
877 GetOobeUI()->GetCoreOobeActor()->SetKeyboardState(true, new_bounds);
881 keyboard_bounds_ = new_bounds;
884 ////////////////////////////////////////////////////////////////////////////////
885 // LoginDisplayHostImpl, gfx::DisplayObserver implementation:
887 void LoginDisplayHostImpl::OnDisplayAdded(const gfx::Display& new_display) {
890 void LoginDisplayHostImpl::OnDisplayRemoved(const gfx::Display& old_display) {
893 void LoginDisplayHostImpl::OnDisplayMetricsChanged(const gfx::Display& display,
894 uint32_t changed_metrics) {
895 if (display.id() != ash::Shell::GetScreen()->GetPrimaryDisplay().id() ||
896 !(changed_metrics & DISPLAY_METRIC_BOUNDS)) {
901 const gfx::Size& size = ash::Shell::GetScreen()->GetPrimaryDisplay().size();
902 GetOobeUI()->GetCoreOobeActor()->SetClientAreaSize(size.width(),
907 ////////////////////////////////////////////////////////////////////////////////
908 // LoginDisplayHostImpl, private
910 void LoginDisplayHostImpl::ShutdownDisplayHost(bool post_quit_task) {
914 shutting_down_ = true;
915 registrar_.RemoveAll();
916 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
918 base::MessageLoop::current()->Quit();
920 if (!completion_callback_.is_null())
921 completion_callback_.Run();
924 void LoginDisplayHostImpl::ScheduleWorkspaceAnimation() {
925 if (ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
926 ash::kShellWindowId_DesktopBackgroundContainer)
929 // If there is no background window, don't perform any animation on the
930 // default and background layer because there is nothing behind it.
934 if (!CommandLine::ForCurrentProcess()->HasSwitch(
935 switches::kDisableLoginAnimations))
936 ash::Shell::GetInstance()->DoInitialWorkspaceAnimation();
939 void LoginDisplayHostImpl::ScheduleFadeOutAnimation() {
940 ui::Layer* layer = login_window_->GetLayer();
941 ui::ScopedLayerAnimationSettings animation(layer->GetAnimator());
942 animation.AddObserver(new AnimationObserver(
943 base::Bind(&LoginDisplayHostImpl::ShutdownDisplayHost,
944 animation_weak_ptr_factory_.GetWeakPtr(),
946 layer->SetOpacity(0);
949 void LoginDisplayHostImpl::OnAutoEnrollmentProgress(
950 policy::AutoEnrollmentState state) {
951 VLOG(1) << "OnAutoEnrollmentProgress, state " << state;
953 if (sign_in_controller_ &&
954 auto_enrollment_controller_->ShouldEnrollSilently()) {
955 sign_in_controller_->DoAutoEnrollment();
959 void LoginDisplayHostImpl::LoadURL(const GURL& url) {
960 InitLoginWindowAndView();
961 // Subscribe to crash events.
962 content::WebContentsObserver::Observe(login_view_->GetWebContents());
963 login_view_->LoadURL(url);
965 // LoadURL could be called after the spring charger dialog shows, and
966 // take away the focus from it. Set the focus back to the charger dialog
968 // See crbug.com/328538.
969 ChargerReplacementDialog::SetFocusOnChargerDialogIfVisible();
972 void LoginDisplayHostImpl::ShowWebUI() {
973 if (!login_window_ || !login_view_) {
977 LOG(WARNING) << "Login WebUI >> Show already initialized UI";
978 login_window_->Show();
979 login_view_->GetWebContents()->Focus();
980 if (::switches::IsTextInputFocusManagerEnabled())
981 login_view_->RequestFocus();
982 login_view_->SetStatusAreaVisible(status_area_saved_visibility_);
983 login_view_->OnPostponedShow();
985 // Login window could be shown after the spring charger dialog shows, and
986 // take away the focus from it. Set the focus back to the charger dialog
988 // See crbug.com/328538.
989 ChargerReplacementDialog::SetFocusOnChargerDialogIfVisible();
991 // We should reset this flag to allow changing of status area visibility.
992 initialize_webui_hidden_ = false;
995 void LoginDisplayHostImpl::StartPostponedWebUI() {
996 if (!is_wallpaper_loaded_) {
1000 LOG(WARNING) << "Login WebUI >> Init postponed WebUI";
1002 // Wallpaper has finished loading before StartWizard/StartSignInScreen has
1003 // been called. In general this should not happen.
1004 // Let go through normal code path when one of those will be called.
1005 if (restore_path_ == RESTORE_UNKNOWN) {
1010 switch (restore_path_) {
1011 case RESTORE_WIZARD:
1012 StartWizard(wizard_first_screen_name_,
1013 wizard_screen_parameters_.Pass());
1015 case RESTORE_SIGN_IN:
1016 StartSignInScreen(LoginScreenContext());
1018 case RESTORE_ADD_USER_INTO_SESSION:
1019 StartUserAdding(completion_callback_);
1027 void LoginDisplayHostImpl::InitLoginWindowAndView() {
1031 if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation()) {
1032 views::FocusManager::set_arrow_key_traversal_enabled(true);
1034 focus_ring_controller_.reset(new FocusRingController);
1035 focus_ring_controller_->SetVisible(true);
1037 keyboard_driven_oobe_key_handler_.reset(new KeyboardDrivenOobeKeyHandler);
1040 views::Widget::InitParams params(
1041 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1042 params.bounds = background_bounds();
1043 params.show_state = ui::SHOW_STATE_FULLSCREEN;
1044 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
1046 ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
1047 ash::kShellWindowId_LockScreenContainer);
1049 login_window_ = new views::Widget;
1050 params.delegate = new LoginWidgetDelegate(login_window_);
1051 login_window_->Init(params);
1053 login_view_ = new WebUILoginView();
1054 login_view_->Init();
1055 if (login_view_->webui_visible())
1056 OnLoginPromptVisible();
1058 wm::SetWindowVisibilityAnimationDuration(
1059 login_window_->GetNativeView(),
1060 base::TimeDelta::FromMilliseconds(kLoginFadeoutTransitionDurationMs));
1061 wm::SetWindowVisibilityAnimationTransition(
1062 login_window_->GetNativeView(),
1065 login_window_->SetContentsView(login_view_);
1067 // If WebUI is initialized in hidden state, show it only if we're no
1068 // longer waiting for wallpaper animation/user images loading. Otherwise,
1070 if (!initialize_webui_hidden_ ||
1071 (!waiting_for_wallpaper_load_ && !waiting_for_user_pods_)) {
1072 LOG(WARNING) << "Login WebUI >> show login wnd on create";
1073 login_window_->Show();
1075 LOG(WARNING) << "Login WebUI >> login wnd is hidden on create";
1076 login_view_->set_is_hidden(true);
1078 login_window_->GetNativeView()->SetName("WebUILoginView");
1081 void LoginDisplayHostImpl::ResetLoginWindowAndView() {
1084 login_window_->Close();
1085 login_window_ = NULL;
1089 void LoginDisplayHostImpl::OnAuthPrewarmDone() {
1090 auth_prewarmer_.reset();
1093 void LoginDisplayHostImpl::SetOobeProgressBarVisible(bool visible) {
1094 GetOobeUI()->ShowOobeUI(visible);
1097 void LoginDisplayHostImpl::TryToPlayStartupSound() {
1098 if (startup_sound_played_ || login_prompt_visible_time_.is_null() ||
1099 !CrasAudioHandler::Get()->GetActiveOutputNode()) {
1103 startup_sound_played_ = true;
1105 // Don't try play startup sound if login prompt is already visible
1106 // for a long time or can't be played.
1107 if (base::TimeTicks::Now() - login_prompt_visible_time_ >
1108 base::TimeDelta::FromMilliseconds(kStartupSoundMaxDelayMs)) {
1109 EnableSystemSoundsForAccessibility();
1113 if (!startup_sound_honors_spoken_feedback_ &&
1114 !ash::PlaySystemSoundAlways(SOUND_STARTUP)) {
1115 EnableSystemSoundsForAccessibility();
1119 if (startup_sound_honors_spoken_feedback_ &&
1120 !ash::PlaySystemSoundIfSpokenFeedback(SOUND_STARTUP)) {
1121 EnableSystemSoundsForAccessibility();
1125 base::MessageLoop::current()->PostDelayedTask(
1127 base::Bind(&EnableSystemSoundsForAccessibility),
1128 media::SoundsManager::Get()->GetDuration(SOUND_STARTUP));
1131 void LoginDisplayHostImpl::OnLoginPromptVisible() {
1132 if (!login_prompt_visible_time_.is_null())
1134 login_prompt_visible_time_ = base::TimeTicks::Now();
1135 TryToPlayStartupSound();
1138 ////////////////////////////////////////////////////////////////////////////////
1141 // Declared in login_wizard.h so that others don't need to depend on our .h.
1142 // TODO(nkostylev): Split this into a smaller functions.
1143 void ShowLoginWizard(const std::string& first_screen_name) {
1144 if (browser_shutdown::IsTryingToQuit())
1147 VLOG(1) << "Showing OOBE screen: " << first_screen_name;
1149 chromeos::input_method::InputMethodManager* manager =
1150 chromeos::input_method::InputMethodManager::Get();
1152 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
1153 // and US dvorak keyboard layouts.
1154 if (g_browser_process && g_browser_process->local_state()) {
1155 manager->SetInputMethodLoginDefault();
1157 PrefService* prefs = g_browser_process->local_state();
1158 // Apply owner preferences for tap-to-click and mouse buttons swap for
1160 system::InputDeviceSettings::Get()->SetPrimaryButtonRight(
1161 prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight));
1162 system::InputDeviceSettings::Get()->SetTapToClick(
1163 prefs->GetBoolean(prefs::kOwnerTapToClickEnabled));
1165 system::InputDeviceSettings::Get()->SetNaturalScroll(
1166 CommandLine::ForCurrentProcess()->HasSwitch(
1167 switches::kNaturalScrollDefault));
1169 gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));
1171 // Check whether we need to execute OOBE process.
1172 bool oobe_complete = chromeos::StartupUtils::IsOobeCompleted();
1173 if (!oobe_complete) {
1174 LoginState::Get()->SetLoggedInState(
1175 LoginState::LOGGED_IN_OOBE, LoginState::LOGGED_IN_USER_NONE);
1177 LoginState::Get()->SetLoggedInState(
1178 LoginState::LOGGED_IN_NONE, LoginState::LOGGED_IN_USER_NONE);
1181 LoginDisplayHost* display_host = new LoginDisplayHostImpl(screen_bounds);
1183 bool show_app_launch_splash_screen = (first_screen_name ==
1184 chromeos::WizardController::kAppLaunchSplashScreenName);
1185 if (show_app_launch_splash_screen) {
1186 const std::string& auto_launch_app_id =
1187 chromeos::KioskAppManager::Get()->GetAutoLaunchApp();
1188 display_host->StartAppLaunch(auto_launch_app_id,
1189 false /* diagnostic_mode */);
1193 policy::BrowserPolicyConnectorChromeOS* connector =
1194 g_browser_process->platform_part()->browser_policy_connector_chromeos();
1195 bool enrollment_screen_wanted =
1196 chromeos::WizardController::ShouldRecoverEnrollment() ||
1197 (chromeos::WizardController::ShouldAutoStartEnrollment() &&
1199 !connector->IsEnterpriseManaged());
1200 if (enrollment_screen_wanted && first_screen_name.empty()) {
1201 // Shows networks screen instead of enrollment screen to resume the
1202 // interrupted auto start enrollment flow because enrollment screen does
1203 // not handle flaky network. See http://crbug.com/332572
1204 display_host->StartWizard(chromeos::WizardController::kNetworkScreenName,
1205 scoped_ptr<base::DictionaryValue>());
1209 if (StartupUtils::IsEulaAccepted()) {
1211 ServicesCustomizationDocument::GetInstance()
1212 ->EnsureCustomizationAppliedClosure(),
1213 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS));
1216 bool show_login_screen =
1217 (first_screen_name.empty() && oobe_complete) ||
1218 first_screen_name == chromeos::WizardController::kLoginScreenName;
1220 if (show_login_screen) {
1221 display_host->StartSignInScreen(LoginScreenContext());
1225 // Load startup manifest.
1226 const chromeos::StartupCustomizationDocument* startup_manifest =
1227 chromeos::StartupCustomizationDocument::GetInstance();
1229 // Switch to initial locale if specified by customization
1230 // and has not been set yet. We cannot call
1231 // chromeos::LanguageSwitchMenu::SwitchLanguage here before
1232 // EmitLoginPromptReady.
1233 PrefService* prefs = g_browser_process->local_state();
1234 const std::string& current_locale =
1235 prefs->GetString(prefs::kApplicationLocale);
1236 VLOG(1) << "Current locale: " << current_locale;
1237 const std::string& locale = startup_manifest->initial_locale_default();
1239 const std::string& layout = startup_manifest->keyboard_layout();
1240 VLOG(1) << "Initial locale: " << locale << "keyboard layout " << layout;
1242 // Determine keyboard layout from OEM customization (if provided) or
1243 // initial locale and save it in preferences.
1244 manager->SetInputMethodLoginDefaultFromVPD(locale, layout);
1246 if (!current_locale.empty() || locale.empty()) {
1247 ShowLoginWizardFinish(first_screen_name, startup_manifest, display_host);
1251 // Save initial locale from VPD/customization manifest as current
1252 // Chrome locale. Otherwise it will be lost if Chrome restarts.
1253 // Don't need to schedule pref save because setting initial local
1254 // will enforce preference saving.
1255 prefs->SetString(prefs::kApplicationLocale, locale);
1256 chromeos::StartupUtils::SetInitialLocale(locale);
1258 scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> data(
1259 new ShowLoginWizardSwitchLanguageCallbackData(
1260 first_screen_name, startup_manifest, display_host));
1262 scoped_ptr<locale_util::SwitchLanguageCallback> callback(
1263 new locale_util::SwitchLanguageCallback(
1264 base::Bind(&OnLanguageSwitchedCallback, base::Passed(data.Pass()))));
1266 // Load locale keyboards here. Hardware layout would be automatically enabled.
1267 locale_util::SwitchLanguage(
1268 locale, true, true /* login_layouts_only */, callback.Pass());
1271 } // namespace chromeos