login_display_host_impl.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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 "components/session_manager/core/session_manager.h"
72#include "content/public/browser/notification_service.h"
73#include "content/public/browser/notification_types.h"
74#include "content/public/browser/render_frame_host.h"
75#include "content/public/browser/web_contents.h"
76#include "content/public/browser/web_ui.h"
77#include "grit/browser_resources.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
99namespace {
100
101// Maximum delay for startup sound after 'loginPromptVisible' signal.
102const int kStartupSoundMaxDelayMs = 2000;
103
104// URL which corresponds to the login WebUI.
105const char kLoginURL[] = "chrome://oobe/login";
106
107// URL which corresponds to the OOBE WebUI.
108const char kOobeURL[] = "chrome://oobe/oobe";
109
110// URL which corresponds to the user adding WebUI.
111const char kUserAddingURL[] = "chrome://oobe/user-adding";
112
113// URL which corresponds to the app launch splash WebUI.
114const char kAppLaunchSplashURL[] = "chrome://oobe/app-launch-splash";
115
116// Duration of sign-in transition animation.
117const int kLoginFadeoutTransitionDurationMs = 700;
118
119// Number of times we try to reload OOBE/login WebUI if it crashes.
120const int kCrashCountLimit = 5;
121
122// Whether to enable tnitializing WebUI in hidden state (see
123// |initialize_webui_hidden_|) by default.
124const bool kHiddenWebUIInitializationDefault = true;
125
126// Switch values that might be used to override WebUI init type.
127const char kWebUIInitParallel[] = "parallel";
128const char kWebUIInitPostpone[] = "postpone";
129
130// The delay of triggering initialization of the device policy subsystem
131// after the login screen is initialized. This makes sure that device policy
132// network requests are made while the system is idle waiting for user input.
133const int64 kPolicyServiceInitializationDelayMilliseconds = 100;
134
135// A class to observe an implicit animation and invokes the callback after the
136// animation is completed.
137class AnimationObserver : public ui::ImplicitAnimationObserver {
138 public:
139  explicit AnimationObserver(const base::Closure& callback)
140      : callback_(callback) {}
141  virtual ~AnimationObserver() {}
142
143 private:
144  // ui::ImplicitAnimationObserver implementation:
145  virtual void OnImplicitAnimationsCompleted() OVERRIDE {
146    callback_.Run();
147    delete this;
148  }
149
150  base::Closure callback_;
151
152  DISALLOW_COPY_AND_ASSIGN(AnimationObserver);
153};
154
155// ShowLoginWizard is split into two parts. This function is sometimes called
156// from ShowLoginWizard(), and sometimes from OnLanguageSwitchedCallback()
157// (if locale was updated).
158void ShowLoginWizardFinish(
159    const std::string& first_screen_name,
160    const chromeos::StartupCustomizationDocument* startup_manifest,
161    chromeos::LoginDisplayHost* display_host) {
162  TRACE_EVENT0("chromeos", "ShowLoginWizard::ShowLoginWizardFinish");
163
164  scoped_ptr<base::DictionaryValue> params;
165  display_host->StartWizard(first_screen_name, params.Pass());
166
167  // Set initial timezone if specified by customization.
168  const std::string timezone_name = startup_manifest->initial_timezone();
169  VLOG(1) << "Initial time zone: " << timezone_name;
170  // Apply locale customizations only once to preserve whatever locale
171  // user has changed to during OOBE.
172  if (!timezone_name.empty()) {
173    chromeos::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
174        base::UTF8ToUTF16(timezone_name));
175  }
176}
177
178struct ShowLoginWizardSwitchLanguageCallbackData {
179  explicit ShowLoginWizardSwitchLanguageCallbackData(
180      const std::string& first_screen_name,
181      const chromeos::StartupCustomizationDocument* startup_manifest,
182      chromeos::LoginDisplayHost* display_host)
183      : first_screen_name(first_screen_name),
184        startup_manifest(startup_manifest),
185        display_host(display_host) {}
186
187  const std::string first_screen_name;
188  const chromeos::StartupCustomizationDocument* const startup_manifest;
189  chromeos::LoginDisplayHost* const display_host;
190
191  // lock UI while resource bundle is being reloaded.
192  chromeos::InputEventsBlocker events_blocker;
193};
194
195void OnLanguageSwitchedCallback(
196    scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> self,
197    const std::string& locale,
198    const std::string& loaded_locale,
199    const bool success) {
200  if (!success)
201    LOG(WARNING) << "Locale could not be found for '" << locale << "'";
202
203  ShowLoginWizardFinish(
204      self->first_screen_name, self->startup_manifest, self->display_host);
205}
206
207void EnableSystemSoundsForAccessibility() {
208  chromeos::AccessibilityManager::Get()->EnableSystemSounds(true);
209}
210
211void AddToSetIfIsGaiaAuthIframe(std::set<content::RenderFrameHost*>* frame_set,
212                                content::RenderFrameHost* frame) {
213  content::RenderFrameHost* parent = frame->GetParent();
214  if (parent && parent->GetFrameName() == "signin-frame")
215    frame_set->insert(frame);
216}
217
218// A login implementation of WidgetDelegate.
219class LoginWidgetDelegate : public views::WidgetDelegate {
220 public:
221  explicit LoginWidgetDelegate(views::Widget* widget) : widget_(widget) {
222  }
223  virtual ~LoginWidgetDelegate() {}
224
225  // Overridden from WidgetDelegate:
226  virtual void DeleteDelegate() OVERRIDE {
227    delete this;
228  }
229  virtual views::Widget* GetWidget() OVERRIDE {
230    return widget_;
231  }
232  virtual const views::Widget* GetWidget() const OVERRIDE {
233    return widget_;
234  }
235  virtual bool CanActivate() const OVERRIDE {
236    return true;
237  }
238  virtual bool ShouldAdvanceFocusToTopLevelWidget() const OVERRIDE {
239    return true;
240  }
241
242 private:
243  views::Widget* widget_;
244
245  DISALLOW_COPY_AND_ASSIGN(LoginWidgetDelegate);
246};
247
248// Disables virtual keyboard overscroll. Login UI will scroll user pods
249// into view on JS side when virtual keyboard is shown.
250void DisableKeyboardOverscroll() {
251  keyboard::SetKeyboardOverscrollOverride(
252      keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
253}
254
255void ResetKeyboardOverscrollOverride() {
256  keyboard::SetKeyboardOverscrollOverride(
257      keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE);
258}
259
260}  // namespace
261
262namespace chromeos {
263
264// static
265LoginDisplayHost* LoginDisplayHostImpl::default_host_ = NULL;
266
267// static
268const int LoginDisplayHostImpl::kShowLoginWebUIid = 0x1111;
269
270// static
271content::RenderFrameHost* LoginDisplayHostImpl::GetGaiaAuthIframe(
272    content::WebContents* web_contents) {
273  std::set<content::RenderFrameHost*> frame_set;
274  web_contents->ForEachFrame(
275      base::Bind(&AddToSetIfIsGaiaAuthIframe, &frame_set));
276  DCHECK_EQ(1U, frame_set.size());
277  return *frame_set.begin();
278}
279
280////////////////////////////////////////////////////////////////////////////////
281// LoginDisplayHostImpl, public
282
283LoginDisplayHostImpl::LoginDisplayHostImpl(const gfx::Rect& background_bounds)
284    : background_bounds_(background_bounds),
285      pointer_factory_(this),
286      shutting_down_(false),
287      oobe_progress_bar_visible_(false),
288      session_starting_(false),
289      login_window_(NULL),
290      login_view_(NULL),
291      webui_login_display_(NULL),
292      is_showing_login_(false),
293      is_wallpaper_loaded_(false),
294      status_area_saved_visibility_(false),
295      crash_count_(0),
296      restore_path_(RESTORE_UNKNOWN),
297      finalize_animation_type_(ANIMATION_WORKSPACE),
298      animation_weak_ptr_factory_(this),
299      startup_sound_played_(false),
300      startup_sound_honors_spoken_feedback_(false),
301      is_observing_keyboard_(false) {
302  DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this);
303  CrasAudioHandler::Get()->AddAudioObserver(this);
304  if (keyboard::KeyboardController::GetInstance()) {
305    keyboard::KeyboardController::GetInstance()->AddObserver(this);
306    is_observing_keyboard_ = true;
307  }
308
309  ash::Shell::GetInstance()->delegate()->AddVirtualKeyboardStateObserver(this);
310  ash::Shell::GetScreen()->AddObserver(this);
311
312  // We need to listen to CLOSE_ALL_BROWSERS_REQUEST but not APP_TERMINATING
313  // because/ APP_TERMINATING will never be fired as long as this keeps
314  // ref-count. CLOSE_ALL_BROWSERS_REQUEST is safe here because there will be no
315  // browser instance that will block the shutdown.
316  registrar_.Add(this,
317                 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
318                 content::NotificationService::AllSources());
319
320  // NOTIFICATION_BROWSER_OPENED is issued after browser is created, but
321  // not shown yet. Lock window has to be closed at this point so that
322  // a browser window exists and the window can acquire input focus.
323  registrar_.Add(this,
324                 chrome::NOTIFICATION_BROWSER_OPENED,
325                 content::NotificationService::AllSources());
326
327  // Login screen is moved to lock screen container when user logs in.
328  registrar_.Add(this,
329                 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
330                 content::NotificationService::AllSources());
331
332  DCHECK(default_host_ == NULL);
333  default_host_ = this;
334
335  // Make sure chrome won't exit while we are at login/oobe screen.
336  chrome::IncrementKeepAliveCount();
337
338  bool is_registered = StartupUtils::IsDeviceRegistered();
339  bool zero_delay_enabled = WizardController::IsZeroDelayEnabled();
340  bool disable_boot_animation = CommandLine::ForCurrentProcess()->HasSwitch(
341      switches::kDisableBootAnimation);
342
343  waiting_for_wallpaper_load_ = !zero_delay_enabled &&
344                                (!is_registered || !disable_boot_animation);
345
346  // For slower hardware we have boot animation disabled so
347  // we'll be initializing WebUI hidden, waiting for user pods to load and then
348  // show WebUI at once.
349  waiting_for_user_pods_ = !zero_delay_enabled && !waiting_for_wallpaper_load_;
350
351  initialize_webui_hidden_ =
352      kHiddenWebUIInitializationDefault && !zero_delay_enabled;
353
354  // Check if WebUI init type is overriden.
355  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshWebUIInit)) {
356    const std::string override_type =
357        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
358            switches::kAshWebUIInit);
359    if (override_type == kWebUIInitParallel)
360      initialize_webui_hidden_ = true;
361    else if (override_type == kWebUIInitPostpone)
362      initialize_webui_hidden_ = false;
363  }
364
365  // Always postpone WebUI initialization on first boot, otherwise we miss
366  // initial animation.
367  if (!StartupUtils::IsOobeCompleted())
368    initialize_webui_hidden_ = false;
369
370  // There is no wallpaper for KioskMode, don't initialize the webui hidden.
371  if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
372    initialize_webui_hidden_ = false;
373
374  if (waiting_for_wallpaper_load_) {
375    registrar_.Add(this,
376                   chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
377                   content::NotificationService::AllSources());
378  }
379
380  // When we wait for WebUI to be initialized we wait for one of
381  // these notifications.
382  if ((waiting_for_user_pods_ || waiting_for_wallpaper_load_) &&
383      initialize_webui_hidden_) {
384    registrar_.Add(this,
385                   chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
386                   content::NotificationService::AllSources());
387    registrar_.Add(this,
388                   chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
389                   content::NotificationService::AllSources());
390  }
391  VLOG(1) << "Login WebUI >> "
392          << "zero_delay: " << zero_delay_enabled
393          << " wait_for_wp_load_: " << waiting_for_wallpaper_load_
394          << " wait_for_pods_: " << waiting_for_user_pods_
395          << " init_webui_hidden_: " << initialize_webui_hidden_;
396
397  media::SoundsManager* manager = media::SoundsManager::Get();
398  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
399  manager->Initialize(chromeos::SOUND_STARTUP,
400                      bundle.GetRawDataResource(IDR_SOUND_STARTUP_WAV));
401}
402
403LoginDisplayHostImpl::~LoginDisplayHostImpl() {
404  DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
405  CrasAudioHandler::Get()->RemoveAudioObserver(this);
406  if (keyboard::KeyboardController::GetInstance() && is_observing_keyboard_) {
407    keyboard::KeyboardController::GetInstance()->RemoveObserver(this);
408    is_observing_keyboard_ = false;
409  }
410
411  ash::Shell::GetInstance()->delegate()->
412      RemoveVirtualKeyboardStateObserver(this);
413  ash::Shell::GetScreen()->RemoveObserver(this);
414
415  if (login_view_ && login_window_)
416    login_window_->RemoveRemovalsObserver(this);
417
418  if (login::LoginScrollIntoViewEnabled())
419    ResetKeyboardOverscrollOverride();
420
421  views::FocusManager::set_arrow_key_traversal_enabled(false);
422  ResetLoginWindowAndView();
423
424  // Let chrome process exit after login/oobe screen if needed.
425  chrome::DecrementKeepAliveCount();
426
427  default_host_ = NULL;
428  // TODO(tengs): This should be refactored. See crbug.com/314934.
429  if (UserManager::Get()->IsCurrentUserNew()) {
430    // DriveOptInController will delete itself when finished.
431    (new DriveFirstRunController(
432        ProfileManager::GetActiveUserProfile()))->EnableOfflineMode();
433  }
434}
435
436////////////////////////////////////////////////////////////////////////////////
437// LoginDisplayHostImpl, LoginDisplayHost implementation:
438
439LoginDisplay* LoginDisplayHostImpl::CreateLoginDisplay(
440    LoginDisplay::Delegate* delegate) {
441  webui_login_display_ = new WebUILoginDisplay(delegate);
442  webui_login_display_->set_background_bounds(background_bounds());
443  return webui_login_display_;
444}
445
446gfx::NativeWindow LoginDisplayHostImpl::GetNativeWindow() const {
447  return login_window_ ? login_window_->GetNativeWindow() : NULL;
448}
449
450WebUILoginView* LoginDisplayHostImpl::GetWebUILoginView() const {
451  return login_view_;
452}
453
454void LoginDisplayHostImpl::BeforeSessionStart() {
455  session_starting_ = true;
456}
457
458void LoginDisplayHostImpl::Finalize() {
459  DVLOG(1) << "Session starting";
460  if (ash::Shell::HasInstance()) {
461    ash::Shell::GetInstance()->
462        desktop_background_controller()->MoveDesktopToUnlockedContainer();
463  }
464  if (wizard_controller_.get())
465    wizard_controller_->OnSessionStart();
466
467  switch (finalize_animation_type_) {
468    case ANIMATION_NONE:
469      ShutdownDisplayHost(false);
470      break;
471    case ANIMATION_WORKSPACE:
472      if (ash::Shell::HasInstance())
473        ScheduleWorkspaceAnimation();
474
475      ShutdownDisplayHost(false);
476      break;
477    case ANIMATION_FADE_OUT:
478      // Display host is deleted once animation is completed
479      // since sign in screen widget has to stay alive.
480      ScheduleFadeOutAnimation();
481      break;
482  }
483}
484
485void LoginDisplayHostImpl::OnCompleteLogin() {
486  if (auto_enrollment_controller_)
487    auto_enrollment_controller_->Cancel();
488}
489
490void LoginDisplayHostImpl::OpenProxySettings() {
491  if (login_view_)
492    login_view_->OpenProxySettings();
493}
494
495void LoginDisplayHostImpl::SetStatusAreaVisible(bool visible) {
496  if (initialize_webui_hidden_)
497    status_area_saved_visibility_ = visible;
498  else if (login_view_)
499    login_view_->SetStatusAreaVisible(visible);
500}
501
502AutoEnrollmentController* LoginDisplayHostImpl::GetAutoEnrollmentController() {
503  if (!auto_enrollment_controller_) {
504    auto_enrollment_controller_.reset(new AutoEnrollmentController());
505    auto_enrollment_progress_subscription_ =
506        auto_enrollment_controller_->RegisterProgressCallback(
507            base::Bind(&LoginDisplayHostImpl::OnAutoEnrollmentProgress,
508                       base::Unretained(this)));
509  }
510  return auto_enrollment_controller_.get();
511}
512
513void LoginDisplayHostImpl::StartWizard(
514    const std::string& first_screen_name,
515    scoped_ptr<base::DictionaryValue> screen_parameters) {
516  if (login::LoginScrollIntoViewEnabled())
517    DisableKeyboardOverscroll();
518
519  startup_sound_honors_spoken_feedback_ = false;
520  TryToPlayStartupSound();
521
522  // Keep parameters to restore if renderer crashes.
523  restore_path_ = RESTORE_WIZARD;
524  wizard_first_screen_name_ = first_screen_name;
525  if (screen_parameters.get())
526    wizard_screen_parameters_.reset(screen_parameters->DeepCopy());
527  else
528    wizard_screen_parameters_.reset();
529  is_showing_login_ = false;
530
531  if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
532    VLOG(1) << "Login WebUI >> wizard postponed";
533    return;
534  }
535  VLOG(1) << "Login WebUI >> wizard";
536
537  if (!login_window_)
538    LoadURL(GURL(kOobeURL));
539
540  DVLOG(1) << "Starting wizard, first_screen_name: " << first_screen_name;
541  // Create and show the wizard.
542  // Note, dtor of the old WizardController should be called before ctor of the
543  // new one, because "default_controller()" is updated there. So pure "reset()"
544  // is done before new controller creation.
545  wizard_controller_.reset();
546  wizard_controller_.reset(CreateWizardController());
547
548  oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
549  SetOobeProgressBarVisible(oobe_progress_bar_visible_);
550  wizard_controller_->Init(first_screen_name, screen_parameters.Pass());
551}
552
553WizardController* LoginDisplayHostImpl::GetWizardController() {
554  return wizard_controller_.get();
555}
556
557AppLaunchController* LoginDisplayHostImpl::GetAppLaunchController() {
558  return app_launch_controller_.get();
559}
560
561void LoginDisplayHostImpl::StartUserAdding(
562    const base::Closure& completion_callback) {
563  if (login::LoginScrollIntoViewEnabled())
564    DisableKeyboardOverscroll();
565
566  restore_path_ = RESTORE_ADD_USER_INTO_SESSION;
567  completion_callback_ = completion_callback;
568  finalize_animation_type_ = ANIMATION_NONE;
569  VLOG(1) << "Login WebUI >> user adding";
570  if (!login_window_)
571    LoadURL(GURL(kUserAddingURL));
572  // We should emit this signal only at login screen (after reboot or sign out).
573  login_view_->set_should_emit_login_prompt_visible(false);
574
575  // Lock container can be transparent after lock screen animation.
576  aura::Window* lock_container = ash::Shell::GetContainer(
577      ash::Shell::GetPrimaryRootWindow(),
578      ash::kShellWindowId_LockScreenContainersContainer);
579  lock_container->layer()->SetOpacity(1.0);
580
581  ash::Shell::GetInstance()->
582      desktop_background_controller()->MoveDesktopToLockedContainer();
583
584  sign_in_controller_.reset();  // Only one controller in a time.
585  sign_in_controller_.reset(new chromeos::ExistingUserController(this));
586  SetOobeProgressBarVisible(oobe_progress_bar_visible_ = false);
587  SetStatusAreaVisible(true);
588  sign_in_controller_->Init(
589      chromeos::UserManager::Get()->GetUsersAdmittedForMultiProfile());
590  CHECK(webui_login_display_);
591  GetOobeUI()->ShowSigninScreen(LoginScreenContext(),
592                                webui_login_display_,
593                                webui_login_display_);
594}
595
596void LoginDisplayHostImpl::StartSignInScreen(
597    const LoginScreenContext& context) {
598  if (login::LoginScrollIntoViewEnabled())
599    DisableKeyboardOverscroll();
600
601  startup_sound_honors_spoken_feedback_ = true;
602  TryToPlayStartupSound();
603
604  restore_path_ = RESTORE_SIGN_IN;
605  is_showing_login_ = true;
606  finalize_animation_type_ = ANIMATION_WORKSPACE;
607
608  PrewarmAuthentication();
609
610  if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
611    VLOG(1) << "Login WebUI >> sign in postponed";
612    return;
613  }
614  VLOG(1) << "Login WebUI >> sign in";
615
616  if (!login_window_) {
617    TRACE_EVENT_ASYNC_BEGIN0("ui", "ShowLoginWebUI", kShowLoginWebUIid);
618    TRACE_EVENT_ASYNC_STEP_INTO0(
619        "ui", "ShowLoginWebUI", kShowLoginWebUIid, "StartSignInScreen");
620    BootTimesLoader::Get()->RecordCurrentStats("login-start-signin-screen");
621    LoadURL(GURL(kLoginURL));
622  }
623
624  DVLOG(1) << "Starting sign in screen";
625  const user_manager::UserList& users =
626      chromeos::UserManager::Get()->GetUsers();
627
628  // Fix for users who updated device and thus never passed register screen.
629  // If we already have users, we assume that it is not a second part of
630  // OOBE. See http://crosbug.com/6289
631  if (!StartupUtils::IsDeviceRegistered() && !users.empty()) {
632    VLOG(1) << "Mark device registered because there are remembered users: "
633            << users.size();
634    StartupUtils::MarkDeviceRegistered(base::Closure());
635  }
636
637  sign_in_controller_.reset();  // Only one controller in a time.
638  sign_in_controller_.reset(new chromeos::ExistingUserController(this));
639  oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
640  SetOobeProgressBarVisible(oobe_progress_bar_visible_);
641  SetStatusAreaVisible(true);
642  sign_in_controller_->Init(users);
643
644  // We might be here after a reboot that was triggered after OOBE was complete,
645  // so check for auto-enrollment again. This might catch a cached decision from
646  // a previous oobe flow, or might start a new check with the server.
647  if (GetAutoEnrollmentController()->ShouldEnrollSilently())
648    sign_in_controller_->DoAutoEnrollment();
649  else
650    GetAutoEnrollmentController()->Start();
651
652  // Initiate mobile config load.
653  MobileConfig::GetInstance();
654
655  // Initiate device policy fetching.
656  policy::BrowserPolicyConnectorChromeOS* connector =
657      g_browser_process->platform_part()->browser_policy_connector_chromeos();
658  connector->ScheduleServiceInitialization(
659      kPolicyServiceInitializationDelayMilliseconds);
660
661  CHECK(webui_login_display_);
662  GetOobeUI()->ShowSigninScreen(context,
663                                webui_login_display_,
664                                webui_login_display_);
665  if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
666    SetStatusAreaVisible(false);
667  TRACE_EVENT_ASYNC_STEP_INTO0("ui",
668                               "ShowLoginWebUI",
669                               kShowLoginWebUIid,
670                               "WaitForScreenStateInitialize");
671  BootTimesLoader::Get()->RecordCurrentStats(
672      "login-wait-for-signin-state-initialize");
673}
674
675void LoginDisplayHostImpl::ResumeSignInScreen() {
676  // We only get here after a previous call the StartSignInScreen. That sign-in
677  // was successful but was interrupted by an auto-enrollment execution; once
678  // auto-enrollment is complete we resume the normal login flow from here.
679  DVLOG(1) << "Resuming sign in screen";
680  CHECK(sign_in_controller_.get());
681  SetOobeProgressBarVisible(oobe_progress_bar_visible_);
682  SetStatusAreaVisible(true);
683  sign_in_controller_->ResumeLogin();
684}
685
686
687void LoginDisplayHostImpl::OnPreferencesChanged() {
688  if (is_showing_login_)
689    webui_login_display_->OnPreferencesChanged();
690}
691
692void LoginDisplayHostImpl::PrewarmAuthentication() {
693  auth_prewarmer_.reset(new AuthPrewarmer());
694  auth_prewarmer_->PrewarmAuthentication(
695      base::Bind(&LoginDisplayHostImpl::OnAuthPrewarmDone,
696                 pointer_factory_.GetWeakPtr()));
697}
698
699void LoginDisplayHostImpl::StartDemoAppLaunch() {
700  VLOG(1) << "Login WebUI >> starting demo app.";
701  SetStatusAreaVisible(false);
702
703  demo_app_launcher_.reset(new DemoAppLauncher());
704  demo_app_launcher_->StartDemoAppLaunch();
705}
706
707void LoginDisplayHostImpl::StartAppLaunch(const std::string& app_id,
708                                          bool diagnostic_mode) {
709  VLOG(1) << "Login WebUI >> start app launch.";
710  SetStatusAreaVisible(false);
711  finalize_animation_type_ = ANIMATION_FADE_OUT;
712  if (!login_window_)
713    LoadURL(GURL(kAppLaunchSplashURL));
714
715  login_view_->set_should_emit_login_prompt_visible(false);
716
717  app_launch_controller_.reset(new AppLaunchController(
718      app_id, diagnostic_mode, this, GetOobeUI()));
719
720  app_launch_controller_->StartAppLaunch();
721}
722
723////////////////////////////////////////////////////////////////////////////////
724// LoginDisplayHostImpl, public
725
726WizardController* LoginDisplayHostImpl::CreateWizardController() {
727  // TODO(altimofeev): ensure that WebUI is ready.
728  OobeDisplay* oobe_display = GetOobeUI();
729  return new WizardController(this, oobe_display);
730}
731
732void LoginDisplayHostImpl::OnBrowserCreated() {
733  // Close lock window now so that the launched browser can receive focus.
734  ResetLoginWindowAndView();
735}
736
737OobeUI* LoginDisplayHostImpl::GetOobeUI() const {
738  if (!login_view_)
739    return NULL;
740  return static_cast<OobeUI*>(login_view_->GetWebUI()->GetController());
741}
742
743////////////////////////////////////////////////////////////////////////////////
744// LoginDisplayHostImpl, content:NotificationObserver implementation:
745
746void LoginDisplayHostImpl::Observe(
747    int type,
748    const content::NotificationSource& source,
749    const content::NotificationDetails& details) {
750  if (chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED == type) {
751    VLOG(1) << "Login WebUI >> wp animation done";
752    is_wallpaper_loaded_ = true;
753    ash::Shell::GetInstance()->user_wallpaper_delegate()
754        ->OnWallpaperBootAnimationFinished();
755    if (waiting_for_wallpaper_load_) {
756      // StartWizard / StartSignInScreen could be called multiple times through
757      // the lifetime of host.
758      // Make sure that subsequent calls are not postponed.
759      waiting_for_wallpaper_load_ = false;
760      if (initialize_webui_hidden_)
761        ShowWebUI();
762      else
763        StartPostponedWebUI();
764    }
765    registrar_.Remove(this,
766                      chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
767                      content::NotificationService::AllSources());
768  } else if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type ||
769             chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN == type) {
770    VLOG(1) << "Login WebUI >> WEBUI_VISIBLE";
771    if (waiting_for_user_pods_ && initialize_webui_hidden_) {
772      waiting_for_user_pods_ = false;
773      ShowWebUI();
774    } else if (waiting_for_wallpaper_load_ && initialize_webui_hidden_) {
775      // Reduce time till login UI is shown - show it as soon as possible.
776      waiting_for_wallpaper_load_ = false;
777      ShowWebUI();
778    }
779    registrar_.Remove(this,
780                      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
781                      content::NotificationService::AllSources());
782    registrar_.Remove(this,
783                      chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
784                      content::NotificationService::AllSources());
785  } else if (type == chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST) {
786    ShutdownDisplayHost(true);
787  } else if (type == chrome::NOTIFICATION_BROWSER_OPENED && session_starting_) {
788    // Browsers created before session start (windows opened by extensions, for
789    // example) are ignored.
790    OnBrowserCreated();
791    registrar_.Remove(this,
792                      chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
793                      content::NotificationService::AllSources());
794    registrar_.Remove(this,
795                      chrome::NOTIFICATION_BROWSER_OPENED,
796                      content::NotificationService::AllSources());
797  } else if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED &&
798             chromeos::UserManager::Get()->IsCurrentUserNew()) {
799    // For new user, move desktop to locker container so that windows created
800    // during the user image picker step are below it.
801    ash::Shell::GetInstance()->
802        desktop_background_controller()->MoveDesktopToLockedContainer();
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
1049    focus_ring_controller_.reset(new FocusRingController);
1050    focus_ring_controller_->SetVisible(true);
1051
1052    keyboard_driven_oobe_key_handler_.reset(new KeyboardDrivenOobeKeyHandler);
1053  }
1054
1055  views::Widget::InitParams params(
1056      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
1057  params.bounds = background_bounds();
1058  params.show_state = ui::SHOW_STATE_FULLSCREEN;
1059  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
1060  params.parent =
1061      ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
1062                               ash::kShellWindowId_LockScreenContainer);
1063
1064  login_window_ = new views::Widget;
1065  params.delegate = new LoginWidgetDelegate(login_window_);
1066  login_window_->Init(params);
1067
1068  login_view_ = new WebUILoginView();
1069  login_view_->Init();
1070  if (login_view_->webui_visible())
1071    OnLoginPromptVisible();
1072
1073  wm::SetWindowVisibilityAnimationDuration(
1074      login_window_->GetNativeView(),
1075      base::TimeDelta::FromMilliseconds(kLoginFadeoutTransitionDurationMs));
1076  wm::SetWindowVisibilityAnimationTransition(
1077      login_window_->GetNativeView(),
1078      wm::ANIMATE_HIDE);
1079
1080  login_window_->AddRemovalsObserver(this);
1081  login_window_->SetContentsView(login_view_);
1082
1083  // If WebUI is initialized in hidden state, show it only if we're no
1084  // longer waiting for wallpaper animation/user images loading. Otherwise,
1085  // always show it.
1086  if (!initialize_webui_hidden_ ||
1087      (!waiting_for_wallpaper_load_ && !waiting_for_user_pods_)) {
1088    VLOG(1) << "Login WebUI >> show login wnd on create";
1089    login_window_->Show();
1090  } else {
1091    VLOG(1) << "Login WebUI >> login wnd is hidden on create";
1092    login_view_->set_is_hidden(true);
1093  }
1094  login_window_->GetNativeView()->SetName("WebUILoginView");
1095}
1096
1097void LoginDisplayHostImpl::ResetLoginWindowAndView() {
1098  if (!login_window_)
1099    return;
1100  login_window_->Close();
1101  login_window_ = NULL;
1102  login_view_ = NULL;
1103}
1104
1105void LoginDisplayHostImpl::OnAuthPrewarmDone() {
1106  auth_prewarmer_.reset();
1107}
1108
1109void LoginDisplayHostImpl::SetOobeProgressBarVisible(bool visible) {
1110  GetOobeUI()->ShowOobeUI(visible);
1111}
1112
1113void LoginDisplayHostImpl::TryToPlayStartupSound() {
1114  if (startup_sound_played_ || login_prompt_visible_time_.is_null() ||
1115      !CrasAudioHandler::Get()->GetActiveOutputNode()) {
1116    return;
1117  }
1118
1119  startup_sound_played_ = true;
1120
1121  // Don't try play startup sound if login prompt is already visible
1122  // for a long time or can't be played.
1123  if (base::TimeTicks::Now() - login_prompt_visible_time_ >
1124      base::TimeDelta::FromMilliseconds(kStartupSoundMaxDelayMs)) {
1125    EnableSystemSoundsForAccessibility();
1126    return;
1127  }
1128
1129  if (!startup_sound_honors_spoken_feedback_ &&
1130      !ash::PlaySystemSoundAlways(SOUND_STARTUP)) {
1131    EnableSystemSoundsForAccessibility();
1132    return;
1133  }
1134
1135  if (startup_sound_honors_spoken_feedback_ &&
1136      !ash::PlaySystemSoundIfSpokenFeedback(SOUND_STARTUP)) {
1137    EnableSystemSoundsForAccessibility();
1138    return;
1139  }
1140
1141  base::MessageLoop::current()->PostDelayedTask(
1142      FROM_HERE,
1143      base::Bind(&EnableSystemSoundsForAccessibility),
1144      media::SoundsManager::Get()->GetDuration(SOUND_STARTUP));
1145}
1146
1147void LoginDisplayHostImpl::OnLoginPromptVisible() {
1148  if (!login_prompt_visible_time_.is_null())
1149    return;
1150  login_prompt_visible_time_ = base::TimeTicks::Now();
1151  TryToPlayStartupSound();
1152}
1153
1154////////////////////////////////////////////////////////////////////////////////
1155// external
1156
1157// Declared in login_wizard.h so that others don't need to depend on our .h.
1158// TODO(nkostylev): Split this into a smaller functions.
1159void ShowLoginWizard(const std::string& first_screen_name) {
1160  if (browser_shutdown::IsTryingToQuit())
1161    return;
1162
1163  VLOG(1) << "Showing OOBE screen: " << first_screen_name;
1164
1165  chromeos::input_method::InputMethodManager* manager =
1166      chromeos::input_method::InputMethodManager::Get();
1167
1168  // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
1169  // and US dvorak keyboard layouts.
1170  if (g_browser_process && g_browser_process->local_state()) {
1171    manager->SetInputMethodLoginDefault();
1172
1173    PrefService* prefs = g_browser_process->local_state();
1174    // Apply owner preferences for tap-to-click and mouse buttons swap for
1175    // login screen.
1176    system::InputDeviceSettings::Get()->SetPrimaryButtonRight(
1177        prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight));
1178    system::InputDeviceSettings::Get()->SetTapToClick(
1179        prefs->GetBoolean(prefs::kOwnerTapToClickEnabled));
1180  }
1181  system::InputDeviceSettings::Get()->SetNaturalScroll(
1182      CommandLine::ForCurrentProcess()->HasSwitch(
1183          switches::kNaturalScrollDefault));
1184
1185  gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));
1186
1187  g_browser_process->platform_part()->SessionManager()->SetSessionState(
1188      StartupUtils::IsOobeCompleted()
1189          ? session_manager::SESSION_STATE_LOGIN_PRIMARY
1190          : session_manager::SESSION_STATE_OOBE);
1191
1192  LoginDisplayHost* display_host = new LoginDisplayHostImpl(screen_bounds);
1193
1194  bool show_app_launch_splash_screen =
1195      (first_screen_name == WizardController::kAppLaunchSplashScreenName);
1196  if (show_app_launch_splash_screen) {
1197    const std::string& auto_launch_app_id =
1198        KioskAppManager::Get()->GetAutoLaunchApp();
1199    display_host->StartAppLaunch(auto_launch_app_id,
1200                                 false /* diagnostic_mode */);
1201    return;
1202  }
1203
1204  // Check whether we need to execute OOBE flow.
1205  bool oobe_complete = StartupUtils::IsOobeCompleted();
1206  policy::BrowserPolicyConnectorChromeOS* connector =
1207      g_browser_process->platform_part()->browser_policy_connector_chromeos();
1208  bool enrollment_screen_wanted =
1209      WizardController::ShouldRecoverEnrollment() ||
1210      (WizardController::ShouldAutoStartEnrollment() && oobe_complete &&
1211       !connector->IsEnterpriseManaged());
1212  if (enrollment_screen_wanted && first_screen_name.empty()) {
1213    // Shows networks screen instead of enrollment screen to resume the
1214    // interrupted auto start enrollment flow because enrollment screen does
1215    // not handle flaky network. See http://crbug.com/332572
1216    display_host->StartWizard(WizardController::kNetworkScreenName,
1217                              scoped_ptr<base::DictionaryValue>());
1218    return;
1219  }
1220
1221  if (StartupUtils::IsEulaAccepted()) {
1222    DelayNetworkCall(
1223        ServicesCustomizationDocument::GetInstance()
1224            ->EnsureCustomizationAppliedClosure(),
1225        base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS));
1226  }
1227
1228  bool show_login_screen =
1229      (first_screen_name.empty() && oobe_complete) ||
1230      first_screen_name == WizardController::kLoginScreenName;
1231
1232  if (show_login_screen) {
1233    display_host->StartSignInScreen(LoginScreenContext());
1234    return;
1235  }
1236
1237  // Load startup manifest.
1238  const StartupCustomizationDocument* startup_manifest =
1239      StartupCustomizationDocument::GetInstance();
1240
1241  // Switch to initial locale if specified by customization
1242  // and has not been set yet. We cannot call
1243  // LanguageSwitchMenu::SwitchLanguage here before
1244  // EmitLoginPromptReady.
1245  PrefService* prefs = g_browser_process->local_state();
1246  const std::string& current_locale =
1247      prefs->GetString(prefs::kApplicationLocale);
1248  VLOG(1) << "Current locale: " << current_locale;
1249  const std::string& locale = startup_manifest->initial_locale_default();
1250
1251  const std::string& layout = startup_manifest->keyboard_layout();
1252  VLOG(1) << "Initial locale: " << locale << "keyboard layout " << layout;
1253
1254  // Determine keyboard layout from OEM customization (if provided) or
1255  // initial locale and save it in preferences.
1256  manager->SetInputMethodLoginDefaultFromVPD(locale, layout);
1257
1258  if (!current_locale.empty() || locale.empty()) {
1259    ShowLoginWizardFinish(first_screen_name, startup_manifest, display_host);
1260    return;
1261  }
1262
1263  // Save initial locale from VPD/customization manifest as current
1264  // Chrome locale. Otherwise it will be lost if Chrome restarts.
1265  // Don't need to schedule pref save because setting initial local
1266  // will enforce preference saving.
1267  prefs->SetString(prefs::kApplicationLocale, locale);
1268  StartupUtils::SetInitialLocale(locale);
1269
1270  scoped_ptr<ShowLoginWizardSwitchLanguageCallbackData> data(
1271      new ShowLoginWizardSwitchLanguageCallbackData(
1272          first_screen_name, startup_manifest, display_host));
1273
1274  scoped_ptr<locale_util::SwitchLanguageCallback> callback(
1275      new locale_util::SwitchLanguageCallback(
1276          base::Bind(&OnLanguageSwitchedCallback, base::Passed(data.Pass()))));
1277
1278  // Load locale keyboards here. Hardware layout would be automatically enabled.
1279  locale_util::SwitchLanguage(
1280      locale, true, true /* login_layouts_only */, callback.Pass());
1281}
1282
1283}  // namespace chromeos
1284