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