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"
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"
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
) {
128 if (!oem_layout
.empty()) {
129 // If the OEM layout information is provided, use it.
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(
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
{
162 explicit AnimationObserver(const base::Closure
& callback
)
163 : callback_(callback
) {}
164 virtual ~AnimationObserver() {}
167 // ui::ImplicitAnimationObserver implementation:
168 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
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
) {
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);
242 LoginDisplayHost
* LoginDisplayHostImpl::default_host_
= NULL
;
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),
258 webui_login_display_(NULL
),
259 is_showing_login_(false),
260 is_wallpaper_loaded_(false),
261 status_area_saved_visibility_(false),
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.
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.
284 chrome::NOTIFICATION_BROWSER_OPENED
,
285 content::NotificationService::AllSources());
287 // Login screen is moved to lock screen container when user logs in.
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_
) {
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_
) {
348 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
349 content::NotificationService::AllSources());
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 {
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_
) {
421 ShutdownDisplayHost(false);
423 case ANIMATION_WORKSPACE
:
424 if (ash::Shell::HasInstance())
425 ScheduleWorkspaceAnimation();
427 ShutdownDisplayHost(false);
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();
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() {
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;
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());
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());
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";
506 LOG(WARNING
) << "Login WebUI >> wizard";
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";
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";
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: "
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",
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
;
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 {
693 return static_cast<OobeUI
*>(login_view_
->GetWebUI()->GetController());
696 ////////////////////////////////////////////////////////////////////////////////
697 // LoginDisplayHostImpl, content:NotificationObserver implementation:
699 void LoginDisplayHostImpl::Observe(
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_
)
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;
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;
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.
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
)
771 if (crash_count_
> kCrashCountLimit
)
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
787 void LoginDisplayHostImpl::EmitLoginPromptVisibleCalled() {
788 OnLoginPromptVisible();
791 void LoginDisplayHostImpl::OnActiveOutputNodeChanged() {
792 TryToPlayStartupSound();
795 ////////////////////////////////////////////////////////////////////////////////
796 // LoginDisplayHostImpl, private
798 void LoginDisplayHostImpl::ShutdownDisplayHost(bool post_quit_task
) {
802 shutting_down_
= true;
803 registrar_
.RemoveAll();
804 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
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.
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(),
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);
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
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();
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
;
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
895 // See crbug.com/328538.
896 ChargerReplacementDialog::SetFocusOnChargerDialogIfVisible();
899 void LoginDisplayHostImpl::ShowWebUI() {
900 if (!login_window_
|| !login_view_
) {
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
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_
) {
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
) {
935 switch (restore_path_
) {
937 StartWizard(wizard_first_screen_name_
,
938 wizard_screen_parameters_
.Pass());
940 case RESTORE_SIGN_IN
:
941 StartSignInScreen(LoginScreenContext());
943 case RESTORE_ADD_USER_INTO_SESSION
:
944 StartUserAdding(completion_callback_
);
952 void LoginDisplayHostImpl::InitLoginWindowAndView() {
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
;
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();
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,
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();
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() {
1008 login_window_
->Close();
1009 login_window_
= 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()) {
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();
1046 base::MessageLoop::current()->PostDelayedTask(
1048 base::Bind(&EnableSystemSoundsForAccessibility
),
1049 media::SoundsManager::Get()->GetDuration(SOUND_STARTUP
));
1052 void LoginDisplayHostImpl::OnLoginPromptVisible() {
1053 if (!login_prompt_visible_time_
.is_null())
1055 login_prompt_visible_time_
= base::TimeTicks::Now();
1056 TryToPlayStartupSound();
1059 ////////////////////////////////////////////////////////////////////////////////
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())
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
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
);
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
);
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
>());
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(
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());
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
);
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