Home | History | Annotate | Download | only in login
      1 // Copyright (c) 2012 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/login_display_host_impl.h"
      6 
      7 #include <vector>
      8 
      9 #include "ash/desktop_background/desktop_background_controller.h"
     10 #include "ash/desktop_background/user_wallpaper_delegate.h"
     11 #include "ash/shell.h"
     12 #include "ash/shell_window_ids.h"
     13 #include "ash/wm/window_properties.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/utf_string_conversions.h"
     20 #include "base/threading/thread_restrictions.h"
     21 #include "base/time/time.h"
     22 #include "base/values.h"
     23 #include "chrome/browser/browser_process.h"
     24 #include "chrome/browser/browser_shutdown.h"
     25 #include "chrome/browser/chrome_notification_types.h"
     26 #include "chrome/browser/chromeos/customization_document.h"
     27 #include "chrome/browser/chromeos/input_method/input_method_util.h"
     28 #include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
     29 #include "chrome/browser/chromeos/language_preferences.h"
     30 #include "chrome/browser/chromeos/login/existing_user_controller.h"
     31 #include "chrome/browser/chromeos/login/helper.h"
     32 #include "chrome/browser/chromeos/login/language_switch_menu.h"
     33 #include "chrome/browser/chromeos/login/login_utils.h"
     34 #include "chrome/browser/chromeos/login/login_wizard.h"
     35 #include "chrome/browser/chromeos/login/oobe_display.h"
     36 #include "chrome/browser/chromeos/login/startup_utils.h"
     37 #include "chrome/browser/chromeos/login/user_manager.h"
     38 #include "chrome/browser/chromeos/login/webui_login_display.h"
     39 #include "chrome/browser/chromeos/login/webui_login_view.h"
     40 #include "chrome/browser/chromeos/login/wizard_controller.h"
     41 #include "chrome/browser/chromeos/mobile_config.h"
     42 #include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
     43 #include "chrome/browser/chromeos/system/input_device_settings.h"
     44 #include "chrome/browser/chromeos/system/timezone_settings.h"
     45 #include "chrome/browser/chromeos/ui/focus_ring_controller.h"
     46 #include "chrome/browser/lifetime/application_lifetime.h"
     47 #include "chrome/browser/policy/browser_policy_connector.h"
     48 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
     49 #include "chrome/common/chrome_constants.h"
     50 #include "chrome/common/pref_names.h"
     51 #include "chromeos/chromeos_constants.h"
     52 #include "chromeos/chromeos_switches.h"
     53 #include "chromeos/dbus/dbus_thread_manager.h"
     54 #include "chromeos/dbus/session_manager_client.h"
     55 #include "chromeos/ime/input_method_manager.h"
     56 #include "chromeos/login/login_state.h"
     57 #include "content/public/browser/notification_service.h"
     58 #include "content/public/browser/notification_types.h"
     59 #include "content/public/browser/web_contents.h"
     60 #include "content/public/browser/web_contents_view.h"
     61 #include "content/public/browser/web_ui.h"
     62 #include "ui/aura/root_window.h"
     63 #include "ui/aura/window.h"
     64 #include "ui/base/events/event_utils.h"
     65 #include "ui/base/resource/resource_bundle.h"
     66 #include "ui/compositor/layer.h"
     67 #include "ui/compositor/layer_animation_element.h"
     68 #include "ui/compositor/layer_animation_sequence.h"
     69 #include "ui/compositor/layer_animator.h"
     70 #include "ui/compositor/scoped_layer_animation_settings.h"
     71 #include "ui/gfx/rect.h"
     72 #include "ui/gfx/transform.h"
     73 #include "ui/views/focus/focus_manager.h"
     74 #include "ui/views/widget/widget.h"
     75 #include "url/gurl.h"
     76 
     77 namespace {
     78 
     79 // URL which corresponds to the login WebUI.
     80 const char kLoginURL[] = "chrome://oobe/login#login";
     81 
     82 // URL which corresponds to the OOBE WebUI.
     83 const char kOobeURL[] = "chrome://oobe#login";
     84 
     85 // URL which corresponds to the user adding WebUI.
     86 const char kUserAddingURL[] = "chrome://oobe/login#user-adding";
     87 
     88 // Duration of sign-in transition animation.
     89 const int kLoginFadeoutTransitionDurationMs = 700;
     90 
     91 // Number of times we try to reload OOBE/login WebUI if it crashes.
     92 const int kCrashCountLimit = 5;
     93 
     94 // Whether to enable tnitializing WebUI in hidden state (see
     95 // |initialize_webui_hidden_|) by default.
     96 const bool kHiddenWebUIInitializationDefault = true;
     97 
     98 // Switch values that might be used to override WebUI init type.
     99 const char kWebUIInitParallel[] = "parallel";
    100 const char kWebUIInitPostpone[] = "postpone";
    101 
    102 // The delay of triggering initialization of the device policy subsystem
    103 // after the login screen is initialized. This makes sure that device policy
    104 // network requests are made while the system is idle waiting for user input.
    105 const int64 kPolicyServiceInitializationDelayMilliseconds = 100;
    106 
    107 // Determines the hardware keyboard from the given locale code
    108 // and the OEM layout information, and saves it to "Locale State".
    109 // The information will be used in InputMethodUtil::GetHardwareInputMethodId().
    110 void DetermineAndSaveHardwareKeyboard(const std::string& locale,
    111                                       const std::string& oem_layout) {
    112   std::string layout;
    113   if (!oem_layout.empty()) {
    114     // If the OEM layout information is provided, use it.
    115     layout = oem_layout;
    116   } else {
    117     chromeos::input_method::InputMethodManager* manager =
    118         chromeos::input_method::InputMethodManager::Get();
    119     // Otherwise, determine the hardware keyboard from the locale.
    120     std::vector<std::string> input_method_ids;
    121     if (manager->GetInputMethodUtil()->GetInputMethodIdsFromLanguageCode(
    122             locale,
    123             chromeos::input_method::kKeyboardLayoutsOnly,
    124             &input_method_ids)) {
    125       // The output list |input_method_ids| is sorted by popularity, hence
    126       // input_method_ids[0] now contains the most popular keyboard layout
    127       // for the given locale.
    128       layout = input_method_ids[0];
    129     }
    130   }
    131 
    132   if (!layout.empty()) {
    133     PrefService* prefs = g_browser_process->local_state();
    134     prefs->SetString(prefs::kHardwareKeyboardLayout, layout);
    135     // This asks the file thread to save the prefs (i.e. doesn't block).
    136     // The latest values of Local State reside in memory so we can safely
    137     // get the value of kHardwareKeyboardLayout even if the data is not
    138     // yet saved to disk.
    139     prefs->CommitPendingWrite();
    140   }
    141 }
    142 
    143 ui::Layer* GetLayer(views::Widget* widget) {
    144   return widget->GetNativeView()->layer();
    145 }
    146 
    147 }  // namespace
    148 
    149 namespace chromeos {
    150 
    151 // static
    152 LoginDisplayHost* LoginDisplayHostImpl::default_host_ = NULL;
    153 
    154 // static
    155 const int LoginDisplayHostImpl::kShowLoginWebUIid = 0x1111;
    156 
    157 ////////////////////////////////////////////////////////////////////////////////
    158 // LoginDisplayHostImpl, public
    159 
    160 LoginDisplayHostImpl::LoginDisplayHostImpl(const gfx::Rect& background_bounds)
    161     : background_bounds_(background_bounds),
    162       pointer_factory_(this),
    163       shutting_down_(false),
    164       oobe_progress_bar_visible_(false),
    165       session_starting_(false),
    166       login_window_(NULL),
    167       login_view_(NULL),
    168       webui_login_display_(NULL),
    169       is_showing_login_(false),
    170       is_wallpaper_loaded_(false),
    171       status_area_saved_visibility_(false),
    172       crash_count_(0),
    173       restore_path_(RESTORE_UNKNOWN),
    174       old_ignore_solo_window_frame_painter_policy_value_(false) {
    175   // We need to listen to CLOSE_ALL_BROWSERS_REQUEST but not APP_TERMINATING
    176   // because/ APP_TERMINATING will never be fired as long as this keeps
    177   // ref-count. CLOSE_ALL_BROWSERS_REQUEST is safe here because there will be no
    178   // browser instance that will block the shutdown.
    179   registrar_.Add(this,
    180                  chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
    181                  content::NotificationService::AllSources());
    182 
    183   // NOTIFICATION_BROWSER_OPENED is issued after browser is created, but
    184   // not shown yet. Lock window has to be closed at this point so that
    185   // a browser window exists and the window can acquire input focus.
    186   registrar_.Add(this,
    187                  chrome::NOTIFICATION_BROWSER_OPENED,
    188                  content::NotificationService::AllSources());
    189 
    190   // Login screen is moved to lock screen container when user logs in.
    191   registrar_.Add(this,
    192                  chrome::NOTIFICATION_LOGIN_USER_CHANGED,
    193                  content::NotificationService::AllSources());
    194 
    195   DCHECK(default_host_ == NULL);
    196   default_host_ = this;
    197 
    198   // Make sure chrome won't exit while we are at login/oobe screen.
    199   chrome::StartKeepAlive();
    200 
    201   bool is_registered = StartupUtils::IsDeviceRegistered();
    202   bool zero_delay_enabled = WizardController::IsZeroDelayEnabled();
    203   bool disable_boot_animation = CommandLine::ForCurrentProcess()->
    204       HasSwitch(switches::kDisableBootAnimation);
    205   bool disable_oobe_animation = CommandLine::ForCurrentProcess()->
    206       HasSwitch(switches::kDisableOobeAnimation);
    207 
    208   waiting_for_wallpaper_load_ =
    209       !zero_delay_enabled &&
    210       (is_registered || !disable_oobe_animation) &&
    211       (!is_registered || !disable_boot_animation);
    212 
    213   // For slower hardware we have boot animation disabled so
    214   // we'll be initializing WebUI hidden, waiting for user pods to load and then
    215   // show WebUI at once.
    216   waiting_for_user_pods_ = !zero_delay_enabled && !waiting_for_wallpaper_load_;
    217 
    218   initialize_webui_hidden_ =
    219       kHiddenWebUIInitializationDefault && !zero_delay_enabled;
    220 
    221   // Check if WebUI init type is overriden.
    222   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshWebUIInit)) {
    223     const std::string override_type = CommandLine::ForCurrentProcess()->
    224         GetSwitchValueASCII(switches::kAshWebUIInit);
    225     if (override_type == kWebUIInitParallel)
    226       initialize_webui_hidden_ = true;
    227     else if (override_type == kWebUIInitPostpone)
    228       initialize_webui_hidden_ = false;
    229   }
    230 
    231   // Always postpone WebUI initialization on first boot, otherwise we miss
    232   // initial animation.
    233   if (!StartupUtils::IsOobeCompleted())
    234     initialize_webui_hidden_ = false;
    235 
    236   // There is no wallpaper for KioskMode, don't initialize the webui hidden.
    237   if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
    238     initialize_webui_hidden_ = false;
    239 
    240   if (waiting_for_wallpaper_load_) {
    241     registrar_.Add(this, chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
    242                    content::NotificationService::AllSources());
    243   }
    244 
    245   // When we wait for WebUI to be initialized we wait for one of
    246   // these notifications.
    247   if ((waiting_for_user_pods_ || waiting_for_wallpaper_load_)
    248       && initialize_webui_hidden_) {
    249     registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
    250                    content::NotificationService::AllSources());
    251     registrar_.Add(this, chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
    252                    content::NotificationService::AllSources());
    253   }
    254   LOG(WARNING) << "Login WebUI >> "
    255                << "zero_delay: " << zero_delay_enabled
    256                << " wait_for_wp_load_: " << waiting_for_wallpaper_load_
    257                << " wait_for_pods_: " << waiting_for_user_pods_
    258                << " init_webui_hidden_: " << initialize_webui_hidden_;
    259 
    260   if (system::keyboard_settings::ForceKeyboardDrivenUINavigation()) {
    261     views::FocusManager::set_arrow_key_traversal_enabled(true);
    262 
    263     focus_ring_controller_.reset(new FocusRingController);
    264     focus_ring_controller_->SetVisible(true);
    265   }
    266 }
    267 
    268 LoginDisplayHostImpl::~LoginDisplayHostImpl() {
    269   views::FocusManager::set_arrow_key_traversal_enabled(false);
    270   ResetLoginWindowAndView();
    271 
    272   // Let chrome process exit after login/oobe screen if needed.
    273   chrome::EndKeepAlive();
    274 
    275   default_host_ = NULL;
    276 }
    277 
    278 ////////////////////////////////////////////////////////////////////////////////
    279 // LoginDisplayHostImpl, LoginDisplayHost implementation:
    280 
    281 LoginDisplay* LoginDisplayHostImpl::CreateLoginDisplay(
    282     LoginDisplay::Delegate* delegate) {
    283   webui_login_display_ = new WebUILoginDisplay(delegate);
    284   webui_login_display_->set_background_bounds(background_bounds());
    285   return webui_login_display_;
    286 }
    287 
    288 gfx::NativeWindow LoginDisplayHostImpl::GetNativeWindow() const {
    289   return login_window_ ? login_window_->GetNativeWindow() : NULL;
    290 }
    291 
    292 WebUILoginView* LoginDisplayHostImpl::GetWebUILoginView() const {
    293   return login_view_;
    294 }
    295 
    296 views::Widget* LoginDisplayHostImpl::GetWidget() const {
    297   return login_window_;
    298 }
    299 
    300 void LoginDisplayHostImpl::BeforeSessionStart() {
    301   session_starting_ = true;
    302 }
    303 
    304 void LoginDisplayHostImpl::Finalize() {
    305   DVLOG(1) << "Session starting";
    306   ash::Shell::GetInstance()->
    307       desktop_background_controller()->MoveDesktopToUnlockedContainer();
    308   if (wizard_controller_.get())
    309     wizard_controller_->OnSessionStart();
    310   if (!IsRunningUserAdding()) {
    311     // Display host is deleted once animation is completed
    312     // since sign in screen widget has to stay alive.
    313     StartAnimation();
    314   }
    315   ShutdownDisplayHost(false);
    316 }
    317 
    318 void LoginDisplayHostImpl::OnCompleteLogin() {
    319   // Cancelling the |auto_enrollment_client_| now allows it to determine whether
    320   // its protocol finished before login was complete.
    321   if (auto_enrollment_client_.get())
    322     auto_enrollment_client_.release()->CancelAndDeleteSoon();
    323 }
    324 
    325 void LoginDisplayHostImpl::OpenProxySettings() {
    326   if (login_view_)
    327     login_view_->OpenProxySettings();
    328 }
    329 
    330 void LoginDisplayHostImpl::SetOobeProgressBarVisible(bool visible) {
    331   GetOobeUI()->ShowOobeUI(visible);
    332 }
    333 
    334 void LoginDisplayHostImpl::SetShutdownButtonEnabled(bool enable) {
    335 }
    336 
    337 void LoginDisplayHostImpl::SetStatusAreaVisible(bool visible) {
    338   if (initialize_webui_hidden_)
    339     status_area_saved_visibility_ = visible;
    340   else if (login_view_)
    341     login_view_->SetStatusAreaVisible(visible);
    342 }
    343 
    344 void LoginDisplayHostImpl::CheckForAutoEnrollment() {
    345   // This method is called when the controller determines that the
    346   // auto-enrollment check can start. This happens either after the EULA is
    347   // accepted, or right after a reboot if the EULA has already been accepted.
    348 
    349   if (policy::AutoEnrollmentClient::IsDisabled()) {
    350     VLOG(1) << "CheckForAutoEnrollment: auto-enrollment disabled";
    351     return;
    352   }
    353 
    354   // Start by checking if the device has already been owned.
    355   pointer_factory_.InvalidateWeakPtrs();
    356   DeviceSettingsService::Get()->GetOwnershipStatusAsync(
    357       base::Bind(&LoginDisplayHostImpl::OnOwnershipStatusCheckDone,
    358                  pointer_factory_.GetWeakPtr()));
    359 }
    360 
    361 void LoginDisplayHostImpl::StartWizard(
    362     const std::string& first_screen_name,
    363     scoped_ptr<DictionaryValue> screen_parameters) {
    364   // Keep parameters to restore if renderer crashes.
    365   restore_path_ = RESTORE_WIZARD;
    366   wizard_first_screen_name_ = first_screen_name;
    367   if (screen_parameters.get())
    368     wizard_screen_parameters_.reset(screen_parameters->DeepCopy());
    369   else
    370     wizard_screen_parameters_.reset();
    371   is_showing_login_ = false;
    372 
    373   if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
    374     LOG(WARNING) << "Login WebUI >> wizard postponed";
    375     return;
    376   }
    377   LOG(WARNING) << "Login WebUI >> wizard";
    378 
    379   if (!login_window_)
    380     LoadURL(GURL(kOobeURL));
    381 
    382   DVLOG(1) << "Starting wizard, first_screen_name: " << first_screen_name;
    383   // Create and show the wizard.
    384   // Note, dtor of the old WizardController should be called before ctor of the
    385   // new one, because "default_controller()" is updated there. So pure "reset()"
    386   // is done before new controller creation.
    387   wizard_controller_.reset();
    388   wizard_controller_.reset(CreateWizardController());
    389 
    390   oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
    391   SetOobeProgressBarVisible(oobe_progress_bar_visible_);
    392   wizard_controller_->Init(first_screen_name, screen_parameters.Pass());
    393 }
    394 
    395 WizardController* LoginDisplayHostImpl::GetWizardController() {
    396   return wizard_controller_.get();
    397 }
    398 
    399 void LoginDisplayHostImpl::StartUserAdding(
    400     const base::Closure& completion_callback) {
    401   restore_path_ = RESTORE_ADD_USER_INTO_SESSION;
    402   completion_callback_ = completion_callback;
    403   LOG(WARNING) << "Login WebUI >> user adding";
    404   if (!login_window_)
    405     LoadURL(GURL(kUserAddingURL));
    406   // We should emit this signal only at login screen (after reboot or sign out).
    407   login_view_->set_should_emit_login_prompt_visible(false);
    408 
    409   // Lock container can be transparent after lock screen animation.
    410   aura::Window* lock_container = ash::Shell::GetContainer(
    411       ash::Shell::GetPrimaryRootWindow(),
    412       ash::internal::kShellWindowId_LockScreenContainersContainer);
    413   lock_container->layer()->SetOpacity(1.0);
    414 
    415   ash::Shell::GetInstance()->
    416       desktop_background_controller()->MoveDesktopToLockedContainer();
    417 
    418   sign_in_controller_.reset();  // Only one controller in a time.
    419   sign_in_controller_.reset(new chromeos::ExistingUserController(this));
    420   SetOobeProgressBarVisible(oobe_progress_bar_visible_ = false);
    421   SetStatusAreaVisible(true);
    422   SetShutdownButtonEnabled(true);
    423   sign_in_controller_->Init(
    424       chromeos::UserManager::Get()->GetUsersAdmittedForMultiProfile());
    425   CHECK(webui_login_display_);
    426   GetOobeUI()->ShowSigninScreen(webui_login_display_, webui_login_display_);
    427 }
    428 
    429 void LoginDisplayHostImpl::StartSignInScreen() {
    430   restore_path_ = RESTORE_SIGN_IN;
    431   is_showing_login_ = true;
    432 
    433   PrewarmAuthentication();
    434 
    435   if (waiting_for_wallpaper_load_ && !initialize_webui_hidden_) {
    436     LOG(WARNING) << "Login WebUI >> sign in postponed";
    437     return;
    438   }
    439   LOG(WARNING) << "Login WebUI >> sign in";
    440 
    441   if (!login_window_) {
    442     TRACE_EVENT_ASYNC_BEGIN0("ui", "ShowLoginWebUI", kShowLoginWebUIid);
    443     LoadURL(GURL(kLoginURL));
    444   }
    445 
    446   DVLOG(1) << "Starting sign in screen";
    447   const chromeos::UserList& users = chromeos::UserManager::Get()->GetUsers();
    448 
    449   // Fix for users who updated device and thus never passed register screen.
    450   // If we already have users, we assume that it is not a second part of
    451   // OOBE. See http://crosbug.com/6289
    452   if (!StartupUtils::IsDeviceRegistered() && !users.empty()) {
    453     VLOG(1) << "Mark device registered because there are remembered users: "
    454             << users.size();
    455     StartupUtils::MarkDeviceRegistered();
    456   }
    457 
    458   sign_in_controller_.reset();  // Only one controller in a time.
    459   sign_in_controller_.reset(new chromeos::ExistingUserController(this));
    460   oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
    461   SetOobeProgressBarVisible(oobe_progress_bar_visible_);
    462   SetStatusAreaVisible(true);
    463   SetShutdownButtonEnabled(true);
    464   sign_in_controller_->Init(users);
    465 
    466   // We might be here after a reboot that was triggered after OOBE was complete,
    467   // so check for auto-enrollment again. This might catch a cached decision from
    468   // a previous oobe flow, or might start a new check with the server.
    469   CheckForAutoEnrollment();
    470 
    471   // Initiate services customization manifest fetching.
    472   ServicesCustomizationDocument::GetInstance()->StartFetching();
    473 
    474   // Initiate mobile config load.
    475   MobileConfig::GetInstance();
    476 
    477   // Initiate device policy fetching.
    478   g_browser_process->browser_policy_connector()->ScheduleServiceInitialization(
    479       kPolicyServiceInitializationDelayMilliseconds);
    480 
    481   CHECK(webui_login_display_);
    482   GetOobeUI()->ShowSigninScreen(webui_login_display_, webui_login_display_);
    483   if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
    484     SetStatusAreaVisible(false);
    485 }
    486 
    487 void LoginDisplayHostImpl::ResumeSignInScreen() {
    488   // We only get here after a previous call the StartSignInScreen. That sign-in
    489   // was successful but was interrupted by an auto-enrollment execution; once
    490   // auto-enrollment is complete we resume the normal login flow from here.
    491   DVLOG(1) << "Resuming sign in screen";
    492   CHECK(sign_in_controller_.get());
    493   SetOobeProgressBarVisible(oobe_progress_bar_visible_);
    494   SetStatusAreaVisible(true);
    495   SetShutdownButtonEnabled(true);
    496   sign_in_controller_->ResumeLogin();
    497 }
    498 
    499 
    500 void LoginDisplayHostImpl::OnPreferencesChanged() {
    501   if (is_showing_login_)
    502     webui_login_display_->OnPreferencesChanged();
    503 }
    504 
    505 void LoginDisplayHostImpl::PrewarmAuthentication() {
    506   auth_prewarmer_.reset(new AuthPrewarmer());
    507   auth_prewarmer_->PrewarmAuthentication(
    508       base::Bind(&LoginDisplayHostImpl::OnAuthPrewarmDone,
    509                  pointer_factory_.GetWeakPtr()));
    510 }
    511 
    512 ////////////////////////////////////////////////////////////////////////////////
    513 // LoginDisplayHostImpl, public
    514 
    515 WizardController* LoginDisplayHostImpl::CreateWizardController() {
    516   // TODO(altimofeev): ensure that WebUI is ready.
    517   OobeDisplay* oobe_display = GetOobeUI();
    518   return new WizardController(this, oobe_display);
    519 }
    520 
    521 void LoginDisplayHostImpl::OnBrowserCreated() {
    522   // Close lock window now so that the launched browser can receive focus.
    523   ResetLoginWindowAndView();
    524 }
    525 
    526 OobeUI* LoginDisplayHostImpl::GetOobeUI() const {
    527   if (!login_view_)
    528     return NULL;
    529   return static_cast<OobeUI*>(login_view_->GetWebUI()->GetController());
    530 }
    531 
    532 ////////////////////////////////////////////////////////////////////////////////
    533 // LoginDisplayHostImpl, content:NotificationObserver implementation:
    534 
    535 void LoginDisplayHostImpl::Observe(
    536     int type,
    537     const content::NotificationSource& source,
    538     const content::NotificationDetails& details) {
    539   if (chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED == type) {
    540     LOG(WARNING) << "Login WebUI >> wp animation done";
    541     is_wallpaper_loaded_ = true;
    542     ash::Shell::GetInstance()->user_wallpaper_delegate()
    543         ->OnWallpaperBootAnimationFinished();
    544     if (waiting_for_wallpaper_load_) {
    545       // StartWizard / StartSignInScreen could be called multiple times through
    546       // the lifetime of host.
    547       // Make sure that subsequent calls are not postponed.
    548       waiting_for_wallpaper_load_ = false;
    549       if (initialize_webui_hidden_)
    550         ShowWebUI();
    551       else
    552         StartPostponedWebUI();
    553     }
    554     registrar_.Remove(this,
    555                       chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
    556                       content::NotificationService::AllSources());
    557   } else if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type ||
    558              chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN == type) {
    559     LOG(WARNING) << "Login WebUI >> WEBUI_VISIBLE";
    560     if (waiting_for_user_pods_ && initialize_webui_hidden_) {
    561       waiting_for_user_pods_ = false;
    562       ShowWebUI();
    563     } else if (waiting_for_wallpaper_load_ && initialize_webui_hidden_) {
    564       // Reduce time till login UI is shown - show it as soon as possible.
    565       waiting_for_wallpaper_load_ = false;
    566       ShowWebUI();
    567     }
    568     registrar_.Remove(this,
    569                       chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
    570                       content::NotificationService::AllSources());
    571     registrar_.Remove(this,
    572                       chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
    573                       content::NotificationService::AllSources());
    574   } else if (type == chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST) {
    575     ShutdownDisplayHost(true);
    576   } else if (type == chrome::NOTIFICATION_BROWSER_OPENED && session_starting_) {
    577     // Browsers created before session start (windows opened by extensions, for
    578     // example) are ignored.
    579     OnBrowserCreated();
    580     registrar_.Remove(this,
    581                       chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
    582                       content::NotificationService::AllSources());
    583     registrar_.Remove(this,
    584                       chrome::NOTIFICATION_BROWSER_OPENED,
    585                       content::NotificationService::AllSources());
    586   } else if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED &&
    587              chromeos::UserManager::Get()->IsCurrentUserNew()) {
    588     // For new user, move desktop to locker container so that windows created
    589     // during the user image picker step are below it.
    590     ash::Shell::GetInstance()->
    591         desktop_background_controller()->MoveDesktopToLockedContainer();
    592     registrar_.Remove(this,
    593                       chrome::NOTIFICATION_LOGIN_USER_CHANGED,
    594                       content::NotificationService::AllSources());
    595   }
    596 }
    597 
    598 ////////////////////////////////////////////////////////////////////////////////
    599 // LoginDisplayHostImpl, WebContentsObserver implementation:
    600 
    601 void LoginDisplayHostImpl::RenderProcessGone(base::TerminationStatus status) {
    602   // Do not try to restore on shutdown
    603   if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID)
    604     return;
    605 
    606   crash_count_++;
    607   if (crash_count_ > kCrashCountLimit)
    608     return;
    609 
    610   if (status != base::TERMINATION_STATUS_NORMAL_TERMINATION) {
    611     // Render with login screen crashed. Let's crash browser process to let
    612     // session manager restart it properly. It is hard to reload the page
    613     // and get to controlled state that is fully functional.
    614     // If you see check, search for renderer crash for the same client.
    615     LOG(FATAL) << "Renderer crash on login window";
    616   }
    617 }
    618 
    619 ////////////////////////////////////////////////////////////////////////////////
    620 // LoginDisplayHostImpl, private
    621 
    622 void LoginDisplayHostImpl::ShutdownDisplayHost(bool post_quit_task) {
    623   if (shutting_down_)
    624     return;
    625 
    626   shutting_down_ = true;
    627   registrar_.RemoveAll();
    628   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
    629   if (post_quit_task)
    630     base::MessageLoop::current()->Quit();
    631 
    632   if (!completion_callback_.is_null())
    633     completion_callback_.Run();
    634 }
    635 
    636 void LoginDisplayHostImpl::StartAnimation() {
    637   if (ash::Shell::GetContainer(
    638           ash::Shell::GetPrimaryRootWindow(),
    639           ash::internal::kShellWindowId_DesktopBackgroundContainer)->
    640           children().empty()) {
    641     // If there is no background window, don't perform any animation on the
    642     // default and background layer because there is nothing behind it.
    643     return;
    644   }
    645 
    646   if (!CommandLine::ForCurrentProcess()->HasSwitch(
    647           switches::kDisableLoginAnimations))
    648     ash::Shell::GetInstance()->DoInitialWorkspaceAnimation();
    649 }
    650 
    651 void LoginDisplayHostImpl::OnOwnershipStatusCheckDone(
    652     DeviceSettingsService::OwnershipStatus status,
    653     bool current_user_is_owner) {
    654   if (status != DeviceSettingsService::OWNERSHIP_NONE) {
    655     // The device is already owned. No need for auto-enrollment checks.
    656     VLOG(1) << "CheckForAutoEnrollment: device already owned";
    657     return;
    658   }
    659 
    660   // Kick off the auto-enrollment client.
    661   if (auto_enrollment_client_.get()) {
    662     // They client might have been started after the EULA screen, but we made
    663     // it to the login screen before it finished. In that case let the current
    664     // client proceed.
    665     //
    666     // CheckForAutoEnrollment() is also called when we reach the sign-in screen,
    667     // because that's what happens after an auto-update.
    668     VLOG(1) << "CheckForAutoEnrollment: client already started";
    669 
    670     // If the client already started and already finished too, pass the decision
    671     // to the |sign_in_controller_| now.
    672     if (auto_enrollment_client_->should_auto_enroll())
    673       ForceAutoEnrollment();
    674   } else {
    675     VLOG(1) << "CheckForAutoEnrollment: starting auto-enrollment client";
    676     auto_enrollment_client_.reset(policy::AutoEnrollmentClient::Create(
    677         base::Bind(&LoginDisplayHostImpl::OnAutoEnrollmentClientDone,
    678                    base::Unretained(this))));
    679     auto_enrollment_client_->Start();
    680   }
    681 }
    682 
    683 void LoginDisplayHostImpl::OnAutoEnrollmentClientDone() {
    684   bool auto_enroll = auto_enrollment_client_->should_auto_enroll();
    685   VLOG(1) << "OnAutoEnrollmentClientDone, decision is " << auto_enroll;
    686 
    687   if (auto_enroll)
    688     ForceAutoEnrollment();
    689 }
    690 
    691 void LoginDisplayHostImpl::ForceAutoEnrollment() {
    692   if (sign_in_controller_.get())
    693     sign_in_controller_->DoAutoEnrollment();
    694 }
    695 
    696 void LoginDisplayHostImpl::LoadURL(const GURL& url) {
    697   InitLoginWindowAndView();
    698   // Subscribe to crash events.
    699   content::WebContentsObserver::Observe(login_view_->GetWebContents());
    700   login_view_->LoadURL(url);
    701 }
    702 
    703 void LoginDisplayHostImpl::ShowWebUI() {
    704   if (!login_window_ || !login_view_) {
    705     NOTREACHED();
    706     return;
    707   }
    708   LOG(WARNING) << "Login WebUI >> Show already initialized UI";
    709   login_window_->Show();
    710   login_view_->GetWebContents()->GetView()->Focus();
    711   login_view_->SetStatusAreaVisible(status_area_saved_visibility_);
    712   login_view_->OnPostponedShow();
    713   // We should reset this flag to allow changing of status area visibility.
    714   initialize_webui_hidden_ = false;
    715 }
    716 
    717 void LoginDisplayHostImpl::StartPostponedWebUI() {
    718   if (!is_wallpaper_loaded_) {
    719     NOTREACHED();
    720     return;
    721   }
    722   LOG(WARNING) << "Login WebUI >> Init postponed WebUI";
    723 
    724   // Wallpaper has finished loading before StartWizard/StartSignInScreen has
    725   // been called. In general this should not happen.
    726   // Let go through normal code path when one of those will be called.
    727   if (restore_path_ == RESTORE_UNKNOWN) {
    728     NOTREACHED();
    729     return;
    730   }
    731 
    732   switch (restore_path_) {
    733     case RESTORE_WIZARD:
    734       StartWizard(wizard_first_screen_name_,
    735                   wizard_screen_parameters_.Pass());
    736       break;
    737     case RESTORE_SIGN_IN:
    738       StartSignInScreen();
    739       break;
    740     case RESTORE_ADD_USER_INTO_SESSION:
    741       StartUserAdding(completion_callback_);
    742       break;
    743     default:
    744       NOTREACHED();
    745       break;
    746   }
    747 }
    748 
    749 void LoginDisplayHostImpl::InitLoginWindowAndView() {
    750   if (login_window_)
    751     return;
    752 
    753   views::Widget::InitParams params(
    754       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    755   params.bounds = background_bounds();
    756   params.show_state = ui::SHOW_STATE_FULLSCREEN;
    757   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    758   params.parent =
    759       ash::Shell::GetContainer(
    760           ash::Shell::GetPrimaryRootWindow(),
    761           ash::internal::kShellWindowId_LockScreenContainer);
    762 
    763   login_window_ = new views::Widget;
    764   login_window_->Init(params);
    765   if (login_window_->GetNativeWindow()) {
    766     aura::RootWindow* root = login_window_->GetNativeWindow()->GetRootWindow();
    767     if (root) {
    768       old_ignore_solo_window_frame_painter_policy_value_ =
    769           root->GetProperty(ash::internal::kIgnoreSoloWindowFramePainterPolicy);
    770       root->SetProperty(ash::internal::kIgnoreSoloWindowFramePainterPolicy,
    771                         true);
    772     }
    773   }
    774   login_view_ = new WebUILoginView();
    775 
    776   login_view_->Init(login_window_);
    777 
    778   views::corewm::SetWindowVisibilityAnimationDuration(
    779       login_window_->GetNativeView(),
    780       base::TimeDelta::FromMilliseconds(kLoginFadeoutTransitionDurationMs));
    781   views::corewm::SetWindowVisibilityAnimationTransition(
    782       login_window_->GetNativeView(),
    783       views::corewm::ANIMATE_HIDE);
    784 
    785   login_window_->SetContentsView(login_view_);
    786   login_view_->UpdateWindowType();
    787 
    788   // If WebUI is initialized in hidden state, show it only if we're no
    789   // longer waiting for wallpaper animation/user images loading. Otherwise,
    790   // always show it.
    791   if (!initialize_webui_hidden_ ||
    792       (!waiting_for_wallpaper_load_ && !waiting_for_user_pods_)) {
    793     LOG(WARNING) << "Login WebUI >> show login wnd on create";
    794     login_window_->Show();
    795   } else {
    796     LOG(WARNING) << "Login WebUI >> login wnd is hidden on create";
    797     login_view_->set_is_hidden(true);
    798   }
    799   login_window_->GetNativeView()->SetName("WebUILoginView");
    800   login_view_->OnWindowCreated();
    801 }
    802 
    803 void LoginDisplayHostImpl::ResetLoginWindowAndView() {
    804   if (!login_window_)
    805     return;
    806 
    807   if (login_window_->GetNativeWindow()) {
    808     aura::RootWindow* root = login_window_->GetNativeWindow()->GetRootWindow();
    809     if (root) {
    810       root->SetProperty(ash::internal::kIgnoreSoloWindowFramePainterPolicy,
    811                         old_ignore_solo_window_frame_painter_policy_value_);
    812     }
    813   }
    814   login_window_->Close();
    815   login_window_ = NULL;
    816   login_view_ = NULL;
    817 }
    818 
    819 bool LoginDisplayHostImpl::IsRunningUserAdding() {
    820   return restore_path_ == RESTORE_ADD_USER_INTO_SESSION;
    821 }
    822 
    823 void LoginDisplayHostImpl::OnAuthPrewarmDone() {
    824   auth_prewarmer_.reset();
    825 }
    826 
    827 ////////////////////////////////////////////////////////////////////////////////
    828 // external
    829 
    830 // Declared in login_wizard.h so that others don't need to depend on our .h.
    831 // TODO(nkostylev): Split this into a smaller functions.
    832 void ShowLoginWizard(const std::string& first_screen_name) {
    833   if (browser_shutdown::IsTryingToQuit())
    834     return;
    835 
    836   VLOG(1) << "Showing OOBE screen: " << first_screen_name;
    837 
    838   chromeos::input_method::InputMethodManager* manager =
    839       chromeos::input_method::InputMethodManager::Get();
    840 
    841   // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
    842   // and US dvorak keyboard layouts.
    843   if (g_browser_process && g_browser_process->local_state()) {
    844     manager->SetInputMethodDefault();
    845 
    846     PrefService* prefs = g_browser_process->local_state();
    847     // Apply owner preferences for tap-to-click and mouse buttons swap for
    848     // login screen.
    849     system::mouse_settings::SetPrimaryButtonRight(
    850         prefs->GetBoolean(prefs::kOwnerPrimaryMouseButtonRight));
    851     system::touchpad_settings::SetTapToClick(
    852       prefs->GetBoolean(prefs::kOwnerTapToClickEnabled));
    853   }
    854 
    855   ui::SetNaturalScroll(CommandLine::ForCurrentProcess()->HasSwitch(
    856       switches::kNaturalScrollDefault));
    857 
    858   gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));
    859 
    860   // Check whether we need to execute OOBE process.
    861   bool oobe_complete = chromeos::StartupUtils::IsOobeCompleted();
    862   if (!oobe_complete) {
    863     LoginState::Get()->SetLoggedInState(
    864         LoginState::LOGGED_IN_OOBE, LoginState::LOGGED_IN_USER_NONE);
    865   } else {
    866     LoginState::Get()->SetLoggedInState(
    867         LoginState::LOGGED_IN_NONE, LoginState::LOGGED_IN_USER_NONE);
    868   }
    869   bool show_login_screen =
    870       (first_screen_name.empty() && oobe_complete) ||
    871       first_screen_name == chromeos::WizardController::kLoginScreenName;
    872 
    873   chromeos::LoginDisplayHost* display_host =
    874       new chromeos::LoginDisplayHostImpl(screen_bounds);
    875 
    876   if (show_login_screen) {
    877     // R11 > R12 migration fix. See http://crosbug.com/p/4898.
    878     // If user has manually changed locale during R11 OOBE, locale will be set.
    879     // On R12 > R12|R13 etc. this fix won't get activated since
    880     // OOBE process has set kApplicationLocale to non-default value.
    881     PrefService* prefs = g_browser_process->local_state();
    882     if (!prefs->HasPrefPath(prefs::kApplicationLocale)) {
    883       std::string locale = chromeos::StartupUtils::GetInitialLocale();
    884       prefs->SetString(prefs::kApplicationLocale, locale);
    885       manager->EnableLayouts(
    886           locale,
    887           manager->GetInputMethodUtil()->GetHardwareInputMethodId());
    888       base::ThreadRestrictions::ScopedAllowIO allow_io;
    889       const std::string loaded_locale =
    890           ResourceBundle::GetSharedInstance().ReloadLocaleResources(locale);
    891       g_browser_process->SetApplicationLocale(loaded_locale);
    892     }
    893     display_host->StartSignInScreen();
    894     return;
    895   }
    896 
    897   // Load startup manifest.
    898   const chromeos::StartupCustomizationDocument* startup_manifest =
    899       chromeos::StartupCustomizationDocument::GetInstance();
    900 
    901   // Switch to initial locale if specified by customization
    902   // and has not been set yet. We cannot call
    903   // chromeos::LanguageSwitchMenu::SwitchLanguage here before
    904   // EmitLoginPromptReady.
    905   PrefService* prefs = g_browser_process->local_state();
    906   const std::string current_locale =
    907       prefs->GetString(prefs::kApplicationLocale);
    908   VLOG(1) << "Current locale: " << current_locale;
    909   std::string locale;
    910   if (current_locale.empty()) {
    911     locale = startup_manifest->initial_locale();
    912     std::string layout = startup_manifest->keyboard_layout();
    913     VLOG(1) << "Initial locale: " << locale
    914             << "keyboard layout " << layout;
    915     if (!locale.empty()) {
    916       // Save initial locale from VPD/customization manifest as current
    917       // Chrome locale. Otherwise it will be lost if Chrome restarts.
    918       // Don't need to schedule pref save because setting initial local
    919       // will enforce preference saving.
    920       prefs->SetString(prefs::kApplicationLocale, locale);
    921       chromeos::StartupUtils::SetInitialLocale(locale);
    922       // Determine keyboard layout from OEM customization (if provided) or
    923       // initial locale and save it in preferences.
    924       DetermineAndSaveHardwareKeyboard(locale, layout);
    925       // Then, enable the hardware keyboard.
    926       manager->EnableLayouts(
    927           locale,
    928           manager->GetInputMethodUtil()->GetHardwareInputMethodId());
    929       // Reloading resource bundle causes us to do blocking IO on UI thread.
    930       // Temporarily allow it until we fix http://crosbug.com/11102
    931       base::ThreadRestrictions::ScopedAllowIO allow_io;
    932       const std::string loaded_locale =
    933           ResourceBundle::GetSharedInstance().ReloadLocaleResources(locale);
    934       CHECK(!loaded_locale.empty()) << "Locale could not be found for "
    935                                     << locale;
    936       // Set the application locale here so that the language switch
    937       // menu works properly with the newly loaded locale.
    938       g_browser_process->SetApplicationLocale(loaded_locale);
    939 
    940       // Reload font settings here to use correct font for initial_locale.
    941       LanguageSwitchMenu::LoadFontsForCurrentLocale();
    942     }
    943   }
    944 
    945   scoped_ptr<DictionaryValue> params;
    946   display_host->StartWizard(first_screen_name, params.Pass());
    947 
    948   chromeos::DBusThreadManager::Get()->GetSessionManagerClient()
    949       ->EmitLoginPromptReady();
    950   TRACE_EVENT0("chromeos", "ShowLoginWizard::EmitLoginPromptReady");
    951 
    952   // Set initial timezone if specified by customization.
    953   const std::string timezone_name = startup_manifest->initial_timezone();
    954   VLOG(1) << "Initial time zone: " << timezone_name;
    955   // Apply locale customizations only once to preserve whatever locale
    956   // user has changed to during OOBE.
    957   if (!timezone_name.empty()) {
    958     chromeos::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
    959         UTF8ToUTF16(timezone_name));
    960   }
    961 }
    962 
    963 }  // namespace chromeos
    964