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