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