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/logging.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/strings/string_split.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/trace_event/trace_event.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_recorder.h"
30 #include "chrome/browser/chromeos/first_run/drive_first_run_controller.h"
31 #include "chrome/browser/chromeos/first_run/first_run.h"
32 #include "chrome/browser/chromeos/input_method/input_method_util.h"
33 #include "chrome/browser/chromeos/language_preferences.h"
34 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
35 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
36 #include "chrome/browser/chromeos/login/existing_user_controller.h"
37 #include "chrome/browser/chromeos/login/helper.h"
38 #include "chrome/browser/chromeos/login/login_wizard.h"
39 #include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
40 #include "chrome/browser/chromeos/login/signin/token_handle_util.h"
41 #include "chrome/browser/chromeos/login/startup_utils.h"
42 #include "chrome/browser/chromeos/login/ui/input_events_blocker.h"
43 #include "chrome/browser/chromeos/login/ui/keyboard_driven_oobe_key_handler.h"
44 #include "chrome/browser/chromeos/login/ui/oobe_display.h"
45 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
46 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
47 #include "chrome/browser/chromeos/login/wizard_controller.h"
48 #include "chrome/browser/chromeos/mobile_config.h"
49 #include "chrome/browser/chromeos/net/delay_network_call.h"
50 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
51 #include "chrome/browser/chromeos/policy/enrollment_config.h"
52 #include "chrome/browser/chromeos/settings/cros_settings.h"
53 #include "chrome/browser/chromeos/system/device_disabling_manager.h"
54 #include "chrome/browser/chromeos/system/input_device_settings.h"
55 #include "chrome/browser/chromeos/system/timezone_util.h"
56 #include "chrome/browser/chromeos/ui/focus_ring_controller.h"
57 #include "chrome/browser/lifetime/application_lifetime.h"
58 #include "chrome/browser/profiles/profile_manager.h"
59 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
60 #include "chrome/common/chrome_constants.h"
61 #include "chrome/common/chrome_switches.h"
62 #include "chrome/common/pref_names.h"
63 #include "chrome/grit/browser_resources.h"
64 #include "chromeos/audio/chromeos_sounds.h"
65 #include "chromeos/chromeos_constants.h"
66 #include "chromeos/chromeos_switches.h"
67 #include "chromeos/dbus/dbus_thread_manager.h"
68 #include "chromeos/dbus/session_manager_client.h"
69 #include "chromeos/login/login_state.h"
70 #include "chromeos/settings/cros_settings_names.h"
71 #include "chromeos/settings/cros_settings_provider.h"
72 #include "chromeos/settings/timezone_settings.h"
73 #include "chromeos/timezone/timezone_resolver.h"
74 #include "components/session_manager/core/session_manager.h"
75 #include "components/user_manager/user.h"
76 #include "components/user_manager/user_manager.h"
77 #include "content/public/browser/notification_service.h"
78 #include "content/public/browser/notification_types.h"
79 #include "content/public/browser/web_contents.h"
80 #include "content/public/browser/web_ui.h"
81 #include "media/audio/sounds/sounds_manager.h"
82 #include "ui/aura/window.h"
83 #include "ui/base/ime/chromeos/extension_ime_util.h"
84 #include "ui/base/ime/chromeos/input_method_manager.h"
85 #include "ui/base/resource/resource_bundle.h"
86 #include "ui/compositor/layer.h"
87 #include "ui/compositor/layer_animation_observer.h"
88 #include "ui/compositor/scoped_layer_animation_settings.h"
89 #include "ui/events/event_utils.h"
90 #include "ui/gfx/display.h"
91 #include "ui/gfx/geometry/rect.h"
92 #include "ui/gfx/geometry/size.h"
93 #include "ui/gfx/screen.h"
94 #include "ui/gfx/transform.h"
95 #include "ui/keyboard/keyboard_controller.h"
96 #include "ui/keyboard/keyboard_util.h"
97 #include "ui/views/focus/focus_manager.h"
98 #include "ui/views/widget/widget.h"
99 #include "ui/views/widget/widget_delegate.h"
100 #include "url/gurl.h"
104 // Maximum delay for startup sound after 'loginPromptVisible' signal.
105 const int kStartupSoundMaxDelayMs
= 2000;
107 // URL which corresponds to the login WebUI.
108 const char kLoginURL
[] = "chrome://oobe/login";
110 // URL which corresponds to the OOBE WebUI.
111 const char kOobeURL
[] = "chrome://oobe/oobe";
113 // URL which corresponds to the user adding WebUI.
114 const char kUserAddingURL
[] = "chrome://oobe/user-adding";
116 // URL which corresponds to the app launch splash WebUI.
117 const char kAppLaunchSplashURL
[] = "chrome://oobe/app-launch-splash";
119 // Duration of sign-in transition animation.
120 const int kLoginFadeoutTransitionDurationMs
= 700;
122 // Number of times we try to reload OOBE/login WebUI if it crashes.
123 const int kCrashCountLimit
= 5;
125 // Whether to enable tnitializing WebUI in hidden state (see
126 // |initialize_webui_hidden_|) by default.
127 const bool kHiddenWebUIInitializationDefault
= true;
129 // Switch values that might be used to override WebUI init type.
130 const char kWebUIInitParallel
[] = "parallel";
131 const char kWebUIInitPostpone
[] = "postpone";
133 // The delay of triggering initialization of the device policy subsystem
134 // after the login screen is initialized. This makes sure that device policy
135 // network requests are made while the system is idle waiting for user input.
136 const int64 kPolicyServiceInitializationDelayMilliseconds
= 100;
138 // A class to observe an implicit animation and invokes the callback after the
139 // animation is completed.
140 class AnimationObserver
: public ui::ImplicitAnimationObserver
{
142 explicit AnimationObserver(const base::Closure
& callback
)
143 : callback_(callback
) {}
144 ~AnimationObserver() override
{}
147 // ui::ImplicitAnimationObserver implementation:
148 void OnImplicitAnimationsCompleted() override
{
153 base::Closure callback_
;
155 DISALLOW_COPY_AND_ASSIGN(AnimationObserver
);
158 // ShowLoginWizard is split into two parts. This function is sometimes called
159 // from ShowLoginWizard(), and sometimes from OnLanguageSwitchedCallback()
160 // (if locale was updated).
161 void ShowLoginWizardFinish(
162 const std::string
& first_screen_name
,
163 const chromeos::StartupCustomizationDocument
* startup_manifest
,
164 chromeos::LoginDisplayHost
* display_host
) {
165 TRACE_EVENT0("chromeos", "ShowLoginWizard::ShowLoginWizardFinish");
167 display_host
->StartWizard(first_screen_name
);
169 // Set initial timezone if specified by customization.
170 const std::string timezone_name
= startup_manifest
->initial_timezone();
171 VLOG(1) << "Initial time zone: " << timezone_name
;
172 // Apply locale customizations only once to preserve whatever locale
173 // user has changed to during OOBE.
174 if (!timezone_name
.empty()) {
175 chromeos::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
176 base::UTF8ToUTF16(timezone_name
));
180 struct ShowLoginWizardSwitchLanguageCallbackData
{
181 explicit ShowLoginWizardSwitchLanguageCallbackData(
182 const std::string
& first_screen_name
,
183 const chromeos::StartupCustomizationDocument
* startup_manifest
,
184 chromeos::LoginDisplayHost
* display_host
)
185 : first_screen_name(first_screen_name
),
186 startup_manifest(startup_manifest
),
187 display_host(display_host
) {}
189 const std::string first_screen_name
;
190 const chromeos::StartupCustomizationDocument
* const startup_manifest
;
191 chromeos::LoginDisplayHost
* const display_host
;
193 // lock UI while resource bundle is being reloaded.
194 chromeos::InputEventsBlocker events_blocker
;
197 void OnLanguageSwitchedCallback(
198 scoped_ptr
<ShowLoginWizardSwitchLanguageCallbackData
> self
,
199 const chromeos::locale_util::LanguageSwitchResult
& result
) {
201 LOG(WARNING
) << "Locale could not be found for '" << result
.requested_locale
204 ShowLoginWizardFinish(
205 self
->first_screen_name
, self
->startup_manifest
, self
->display_host
);
208 void EnableSystemSoundsForAccessibility() {
209 chromeos::AccessibilityManager::Get()->EnableSystemSounds(true);
212 // A login implementation of WidgetDelegate.
213 class LoginWidgetDelegate
: public views::WidgetDelegate
{
215 explicit LoginWidgetDelegate(views::Widget
* widget
) : widget_(widget
) {
217 ~LoginWidgetDelegate() override
{}
219 // Overridden from WidgetDelegate:
220 void DeleteDelegate() override
{ delete this; }
221 views::Widget
* GetWidget() override
{ return widget_
; }
222 const views::Widget
* GetWidget() const override
{ return widget_
; }
223 bool CanActivate() const override
{ return true; }
224 bool ShouldAdvanceFocusToTopLevelWidget() const override
{ return true; }
227 views::Widget
* widget_
;
229 DISALLOW_COPY_AND_ASSIGN(LoginWidgetDelegate
);
232 // Disables virtual keyboard overscroll. Login UI will scroll user pods
233 // into view on JS side when virtual keyboard is shown.
234 void DisableKeyboardOverscroll() {
235 keyboard::SetKeyboardOverscrollOverride(
236 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED
);
239 void ResetKeyboardOverscrollOverride() {
240 keyboard::SetKeyboardOverscrollOverride(
241 keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE
);
249 LoginDisplayHost
* LoginDisplayHostImpl::default_host_
= NULL
;
252 const int LoginDisplayHostImpl::kShowLoginWebUIid
= 0x1111;
254 ////////////////////////////////////////////////////////////////////////////////
255 // LoginDisplayHostImpl, public
257 LoginDisplayHostImpl::LoginDisplayHostImpl(const gfx::Rect
& background_bounds
)
258 : background_bounds_(background_bounds
),
259 shutting_down_(false),
260 oobe_progress_bar_visible_(false),
261 session_starting_(false),
264 webui_login_display_(NULL
),
265 is_showing_login_(false),
266 is_wallpaper_loaded_(false),
267 status_area_saved_visibility_(false),
269 restore_path_(RESTORE_UNKNOWN
),
270 finalize_animation_type_(ANIMATION_WORKSPACE
),
271 startup_sound_played_(false),
272 startup_sound_honors_spoken_feedback_(false),
273 is_observing_keyboard_(false),
274 pointer_factory_(this),
275 animation_weak_ptr_factory_(this) {
276 DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this);
277 CrasAudioHandler::Get()->AddAudioObserver(this);
278 if (keyboard::KeyboardController::GetInstance()) {
279 keyboard::KeyboardController::GetInstance()->AddObserver(this);
280 is_observing_keyboard_
= true;
283 ash::Shell::GetInstance()->delegate()->AddVirtualKeyboardStateObserver(this);
284 ash::Shell::GetScreen()->AddObserver(this);
286 // We need to listen to CLOSE_ALL_BROWSERS_REQUEST but not APP_TERMINATING
287 // because/ APP_TERMINATING will never be fired as long as this keeps
288 // ref-count. CLOSE_ALL_BROWSERS_REQUEST is safe here because there will be no
289 // browser instance that will block the shutdown.
291 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST
,
292 content::NotificationService::AllSources());
294 // NOTIFICATION_BROWSER_OPENED is issued after browser is created, but
295 // not shown yet. Lock window has to be closed at this point so that
296 // a browser window exists and the window can acquire input focus.
298 chrome::NOTIFICATION_BROWSER_OPENED
,
299 content::NotificationService::AllSources());
301 // Login screen is moved to lock screen container when user logs in.
303 chrome::NOTIFICATION_LOGIN_USER_CHANGED
,
304 content::NotificationService::AllSources());
306 DCHECK(default_host_
== NULL
);
307 default_host_
= this;
309 // Make sure chrome won't exit while we are at login/oobe screen.
310 chrome::IncrementKeepAliveCount();
312 bool is_registered
= StartupUtils::IsDeviceRegistered();
313 bool zero_delay_enabled
= WizardController::IsZeroDelayEnabled();
314 bool disable_boot_animation
=
315 base::CommandLine::ForCurrentProcess()->HasSwitch(
316 switches::kDisableBootAnimation
);
318 waiting_for_wallpaper_load_
= !zero_delay_enabled
&&
319 (!is_registered
|| !disable_boot_animation
);
321 // For slower hardware we have boot animation disabled so
322 // we'll be initializing WebUI hidden, waiting for user pods to load and then
323 // show WebUI at once.
324 waiting_for_user_pods_
= !zero_delay_enabled
&& !waiting_for_wallpaper_load_
;
326 initialize_webui_hidden_
=
327 kHiddenWebUIInitializationDefault
&& !zero_delay_enabled
;
329 // Check if WebUI init type is overriden.
330 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
331 switches::kAshWebUIInit
)) {
332 const std::string override_type
=
333 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
334 switches::kAshWebUIInit
);
335 if (override_type
== kWebUIInitParallel
)
336 initialize_webui_hidden_
= true;
337 else if (override_type
== kWebUIInitPostpone
)
338 initialize_webui_hidden_
= false;
341 // Always postpone WebUI initialization on first boot, otherwise we miss
342 // initial animation.
343 if (!StartupUtils::IsOobeCompleted())
344 initialize_webui_hidden_
= false;
346 if (waiting_for_wallpaper_load_
) {
348 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED
,
349 content::NotificationService::AllSources());
352 // When we wait for WebUI to be initialized we wait for one of
353 // these notifications.
354 if ((waiting_for_user_pods_
|| waiting_for_wallpaper_load_
) &&
355 initialize_webui_hidden_
) {
357 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
358 content::NotificationService::AllSources());
360 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN
,
361 content::NotificationService::AllSources());
363 VLOG(1) << "Login WebUI >> "
364 << "zero_delay: " << zero_delay_enabled
365 << " wait_for_wp_load_: " << waiting_for_wallpaper_load_
366 << " wait_for_pods_: " << waiting_for_user_pods_
367 << " init_webui_hidden_: " << initialize_webui_hidden_
;
369 media::SoundsManager
* manager
= media::SoundsManager::Get();
370 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
371 manager
->Initialize(chromeos::SOUND_STARTUP
,
372 bundle
.GetRawDataResource(IDR_SOUND_STARTUP_WAV
));
374 // Disable Drag'n'Drop for the login session.
375 scoped_drag_drop_disabler_
.reset(new aura::client::ScopedDragDropDisabler(
376 ash::Shell::GetPrimaryRootWindow()));
379 LoginDisplayHostImpl::~LoginDisplayHostImpl() {
380 DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
381 CrasAudioHandler::Get()->RemoveAudioObserver(this);
382 if (keyboard::KeyboardController::GetInstance() && is_observing_keyboard_
) {
383 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
384 is_observing_keyboard_
= false;
387 ash::Shell::GetInstance()->delegate()->
388 RemoveVirtualKeyboardStateObserver(this);
389 ash::Shell::GetScreen()->RemoveObserver(this);
391 if (login_view_
&& login_window_
)
392 login_window_
->RemoveRemovalsObserver(this);
394 ResetKeyboardOverscrollOverride();
396 views::FocusManager::set_arrow_key_traversal_enabled(false);
397 ResetLoginWindowAndView();
399 // Let chrome process exit after login/oobe screen if needed.
400 chrome::DecrementKeepAliveCount();
402 default_host_
= NULL
;
403 // TODO(tengs): This should be refactored. See crbug.com/314934.
404 if (user_manager::UserManager::Get()->IsCurrentUserNew()) {
405 // DriveOptInController will delete itself when finished.
406 (new DriveFirstRunController(
407 ProfileManager::GetActiveUserProfile()))->EnableOfflineMode();
411 ////////////////////////////////////////////////////////////////////////////////
412 // LoginDisplayHostImpl, LoginDisplayHost implementation:
414 LoginDisplay
* LoginDisplayHostImpl::CreateLoginDisplay(
415 LoginDisplay::Delegate
* delegate
) {
416 webui_login_display_
= new WebUILoginDisplay(delegate
);
417 webui_login_display_
->set_background_bounds(background_bounds());
418 return webui_login_display_
;
421 gfx::NativeWindow
LoginDisplayHostImpl::GetNativeWindow() const {
422 return login_window_
? login_window_
->GetNativeWindow() : NULL
;
425 WebUILoginView
* LoginDisplayHostImpl::GetWebUILoginView() const {
429 void LoginDisplayHostImpl::BeforeSessionStart() {
430 session_starting_
= true;
433 void LoginDisplayHostImpl::Finalize() {
434 DVLOG(1) << "Session starting";
435 if (ash::Shell::HasInstance()) {
436 ash::Shell::GetInstance()->
437 desktop_background_controller()->MoveDesktopToUnlockedContainer();
439 if (wizard_controller_
.get())
440 wizard_controller_
->OnSessionStart();
442 switch (finalize_animation_type_
) {
444 ShutdownDisplayHost(false);
446 case ANIMATION_WORKSPACE
:
447 if (ash::Shell::HasInstance())
448 ScheduleWorkspaceAnimation();
450 ShutdownDisplayHost(false);
452 case ANIMATION_FADE_OUT
:
453 // Display host is deleted once animation is completed
454 // since sign in screen widget has to stay alive.
455 ScheduleFadeOutAnimation();
460 void LoginDisplayHostImpl::OnCompleteLogin() {
461 if (auto_enrollment_controller_
)
462 auto_enrollment_controller_
->Cancel();
465 void LoginDisplayHostImpl::OpenProxySettings() {
467 login_view_
->OpenProxySettings();
470 void LoginDisplayHostImpl::SetStatusAreaVisible(bool visible
) {
471 if (initialize_webui_hidden_
)
472 status_area_saved_visibility_
= visible
;
473 else if (login_view_
)
474 login_view_
->SetStatusAreaVisible(visible
);
477 AutoEnrollmentController
* LoginDisplayHostImpl::GetAutoEnrollmentController() {
478 if (!auto_enrollment_controller_
)
479 auto_enrollment_controller_
.reset(new AutoEnrollmentController());
480 return auto_enrollment_controller_
.get();
483 void LoginDisplayHostImpl::StartWizard(const std::string
& first_screen_name
) {
484 DisableKeyboardOverscroll();
486 startup_sound_honors_spoken_feedback_
= false;
487 TryToPlayStartupSound();
489 // Keep parameters to restore if renderer crashes.
490 restore_path_
= RESTORE_WIZARD
;
491 first_screen_name_
= first_screen_name
;
492 is_showing_login_
= false;
494 if (waiting_for_wallpaper_load_
&& !initialize_webui_hidden_
) {
495 VLOG(1) << "Login WebUI >> wizard postponed";
498 VLOG(1) << "Login WebUI >> wizard";
501 LoadURL(GURL(kOobeURL
));
503 DVLOG(1) << "Starting wizard, first_screen_name: " << first_screen_name
;
504 // Create and show the wizard.
505 // Note, dtor of the old WizardController should be called before ctor of the
506 // new one, because "default_controller()" is updated there. So pure "reset()"
507 // is done before new controller creation.
508 wizard_controller_
.reset();
509 wizard_controller_
.reset(CreateWizardController());
511 oobe_progress_bar_visible_
= !StartupUtils::IsDeviceRegistered();
512 SetOobeProgressBarVisible(oobe_progress_bar_visible_
);
513 wizard_controller_
->Init(first_screen_name
);
516 WizardController
* LoginDisplayHostImpl::GetWizardController() {
517 return wizard_controller_
.get();
520 AppLaunchController
* LoginDisplayHostImpl::GetAppLaunchController() {
521 return app_launch_controller_
.get();
524 void LoginDisplayHostImpl::StartUserAdding(
525 const base::Closure
& completion_callback
) {
526 DisableKeyboardOverscroll();
528 restore_path_
= RESTORE_ADD_USER_INTO_SESSION
;
529 completion_callback_
= completion_callback
;
530 finalize_animation_type_
= ANIMATION_NONE
;
531 VLOG(1) << "Login WebUI >> user adding";
533 LoadURL(GURL(kUserAddingURL
));
534 // We should emit this signal only at login screen (after reboot or sign out).
535 login_view_
->set_should_emit_login_prompt_visible(false);
537 // Lock container can be transparent after lock screen animation.
538 aura::Window
* lock_container
= ash::Shell::GetContainer(
539 ash::Shell::GetPrimaryRootWindow(),
540 ash::kShellWindowId_LockScreenContainersContainer
);
541 lock_container
->layer()->SetOpacity(1.0);
543 ash::Shell::GetInstance()->
544 desktop_background_controller()->MoveDesktopToLockedContainer();
546 existing_user_controller_
.reset(); // Only one controller in a time.
547 existing_user_controller_
.reset(new chromeos::ExistingUserController(this));
549 if (!signin_screen_controller_
.get()) {
550 OobeDisplay
* oobe_display
= GetOobeUI();
551 signin_screen_controller_
.reset(new SignInScreenController(
552 oobe_display
, webui_login_display_
->delegate()));
555 SetOobeProgressBarVisible(oobe_progress_bar_visible_
= false);
556 SetStatusAreaVisible(true);
557 existing_user_controller_
->Init(
558 user_manager::UserManager::Get()->GetUsersAllowedForMultiProfile());
559 CHECK(webui_login_display_
);
560 GetOobeUI()->ShowSigninScreen(LoginScreenContext(),
561 webui_login_display_
,
562 webui_login_display_
);
565 void LoginDisplayHostImpl::StartSignInScreen(
566 const LoginScreenContext
& context
) {
567 DisableKeyboardOverscroll();
569 startup_sound_honors_spoken_feedback_
= true;
570 TryToPlayStartupSound();
572 restore_path_
= RESTORE_SIGN_IN
;
573 is_showing_login_
= true;
574 finalize_animation_type_
= ANIMATION_WORKSPACE
;
576 PrewarmAuthentication();
578 if (waiting_for_wallpaper_load_
&& !initialize_webui_hidden_
) {
579 VLOG(1) << "Login WebUI >> sign in postponed";
582 VLOG(1) << "Login WebUI >> sign in";
584 if (!login_window_
) {
585 TRACE_EVENT_ASYNC_BEGIN0("ui", "ShowLoginWebUI", kShowLoginWebUIid
);
586 TRACE_EVENT_ASYNC_STEP_INTO0(
587 "ui", "ShowLoginWebUI", kShowLoginWebUIid
, "StartSignInScreen");
588 BootTimesRecorder::Get()->RecordCurrentStats("login-start-signin-screen");
589 LoadURL(GURL(kLoginURL
));
592 DVLOG(1) << "Starting sign in screen";
593 const user_manager::UserList
& users
=
594 user_manager::UserManager::Get()->GetUsers();
596 // Fix for users who updated device and thus never passed register screen.
597 // If we already have users, we assume that it is not a second part of
598 // OOBE. See http://crosbug.com/6289
599 if (!StartupUtils::IsDeviceRegistered() && !users
.empty()) {
600 VLOG(1) << "Mark device registered because there are remembered users: "
602 StartupUtils::MarkDeviceRegistered(base::Closure());
605 existing_user_controller_
.reset(); // Only one controller in a time.
606 existing_user_controller_
.reset(new chromeos::ExistingUserController(this));
608 if (!signin_screen_controller_
.get()) {
609 OobeDisplay
* oobe_display
= GetOobeUI();
610 signin_screen_controller_
.reset(new SignInScreenController(
611 oobe_display
, webui_login_display_
->delegate()));
614 oobe_progress_bar_visible_
= !StartupUtils::IsDeviceRegistered();
615 SetOobeProgressBarVisible(oobe_progress_bar_visible_
);
616 SetStatusAreaVisible(true);
617 existing_user_controller_
->Init(users
);
619 // We might be here after a reboot that was triggered after OOBE was complete,
620 // so check for auto-enrollment again. This might catch a cached decision from
621 // a previous oobe flow, or might start a new check with the server.
622 GetAutoEnrollmentController()->Start();
624 // Initiate mobile config load.
625 MobileConfig::GetInstance();
627 // Initiate device policy fetching.
628 policy::BrowserPolicyConnectorChromeOS
* connector
=
629 g_browser_process
->platform_part()->browser_policy_connector_chromeos();
630 connector
->ScheduleServiceInitialization(
631 kPolicyServiceInitializationDelayMilliseconds
);
633 CHECK(webui_login_display_
);
634 GetOobeUI()->ShowSigninScreen(context
,
635 webui_login_display_
,
636 webui_login_display_
);
637 TRACE_EVENT_ASYNC_STEP_INTO0("ui",
640 "WaitForScreenStateInitialize");
641 BootTimesRecorder::Get()->RecordCurrentStats(
642 "login-wait-for-signin-state-initialize");
645 void LoginDisplayHostImpl::OnPreferencesChanged() {
646 if (is_showing_login_
)
647 webui_login_display_
->OnPreferencesChanged();
650 void LoginDisplayHostImpl::PrewarmAuthentication() {
651 auth_prewarmer_
.reset(new AuthPrewarmer());
652 auth_prewarmer_
->PrewarmAuthentication(
653 base::Bind(&LoginDisplayHostImpl::OnAuthPrewarmDone
,
654 pointer_factory_
.GetWeakPtr()));
657 void LoginDisplayHostImpl::StartDemoAppLaunch() {
658 VLOG(1) << "Login WebUI >> starting demo app.";
659 SetStatusAreaVisible(false);
661 demo_app_launcher_
.reset(new DemoAppLauncher());
662 demo_app_launcher_
->StartDemoAppLaunch();
665 void LoginDisplayHostImpl::StartAppLaunch(const std::string
& app_id
,
666 bool diagnostic_mode
,
668 VLOG(1) << "Login WebUI >> start app launch.";
669 SetStatusAreaVisible(false);
671 // Wait for the |CrosSettings| to become either trusted or permanently
673 const CrosSettingsProvider::TrustedStatus status
=
674 CrosSettings::Get()->PrepareTrustedValues(base::Bind(
675 &LoginDisplayHostImpl::StartAppLaunch
,
676 pointer_factory_
.GetWeakPtr(),
680 if (status
== CrosSettingsProvider::TEMPORARILY_UNTRUSTED
)
683 if (status
== CrosSettingsProvider::PERMANENTLY_UNTRUSTED
) {
684 // If the |CrosSettings| are permanently untrusted, refuse to launch a
685 // single-app kiosk mode session.
686 LOG(ERROR
) << "Login WebUI >> Refusing to launch single-app kiosk mode.";
687 SetStatusAreaVisible(true);
691 bool device_disabled
= false;
692 CrosSettings::Get()->GetBoolean(kDeviceDisabled
, &device_disabled
);
693 if (device_disabled
&& system::DeviceDisablingManager::
694 HonorDeviceDisablingDuringNormalOperation()) {
695 // If the device is disabled, bail out. A device disabled screen will be
696 // shown by the DeviceDisablingManager.
700 finalize_animation_type_
= ANIMATION_FADE_OUT
;
702 LoadURL(GURL(kAppLaunchSplashURL
));
704 login_view_
->set_should_emit_login_prompt_visible(false);
706 app_launch_controller_
.reset(new AppLaunchController(
707 app_id
, diagnostic_mode
, this, GetOobeUI()));
709 app_launch_controller_
->StartAppLaunch(auto_launch
);
712 ////////////////////////////////////////////////////////////////////////////////
713 // LoginDisplayHostImpl, public
715 WizardController
* LoginDisplayHostImpl::CreateWizardController() {
716 // TODO(altimofeev): ensure that WebUI is ready.
717 OobeDisplay
* oobe_display
= GetOobeUI();
718 return new WizardController(this, oobe_display
);
721 void LoginDisplayHostImpl::OnBrowserCreated() {
722 // Close lock window now so that the launched browser can receive focus.
723 ResetLoginWindowAndView();
726 OobeUI
* LoginDisplayHostImpl::GetOobeUI() const {
729 return static_cast<OobeUI
*>(login_view_
->GetWebUI()->GetController());
732 ////////////////////////////////////////////////////////////////////////////////
733 // LoginDisplayHostImpl, content:NotificationObserver implementation:
735 void LoginDisplayHostImpl::Observe(
737 const content::NotificationSource
& source
,
738 const content::NotificationDetails
& details
) {
739 if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
== type
||
740 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN
== type
) {
741 VLOG(1) << "Login WebUI >> WEBUI_VISIBLE";
742 if (waiting_for_user_pods_
&& initialize_webui_hidden_
) {
743 waiting_for_user_pods_
= false;
745 } else if (waiting_for_wallpaper_load_
&& initialize_webui_hidden_
) {
746 // Reduce time till login UI is shown - show it as soon as possible.
747 waiting_for_wallpaper_load_
= false;
750 registrar_
.Remove(this,
751 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
752 content::NotificationService::AllSources());
753 registrar_
.Remove(this,
754 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN
,
755 content::NotificationService::AllSources());
756 } else if (type
== chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST
) {
757 ShutdownDisplayHost(true);
758 } else if (type
== chrome::NOTIFICATION_BROWSER_OPENED
&& session_starting_
) {
759 // Browsers created before session start (windows opened by extensions, for
760 // example) are ignored.
762 registrar_
.Remove(this,
763 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST
,
764 content::NotificationService::AllSources());
765 registrar_
.Remove(this,
766 chrome::NOTIFICATION_BROWSER_OPENED
,
767 content::NotificationService::AllSources());
768 } else if (type
== chrome::NOTIFICATION_LOGIN_USER_CHANGED
&&
769 user_manager::UserManager::Get()->IsCurrentUserNew()) {
770 // For new user, move desktop to locker container so that windows created
771 // during the user image picker step are below it.
772 ash::Shell::GetInstance()->
773 desktop_background_controller()->MoveDesktopToLockedContainer();
774 registrar_
.Remove(this,
775 chrome::NOTIFICATION_LOGIN_USER_CHANGED
,
776 content::NotificationService::AllSources());
777 } else if (chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED
== type
) {
778 VLOG(1) << "Login WebUI >> wp animation done";
779 is_wallpaper_loaded_
= true;
780 ash::Shell::GetInstance()->user_wallpaper_delegate()
781 ->OnWallpaperBootAnimationFinished();
782 if (waiting_for_wallpaper_load_
) {
783 // StartWizard / StartSignInScreen could be called multiple times through
784 // the lifetime of host.
785 // Make sure that subsequent calls are not postponed.
786 waiting_for_wallpaper_load_
= false;
787 if (initialize_webui_hidden_
)
790 StartPostponedWebUI();
792 registrar_
.Remove(this,
793 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED
,
794 content::NotificationService::AllSources());
798 ////////////////////////////////////////////////////////////////////////////////
799 // LoginDisplayHostImpl, WebContentsObserver implementation:
801 void LoginDisplayHostImpl::RenderProcessGone(base::TerminationStatus status
) {
802 // Do not try to restore on shutdown
803 if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID
)
807 if (crash_count_
> kCrashCountLimit
)
810 if (status
!= base::TERMINATION_STATUS_NORMAL_TERMINATION
) {
811 // Render with login screen crashed. Let's crash browser process to let
812 // session manager restart it properly. It is hard to reload the page
813 // and get to controlled state that is fully functional.
814 // If you see check, search for renderer crash for the same client.
815 LOG(FATAL
) << "Renderer crash on login window";
819 ////////////////////////////////////////////////////////////////////////////////
820 // LoginDisplayHostImpl, chromeos::SessionManagerClient::Observer
823 void LoginDisplayHostImpl::EmitLoginPromptVisibleCalled() {
824 OnLoginPromptVisible();
827 ////////////////////////////////////////////////////////////////////////////////
828 // LoginDisplayHostImpl, chromeos::CrasAudioHandler::AudioObserver
831 void LoginDisplayHostImpl::OnActiveOutputNodeChanged() {
832 TryToPlayStartupSound();
835 ////////////////////////////////////////////////////////////////////////////////
836 // LoginDisplayHostImpl, ash::KeyboardStateObserver:
839 void LoginDisplayHostImpl::OnVirtualKeyboardStateChanged(bool activated
) {
840 if (keyboard::KeyboardController::GetInstance()) {
842 if (!is_observing_keyboard_
) {
843 keyboard::KeyboardController::GetInstance()->AddObserver(this);
844 is_observing_keyboard_
= true;
847 keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
848 is_observing_keyboard_
= false;
853 ////////////////////////////////////////////////////////////////////////////////
854 // LoginDisplayHostImpl, keyboard::KeyboardControllerObserver:
857 void LoginDisplayHostImpl::OnKeyboardBoundsChanging(
858 const gfx::Rect
& new_bounds
) {
859 if (new_bounds
.IsEmpty()) {
860 // Keyboard has been hidden.
862 GetOobeUI()->GetCoreOobeActor()->ShowControlBar(true);
863 } else if (!new_bounds
.IsEmpty()) {
864 // Keyboard has been shown.
866 GetOobeUI()->GetCoreOobeActor()->ShowControlBar(false);
870 ////////////////////////////////////////////////////////////////////////////////
871 // LoginDisplayHostImpl, gfx::DisplayObserver implementation:
873 void LoginDisplayHostImpl::OnDisplayAdded(const gfx::Display
& new_display
) {
876 void LoginDisplayHostImpl::OnDisplayRemoved(const gfx::Display
& old_display
) {
879 void LoginDisplayHostImpl::OnDisplayMetricsChanged(const gfx::Display
& display
,
880 uint32_t changed_metrics
) {
881 gfx::Display primary_display
=
882 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
883 if (display
.id() != primary_display
.id() ||
884 !(changed_metrics
& DISPLAY_METRIC_BOUNDS
)) {
889 const gfx::Size
& size
= primary_display
.size();
890 GetOobeUI()->GetCoreOobeActor()->SetClientAreaSize(size
.width(),
895 ////////////////////////////////////////////////////////////////////////////////
896 // LoginDisplayHostImpl, views::WidgetRemovalsObserver implementation:
897 void LoginDisplayHostImpl::OnWillRemoveView(views::Widget
* widget
,
899 if (view
!= static_cast<views::View
*>(login_view_
))
902 widget
->RemoveRemovalsObserver(this);
905 ////////////////////////////////////////////////////////////////////////////////
906 // LoginDisplayHostImpl, private
908 void LoginDisplayHostImpl::ShutdownDisplayHost(bool post_quit_task
) {
912 shutting_down_
= true;
913 registrar_
.RemoveAll();
914 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
916 base::MessageLoop::current()->Quit();
918 if (!completion_callback_
.is_null())
919 completion_callback_
.Run();
922 void LoginDisplayHostImpl::ScheduleWorkspaceAnimation() {
923 if (ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
924 ash::kShellWindowId_DesktopBackgroundContainer
)
927 // If there is no background window, don't perform any animation on the
928 // default and background layer because there is nothing behind it.
932 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
933 switches::kDisableLoginAnimations
))
934 ash::Shell::GetInstance()->DoInitialWorkspaceAnimation();
937 void LoginDisplayHostImpl::ScheduleFadeOutAnimation() {
938 ui::Layer
* layer
= login_window_
->GetLayer();
939 ui::ScopedLayerAnimationSettings
animation(layer
->GetAnimator());
940 animation
.AddObserver(new AnimationObserver(
941 base::Bind(&LoginDisplayHostImpl::ShutdownDisplayHost
,
942 animation_weak_ptr_factory_
.GetWeakPtr(),
944 layer
->SetOpacity(0);
947 void LoginDisplayHostImpl::LoadURL(const GURL
& url
) {
948 InitLoginWindowAndView();
949 // Subscribe to crash events.
950 content::WebContentsObserver::Observe(login_view_
->GetWebContents());
951 login_view_
->LoadURL(url
);
954 void LoginDisplayHostImpl::ShowWebUI() {
955 if (!login_window_
|| !login_view_
) {
959 VLOG(1) << "Login WebUI >> Show already initialized UI";
960 login_window_
->Show();
961 login_view_
->GetWebContents()->Focus();
962 login_view_
->SetStatusAreaVisible(status_area_saved_visibility_
);
963 login_view_
->OnPostponedShow();
965 // We should reset this flag to allow changing of status area visibility.
966 initialize_webui_hidden_
= false;
969 void LoginDisplayHostImpl::StartPostponedWebUI() {
970 if (!is_wallpaper_loaded_
) {
974 VLOG(1) << "Login WebUI >> Init postponed WebUI";
976 // Wallpaper has finished loading before StartWizard/StartSignInScreen has
977 // been called. In general this should not happen.
978 // Let go through normal code path when one of those will be called.
979 if (restore_path_
== RESTORE_UNKNOWN
) {
984 switch (restore_path_
) {
986 StartWizard(first_screen_name_
);
988 case RESTORE_SIGN_IN
:
989 StartSignInScreen(LoginScreenContext());
991 case RESTORE_ADD_USER_INTO_SESSION
:
992 StartUserAdding(completion_callback_
);
1000 void LoginDisplayHostImpl::InitLoginWindowAndView() {
1004 if (system::InputDeviceSettings::Get()->ForceKeyboardDrivenUINavigation()) {
1005 views::FocusManager::set_arrow_key_traversal_enabled(true);
1007 focus_ring_controller_
.reset(new FocusRingController
);
1008 focus_ring_controller_
->SetVisible(true);
1010 keyboard_driven_oobe_key_handler_
.reset(new KeyboardDrivenOobeKeyHandler
);
1013 views::Widget::InitParams
params(
1014 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
1015 params
.bounds
= background_bounds();
1016 params
.show_state
= ui::SHOW_STATE_FULLSCREEN
;
1017 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
1019 ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
1020 ash::kShellWindowId_LockScreenContainer
);
1021 login_window_
= new views::Widget
;
1022 params
.delegate
= new LoginWidgetDelegate(login_window_
);
1023 login_window_
->Init(params
);
1025 login_view_
= new WebUILoginView();
1026 login_view_
->Init();
1027 if (login_view_
->webui_visible())
1028 OnLoginPromptVisible();
1030 login_window_
->SetVisibilityAnimationDuration(
1031 base::TimeDelta::FromMilliseconds(kLoginFadeoutTransitionDurationMs
));
1032 login_window_
->SetVisibilityAnimationTransition(views::Widget::ANIMATE_HIDE
);
1034 login_window_
->AddRemovalsObserver(this);
1035 login_window_
->SetContentsView(login_view_
);
1037 // If WebUI is initialized in hidden state, show it only if we're no
1038 // longer waiting for wallpaper animation/user images loading. Otherwise,
1040 if (!initialize_webui_hidden_
||
1041 (!waiting_for_wallpaper_load_
&& !waiting_for_user_pods_
)) {
1042 VLOG(1) << "Login WebUI >> show login wnd on create";
1043 login_window_
->Show();
1045 VLOG(1) << "Login WebUI >> login wnd is hidden on create";
1046 login_view_
->set_is_hidden(true);
1048 login_window_
->GetNativeView()->SetName("WebUILoginView");
1051 void LoginDisplayHostImpl::ResetLoginWindowAndView() {
1054 login_window_
->Close();
1055 login_window_
= NULL
;
1059 login_view_
->SetUIEnabled(true);
1063 void LoginDisplayHostImpl::OnAuthPrewarmDone() {
1064 auth_prewarmer_
.reset();
1067 void LoginDisplayHostImpl::SetOobeProgressBarVisible(bool visible
) {
1068 GetOobeUI()->ShowOobeUI(visible
);
1071 void LoginDisplayHostImpl::TryToPlayStartupSound() {
1072 if (startup_sound_played_
|| login_prompt_visible_time_
.is_null() ||
1073 !CrasAudioHandler::Get()->GetPrimaryActiveOutputNode()) {
1077 startup_sound_played_
= true;
1079 // Don't try play startup sound if login prompt is already visible
1080 // for a long time or can't be played.
1081 if (base::TimeTicks::Now() - login_prompt_visible_time_
>
1082 base::TimeDelta::FromMilliseconds(kStartupSoundMaxDelayMs
)) {
1083 EnableSystemSoundsForAccessibility();
1087 if (!startup_sound_honors_spoken_feedback_
&&
1088 !ash::PlaySystemSoundAlways(SOUND_STARTUP
)) {
1089 EnableSystemSoundsForAccessibility();
1093 if (startup_sound_honors_spoken_feedback_
&&
1094 !ash::PlaySystemSoundIfSpokenFeedback(SOUND_STARTUP
)) {
1095 EnableSystemSoundsForAccessibility();
1099 base::MessageLoop::current()->PostDelayedTask(
1101 base::Bind(&EnableSystemSoundsForAccessibility
),
1102 media::SoundsManager::Get()->GetDuration(SOUND_STARTUP
));
1105 void LoginDisplayHostImpl::OnLoginPromptVisible() {
1106 if (!login_prompt_visible_time_
.is_null())
1108 login_prompt_visible_time_
= base::TimeTicks::Now();
1109 TryToPlayStartupSound();
1112 void LoginDisplayHostImpl::StartTimeZoneResolve() {
1113 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1114 chromeos::switches::kDisableTimeZoneTrackingOption
)) {
1118 if (!g_browser_process
->local_state()->GetBoolean(
1119 prefs::kResolveDeviceTimezoneByGeolocation
)) {
1123 if (system::HasSystemTimezonePolicy())
1126 // Do not start resolver if we are inside active user session.
1127 // If user preferences permit, it will be started on preferences
1129 if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kLoginUser
))
1132 g_browser_process
->platform_part()->GetTimezoneResolver()->Start();
1135 ////////////////////////////////////////////////////////////////////////////////
1138 // Declared in login_wizard.h so that others don't need to depend on our .h.
1139 // TODO(nkostylev): Split this into a smaller functions.
1140 void ShowLoginWizard(const std::string
& first_screen_name
) {
1141 if (browser_shutdown::IsTryingToQuit())
1144 VLOG(1) << "Showing OOBE screen: " << first_screen_name
;
1146 chromeos::input_method::InputMethodManager
* manager
=
1147 chromeos::input_method::InputMethodManager::Get();
1149 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
1150 // and US dvorak keyboard layouts.
1151 if (g_browser_process
&& g_browser_process
->local_state()) {
1152 manager
->GetActiveIMEState()->SetInputMethodLoginDefault();
1154 PrefService
* prefs
= g_browser_process
->local_state();
1155 // Apply owner preferences for tap-to-click and mouse buttons swap for
1157 system::InputDeviceSettings::Get()->SetPrimaryButtonRight(
1158 prefs
->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight
));
1159 system::InputDeviceSettings::Get()->SetTapToClick(
1160 prefs
->GetBoolean(prefs::kOwnerTapToClickEnabled
));
1162 system::InputDeviceSettings::Get()->SetNaturalScroll(
1163 base::CommandLine::ForCurrentProcess()->HasSwitch(
1164 switches::kNaturalScrollDefault
));
1166 gfx::Rect
screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));
1168 g_browser_process
->platform_part()->SessionManager()->SetSessionState(
1169 StartupUtils::IsOobeCompleted()
1170 ? session_manager::SESSION_STATE_LOGIN_PRIMARY
1171 : session_manager::SESSION_STATE_OOBE
);
1173 LoginDisplayHostImpl
* display_host
= new LoginDisplayHostImpl(screen_bounds
);
1175 bool show_app_launch_splash_screen
=
1176 (first_screen_name
== WizardController::kAppLaunchSplashScreenName
);
1177 if (show_app_launch_splash_screen
) {
1178 const std::string
& auto_launch_app_id
=
1179 KioskAppManager::Get()->GetAutoLaunchApp();
1180 const bool diagnostic_mode
= false;
1181 const bool auto_launch
= true;
1182 display_host
->StartAppLaunch(auto_launch_app_id
,
1188 // Check whether we need to execute OOBE flow.
1189 const policy::EnrollmentConfig enrollment_config
=
1190 g_browser_process
->platform_part()
1191 ->browser_policy_connector_chromeos()
1192 ->GetPrescribedEnrollmentConfig();
1193 if (enrollment_config
.should_enroll() && first_screen_name
.empty()) {
1194 // Shows networks screen instead of enrollment screen to resume the
1195 // interrupted auto start enrollment flow because enrollment screen does
1196 // not handle flaky network. See http://crbug.com/332572
1197 display_host
->StartWizard(WizardController::kNetworkScreenName
);
1201 if (StartupUtils::IsEulaAccepted()) {
1203 base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS
),
1204 ServicesCustomizationDocument::GetInstance()
1205 ->EnsureCustomizationAppliedClosure());
1207 display_host
->StartTimeZoneResolve();
1210 bool show_login_screen
=
1211 (first_screen_name
.empty() && StartupUtils::IsOobeCompleted()) ||
1212 first_screen_name
== WizardController::kLoginScreenName
;
1214 if (show_login_screen
) {
1215 display_host
->StartSignInScreen(LoginScreenContext());
1219 // Load startup manifest.
1220 const StartupCustomizationDocument
* startup_manifest
=
1221 StartupCustomizationDocument::GetInstance();
1223 // Switch to initial locale if specified by customization
1224 // and has not been set yet. We cannot call
1225 // LanguageSwitchMenu::SwitchLanguage here before
1226 // EmitLoginPromptReady.
1227 PrefService
* prefs
= g_browser_process
->local_state();
1228 const std::string
& current_locale
=
1229 prefs
->GetString(prefs::kApplicationLocale
);
1230 VLOG(1) << "Current locale: " << current_locale
;
1231 const std::string
& locale
= startup_manifest
->initial_locale_default();
1233 const std::string
& layout
= startup_manifest
->keyboard_layout();
1234 VLOG(1) << "Initial locale: " << locale
<< "keyboard layout " << layout
;
1236 // Determine keyboard layout from OEM customization (if provided) or
1237 // initial locale and save it in preferences.
1238 manager
->GetActiveIMEState()->SetInputMethodLoginDefaultFromVPD(locale
,
1241 if (!current_locale
.empty() || locale
.empty()) {
1242 ShowLoginWizardFinish(first_screen_name
, startup_manifest
, display_host
);
1246 // Save initial locale from VPD/customization manifest as current
1247 // Chrome locale. Otherwise it will be lost if Chrome restarts.
1248 // Don't need to schedule pref save because setting initial local
1249 // will enforce preference saving.
1250 prefs
->SetString(prefs::kApplicationLocale
, locale
);
1251 StartupUtils::SetInitialLocale(locale
);
1253 scoped_ptr
<ShowLoginWizardSwitchLanguageCallbackData
> data(
1254 new ShowLoginWizardSwitchLanguageCallbackData(
1255 first_screen_name
, startup_manifest
, display_host
));
1257 locale_util::SwitchLanguageCallback
callback(
1258 base::Bind(&OnLanguageSwitchedCallback
, base::Passed(data
.Pass())));
1260 // Load locale keyboards here. Hardware layout would be automatically enabled.
1261 locale_util::SwitchLanguage(locale
, true, true /* login_layouts_only */,
1262 callback
, ProfileManager::GetActiveUserProfile());
1265 } // namespace chromeos