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