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