Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / login_display_host_impl.cc
blob9314bf0c93f390320d29807b8703062590715d3e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
7 #include <vector>
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/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"
86 namespace {
88 // Maximum delay for startup sound after 'loginPromptVisible' signal.
89 const int kStartupSoundMaxDelayMs = 2000;
91 // URL which corresponds to the login WebUI.
92 const char kLoginURL[] = "chrome://oobe/login";
94 // URL which corresponds to the OOBE WebUI.
95 const char kOobeURL[] = "chrome://oobe/oobe";
97 // URL which corresponds to the user adding WebUI.
98 const char kUserAddingURL[] = "chrome://oobe/user-adding";
100 // URL which corresponds to the app launch splash WebUI.
101 const char kAppLaunchSplashURL[] = "chrome://oobe/app-launch-splash";
103 // Duration of sign-in transition animation.
104 const int kLoginFadeoutTransitionDurationMs = 700;
106 // Number of times we try to reload OOBE/login WebUI if it crashes.
107 const int kCrashCountLimit = 5;
109 // Whether to enable tnitializing WebUI in hidden state (see
110 // |initialize_webui_hidden_|) by default.
111 const bool kHiddenWebUIInitializationDefault = true;
113 // Switch values that might be used to override WebUI init type.
114 const char kWebUIInitParallel[] = "parallel";
115 const char kWebUIInitPostpone[] = "postpone";
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;
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];
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();
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() {}
166 private:
167 // ui::ImplicitAnimationObserver implementation:
168 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
169 callback_.Run();
170 delete this;
173 base::Closure callback_;
175 DISALLOW_COPY_AND_ASSIGN(AnimationObserver);
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<base::DictionaryValue> params;
186 display_host->StartWizard(first_screen_name, params.Pass());
188 chromeos::DBusThreadManager::Get()->
189 GetSessionManagerClient()->
190 EmitLoginPromptReady();
191 TRACE_EVENT0("chromeos", "ShowLoginWizard::EmitLoginPromptReady");
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 base::UTF8ToUTF16(timezone_name));
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) {}
213 const std::string first_screen_name;
214 const chromeos::StartupCustomizationDocument* const startup_manifest;
215 chromeos::LoginDisplayHost* const display_host;
217 // lock UI while resource bundle is being reloaded.
218 chromeos::InputEventsBlocker events_blocker;
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 << "'";
229 ShowLoginWizardFinish(
230 self->first_screen_name, self->startup_manifest, self->display_host);
233 void EnableSystemSoundsForAccessibility() {
234 chromeos::AccessibilityManager::Get()->EnableSystemSounds(true);
237 } // namespace
239 namespace chromeos {
241 // static
242 LoginDisplayHost* LoginDisplayHostImpl::default_host_ = NULL;
244 // static
245 const int LoginDisplayHostImpl::kShowLoginWebUIid = 0x1111;
247 ////////////////////////////////////////////////////////////////////////////////
248 // LoginDisplayHostImpl, public
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);
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());
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());
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());
292 DCHECK(default_host_ == NULL);
293 default_host_ = this;
295 // Make sure chrome won't exit while we are at login/oobe screen.
296 chrome::StartKeepAlive();
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);
305 waiting_for_wallpaper_load_ = !zero_delay_enabled &&
306 (is_registered || !disable_oobe_animation) &&
307 (!is_registered || !disable_boot_animation);
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_;
314 initialize_webui_hidden_ =
315 kHiddenWebUIInitializationDefault && !zero_delay_enabled;
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;
328 // Always postpone WebUI initialization on first boot, otherwise we miss
329 // initial animation.
330 if (!StartupUtils::IsOobeCompleted())
331 initialize_webui_hidden_ = false;
333 // There is no wallpaper for KioskMode, don't initialize the webui hidden.
334 if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
335 initialize_webui_hidden_ = false;
337 if (waiting_for_wallpaper_load_) {
338 registrar_.Add(this,
339 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
340 content::NotificationService::AllSources());
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());
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_;
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));
366 LoginDisplayHostImpl::~LoginDisplayHostImpl() {
367 DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
368 CrasAudioHandler::Get()->RemoveAudioObserver(this);
370 views::FocusManager::set_arrow_key_traversal_enabled(false);
371 ResetLoginWindowAndView();
373 // Let chrome process exit after login/oobe screen if needed.
374 chrome::EndKeepAlive();
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();
388 ////////////////////////////////////////////////////////////////////////////////
389 // LoginDisplayHostImpl, LoginDisplayHost implementation:
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_;
398 gfx::NativeWindow LoginDisplayHostImpl::GetNativeWindow() const {
399 return login_window_ ? login_window_->GetNativeWindow() : NULL;
402 WebUILoginView* LoginDisplayHostImpl::GetWebUILoginView() const {
403 return login_view_;
406 void LoginDisplayHostImpl::BeforeSessionStart() {
407 session_starting_ = true;
410 void LoginDisplayHostImpl::Finalize() {
411 DVLOG(1) << "Session starting";
412 if (ash::Shell::HasInstance()) {
413 ash::Shell::GetInstance()->
414 desktop_background_controller()->MoveDesktopToUnlockedContainer();
416 if (wizard_controller_.get())
417 wizard_controller_->OnSessionStart();
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();
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;
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();
444 void LoginDisplayHostImpl::OpenProxySettings() {
445 if (login_view_)
446 login_view_->OpenProxySettings();
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);
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.
461 if (policy::AutoEnrollmentClient::IsDisabled()) {
462 VLOG(1) << "CheckForAutoEnrollment: auto-enrollment disabled";
463 auto_enrollment_check_done_ = true;
464 return;
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()));
474 void LoginDisplayHostImpl::GetAutoEnrollmentCheckResult(
475 const GetAutoEnrollmentCheckResultCallback& callback) {
476 DCHECK(!callback.is_null());
478 if (auto_enrollment_check_done_) {
479 callback.Run(auto_enrollment_client_ &&
480 auto_enrollment_client_->should_auto_enroll());
481 return;
484 get_auto_enrollment_result_callbacks_.push_back(callback);
487 void LoginDisplayHostImpl::StartWizard(
488 const std::string& first_screen_name,
489 scoped_ptr<base::DictionaryValue> screen_parameters) {
490 startup_sound_honors_spoken_feedback_ = false;
491 TryToPlayStartupSound();
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;
502 if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
503 LOG(WARNING) << "Login WebUI >> wizard postponed";
504 return;
506 LOG(WARNING) << "Login WebUI >> wizard";
508 if (!login_window_)
509 LoadURL(GURL(kOobeURL));
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());
519 oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
520 SetOobeProgressBarVisible(oobe_progress_bar_visible_);
521 wizard_controller_->Init(first_screen_name, screen_parameters.Pass());
524 WizardController* LoginDisplayHostImpl::GetWizardController() {
525 return wizard_controller_.get();
528 AppLaunchController* LoginDisplayHostImpl::GetAppLaunchController() {
529 return app_launch_controller_.get();
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);
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);
549 ash::Shell::GetInstance()->
550 desktop_background_controller()->MoveDesktopToLockedContainer();
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_);
564 void LoginDisplayHostImpl::StartSignInScreen(
565 const LoginScreenContext& context) {
566 startup_sound_honors_spoken_feedback_ = true;
567 TryToPlayStartupSound();
569 restore_path_ = RESTORE_SIGN_IN;
570 is_showing_login_ = true;
571 finalize_animation_type_ = ANIMATION_WORKSPACE;
573 PrewarmAuthentication();
575 if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
576 LOG(WARNING) << "Login WebUI >> sign in postponed";
577 return;
579 LOG(WARNING) << "Login WebUI >> sign in";
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));
589 DVLOG(1) << "Starting sign in screen";
590 const chromeos::UserList& users = chromeos::UserManager::Get()->GetUsers();
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();
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);
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();
613 // Initiate services customization manifest fetching.
614 ServicesCustomizationDocument::GetInstance()->StartFetching();
616 // Initiate mobile config load.
617 MobileConfig::GetInstance();
619 // Initiate device policy fetching.
620 g_browser_process->browser_policy_connector()->ScheduleServiceInitialization(
621 kPolicyServiceInitializationDelayMilliseconds);
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");
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();
649 void LoginDisplayHostImpl::OnPreferencesChanged() {
650 if (is_showing_login_)
651 webui_login_display_->OnPreferencesChanged();
654 void LoginDisplayHostImpl::PrewarmAuthentication() {
655 auth_prewarmer_.reset(new AuthPrewarmer());
656 auth_prewarmer_->PrewarmAuthentication(
657 base::Bind(&LoginDisplayHostImpl::OnAuthPrewarmDone,
658 pointer_factory_.GetWeakPtr()));
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));
668 login_view_->set_should_emit_login_prompt_visible(false);
670 app_launch_controller_.reset(new AppLaunchController(
671 app_id, this, GetOobeUI()));
673 app_launch_controller_->StartAppLaunch();
676 ////////////////////////////////////////////////////////////////////////////////
677 // LoginDisplayHostImpl, public
679 WizardController* LoginDisplayHostImpl::CreateWizardController() {
680 // TODO(altimofeev): ensure that WebUI is ready.
681 OobeDisplay* oobe_display = GetOobeUI();
682 return new WizardController(this, oobe_display);
685 void LoginDisplayHostImpl::OnBrowserCreated() {
686 // Close lock window now so that the launched browser can receive focus.
687 ResetLoginWindowAndView();
690 OobeUI* LoginDisplayHostImpl::GetOobeUI() const {
691 if (!login_view_)
692 return NULL;
693 return static_cast<OobeUI*>(login_view_->GetWebUI()->GetController());
696 ////////////////////////////////////////////////////////////////////////////////
697 // LoginDisplayHostImpl, content:NotificationObserver implementation:
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();
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();
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());
762 ////////////////////////////////////////////////////////////////////////////////
763 // LoginDisplayHostImpl, WebContentsObserver implementation:
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;
770 crash_count_++;
771 if (crash_count_ > kCrashCountLimit)
772 return;
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";
783 ////////////////////////////////////////////////////////////////////////////////
784 // LoginDisplayHostImpl, chromeos::SessionManagerClient::Observer
785 // implementation:
787 void LoginDisplayHostImpl::EmitLoginPromptVisibleCalled() {
788 OnLoginPromptVisible();
791 void LoginDisplayHostImpl::OnActiveOutputNodeChanged() {
792 TryToPlayStartupSound();
795 ////////////////////////////////////////////////////////////////////////////////
796 // LoginDisplayHostImpl, private
798 void LoginDisplayHostImpl::ShutdownDisplayHost(bool post_quit_task) {
799 if (shutting_down_)
800 return;
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();
808 if (!completion_callback_.is_null())
809 completion_callback_.Run();
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;
822 if (!CommandLine::ForCurrentProcess()->HasSwitch(
823 switches::kDisableLoginAnimations))
824 ash::Shell::GetInstance()->DoInitialWorkspaceAnimation();
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);
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;
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.
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";
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();
870 void LoginDisplayHostImpl::OnAutoEnrollmentClientDone() {
871 bool auto_enroll = auto_enrollment_client_->should_auto_enroll();
872 VLOG(1) << "OnAutoEnrollmentClientDone, decision is " << auto_enroll;
874 if (auto_enroll)
875 ForceAutoEnrollment();
877 auto_enrollment_check_done_ = true;
878 NotifyAutoEnrollmentCheckResult(auto_enroll);
881 void LoginDisplayHostImpl::ForceAutoEnrollment() {
882 if (sign_in_controller_.get())
883 sign_in_controller_->DoAutoEnrollment();
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);
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();
899 void LoginDisplayHostImpl::ShowWebUI() {
900 if (!login_window_ || !login_view_) {
901 NOTREACHED();
902 return;
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();
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();
916 // We should reset this flag to allow changing of status area visibility.
917 initialize_webui_hidden_ = false;
920 void LoginDisplayHostImpl::StartPostponedWebUI() {
921 if (!is_wallpaper_loaded_) {
922 NOTREACHED();
923 return;
925 LOG(WARNING) << "Login WebUI >> Init postponed WebUI";
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;
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;
952 void LoginDisplayHostImpl::InitLoginWindowAndView() {
953 if (login_window_)
954 return;
956 if (system::keyboard_settings::ForceKeyboardDrivenUINavigation()) {
957 views::FocusManager::set_arrow_key_traversal_enabled(true);
959 focus_ring_controller_.reset(new FocusRingController);
960 focus_ring_controller_->SetVisible(true);
962 keyboard_driven_oobe_key_handler_.reset(new KeyboardDrivenOobeKeyHandler);
965 views::Widget::InitParams params(
966 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
967 params.bounds = background_bounds();
968 params.show_state = ui::SHOW_STATE_FULLSCREEN;
969 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
970 params.parent =
971 ash::Shell::GetContainer(
972 ash::Shell::GetPrimaryRootWindow(),
973 ash::internal::kShellWindowId_LockScreenContainer);
975 login_window_ = new views::Widget;
976 login_window_->Init(params);
977 login_view_ = new WebUILoginView();
978 login_view_->Init();
979 if (login_view_->webui_visible())
980 OnLoginPromptVisible();
982 views::corewm::SetWindowHideAnimationDuration(
983 login_window_->GetNativeView(),
984 base::TimeDelta::FromMilliseconds(kLoginFadeoutTransitionDurationMs));
985 views::corewm::SetWindowVisibilityAnimationTransition(
986 login_window_->GetNativeView(),
987 views::corewm::ANIMATE_HIDE);
989 login_window_->SetContentsView(login_view_);
991 // If WebUI is initialized in hidden state, show it only if we're no
992 // longer waiting for wallpaper animation/user images loading. Otherwise,
993 // always show it.
994 if (!initialize_webui_hidden_ ||
995 (!waiting_for_wallpaper_load_ && !waiting_for_user_pods_)) {
996 LOG(WARNING) << "Login WebUI >> show login wnd on create";
997 login_window_->Show();
998 } else {
999 LOG(WARNING) << "Login WebUI >> login wnd is hidden on create";
1000 login_view_->set_is_hidden(true);
1002 login_window_->GetNativeView()->SetName("WebUILoginView");
1005 void LoginDisplayHostImpl::ResetLoginWindowAndView() {
1006 if (!login_window_)
1007 return;
1008 login_window_->Close();
1009 login_window_ = NULL;
1010 login_view_ = NULL;
1013 void LoginDisplayHostImpl::OnAuthPrewarmDone() {
1014 auth_prewarmer_.reset();
1017 void LoginDisplayHostImpl::SetOobeProgressBarVisible(bool visible) {
1018 GetOobeUI()->ShowOobeUI(visible);
1021 void LoginDisplayHostImpl::NotifyAutoEnrollmentCheckResult(
1022 bool should_auto_enroll) {
1023 std::vector<GetAutoEnrollmentCheckResultCallback> callbacks;
1024 callbacks.swap(get_auto_enrollment_result_callbacks_);
1025 for (size_t i = 0; i < callbacks.size(); ++i)
1026 callbacks[i].Run(should_auto_enroll);
1029 void LoginDisplayHostImpl::TryToPlayStartupSound() {
1030 if (startup_sound_played_ || login_prompt_visible_time_.is_null() ||
1031 !CrasAudioHandler::Get()->GetActiveOutputNode()) {
1032 return;
1035 startup_sound_played_ = true;
1037 // Don't try play startup sound if login prompt is already visible
1038 // for a long time or can't be played.
1039 if (base::TimeTicks::Now() - login_prompt_visible_time_ >
1040 base::TimeDelta::FromMilliseconds(kStartupSoundMaxDelayMs) ||
1041 !ash::PlaySystemSound(SOUND_STARTUP,
1042 startup_sound_honors_spoken_feedback_)) {
1043 EnableSystemSoundsForAccessibility();
1044 return;
1046 base::MessageLoop::current()->PostDelayedTask(
1047 FROM_HERE,
1048 base::Bind(&EnableSystemSoundsForAccessibility),
1049 media::SoundsManager::Get()->GetDuration(SOUND_STARTUP));
1052 void LoginDisplayHostImpl::OnLoginPromptVisible() {
1053 if (!login_prompt_visible_time_.is_null())
1054 return;
1055 login_prompt_visible_time_ = base::TimeTicks::Now();
1056 TryToPlayStartupSound();
1059 ////////////////////////////////////////////////////////////////////////////////
1060 // external
1062 // Declared in login_wizard.h so that others don't need to depend on our .h.
1063 // TODO(nkostylev): Split this into a smaller functions.
1064 void ShowLoginWizard(const std::string& first_screen_name) {
1065 if (browser_shutdown::IsTryingToQuit())
1066 return;
1068 VLOG(1) << "Showing OOBE screen: " << first_screen_name;
1070 chromeos::input_method::InputMethodManager* manager =
1071 chromeos::input_method::InputMethodManager::Get();
1073 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
1074 // and US dvorak keyboard layouts.
1075 if (g_browser_process && g_browser_process->local_state()) {
1076 manager->SetInputMethodDefault();
1078 PrefService* prefs = g_browser_process->local_state();
1079 // Apply owner preferences for tap-to-click and mouse buttons swap for
1080 // login screen.
1081 system::mouse_settings::SetPrimaryButtonRight(
1082 prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight));
1083 system::touchpad_settings::SetTapToClick(
1084 prefs->GetBoolean(prefs::kOwnerTapToClickEnabled));
1087 ui::SetNaturalScroll(CommandLine::ForCurrentProcess()->HasSwitch(
1088 switches::kNaturalScrollDefault));
1090 gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));
1092 // Check whether we need to execute OOBE process.
1093 bool oobe_complete = chromeos::StartupUtils::IsOobeCompleted();
1094 if (!oobe_complete) {
1095 LoginState::Get()->SetLoggedInState(
1096 LoginState::LOGGED_IN_OOBE, LoginState::LOGGED_IN_USER_NONE);
1097 } else {
1098 LoginState::Get()->SetLoggedInState(
1099 LoginState::LOGGED_IN_NONE, LoginState::LOGGED_IN_USER_NONE);
1102 LoginDisplayHost* display_host = new LoginDisplayHostImpl(screen_bounds);
1104 bool show_app_launch_splash_screen = (first_screen_name ==
1105 chromeos::WizardController::kAppLaunchSplashScreenName);
1106 if (show_app_launch_splash_screen) {
1107 const std::string& auto_launch_app_id =
1108 chromeos::KioskAppManager::Get()->GetAutoLaunchApp();
1109 display_host->StartAppLaunch(auto_launch_app_id);
1110 return;
1113 bool should_show_enrollment_screen =
1114 first_screen_name.empty() && oobe_complete &&
1115 chromeos::WizardController::ShouldAutoStartEnrollment() &&
1116 !g_browser_process->browser_policy_connector()->IsEnterpriseManaged();
1117 if (should_show_enrollment_screen) {
1118 // Shows networks screen instead of enrollment screen to resume the
1119 // interrupted auto start enrollment flow because enrollment screen does
1120 // not handle flaky network. See http://crbug.com/332572
1121 display_host->StartWizard(chromeos::WizardController::kNetworkScreenName,
1122 scoped_ptr<base::DictionaryValue>());
1123 return;
1126 bool show_login_screen =
1127 (first_screen_name.empty() && oobe_complete) ||
1128 first_screen_name == chromeos::WizardController::kLoginScreenName;
1130 if (show_login_screen) {
1131 // R11 > R12 migration fix. See http://crosbug.com/p/4898.
1132 // If user has manually changed locale during R11 OOBE, locale will be set.
1133 // On R12 > R12|R13 etc. this fix won't get activated since
1134 // OOBE process has set kApplicationLocale to non-default value.
1135 PrefService* prefs = g_browser_process->local_state();
1136 if (!prefs->HasPrefPath(prefs::kApplicationLocale)) {
1137 std::string locale = chromeos::StartupUtils::GetInitialLocale();
1138 prefs->SetString(prefs::kApplicationLocale, locale);
1139 manager->EnableLayouts(
1140 locale,
1141 manager->GetInputMethodUtil()->GetHardwareInputMethodId());
1142 base::ThreadRestrictions::ScopedAllowIO allow_io;
1143 const std::string loaded_locale =
1144 ResourceBundle::GetSharedInstance().ReloadLocaleResources(locale);
1145 g_browser_process->SetApplicationLocale(loaded_locale);
1147 display_host->StartSignInScreen(LoginScreenContext());
1148 return;
1151 // Load startup manifest.
1152 const chromeos::StartupCustomizationDocument* startup_manifest =
1153 chromeos::StartupCustomizationDocument::GetInstance();
1155 // Switch to initial locale if specified by customization
1156 // and has not been set yet. We cannot call
1157 // chromeos::LanguageSwitchMenu::SwitchLanguage here before
1158 // EmitLoginPromptReady.
1159 PrefService* prefs = g_browser_process->local_state();
1160 const std::string current_locale =
1161 prefs->GetString(prefs::kApplicationLocale);
1162 VLOG(1) << "Current locale: " << current_locale;
1163 std::string locale = startup_manifest->initial_locale();
1165 if (!current_locale.empty() || locale.empty()) {
1166 ShowLoginWizardFinish(first_screen_name, startup_manifest, display_host);
1167 return;
1170 std::string layout = startup_manifest->keyboard_layout();
1171 VLOG(1) << "Initial locale: " << locale << "keyboard layout " << layout;
1173 // Save initial locale from VPD/customization manifest as current
1174 // Chrome locale. Otherwise it will be lost if Chrome restarts.
1175 // Don't need to schedule pref save because setting initial local
1176 // will enforce preference saving.
1177 prefs->SetString(prefs::kApplicationLocale, locale);
1178 chromeos::StartupUtils::SetInitialLocale(locale);
1180 // Determine keyboard layout from OEM customization (if provided) or
1181 // initial locale and save it in preferences.
1182 DetermineAndSaveHardwareKeyboard(locale, layout);
1184 // Then, enable the hardware keyboard.
1185 manager->EnableLayouts(
1186 locale, manager->GetInputMethodUtil()->GetHardwareInputMethodId());
1188 scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> data(
1189 new ShowLoginWizardSwitchLanguageCallbackData(
1190 first_screen_name, startup_manifest, display_host));
1192 scoped_ptr<locale_util::SwitchLanguageCallback> callback(
1193 new locale_util::SwitchLanguageCallback(
1194 base::Bind(&OnLanguageSwitchedCallback, base::Passed(data.Pass()))));
1196 // Do not load locale keyboards here.
1197 locale_util::SwitchLanguage(locale, false, callback.Pass());
1200 } // namespace chromeos