1// Copyright (c) 2013 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/ui/webui/chromeos/login/signin_screen_handler.h"
6
7#include "base/callback.h"
8#include "base/command_line.h"
9#include "base/debug/trace_event.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/metrics/histogram.h"
13#include "base/prefs/pref_registry_simple.h"
14#include "base/prefs/pref_service.h"
15#include "base/prefs/scoped_user_pref_update.h"
16#include "base/strings/string16.h"
17#include "base/strings/string_util.h"
18#include "base/strings/stringprintf.h"
19#include "base/strings/utf_string_conversions.h"
20#include "chrome/browser/browser_process.h"
21#include "chrome/browser/browser_process_platform_part_chromeos.h"
22#include "chrome/browser/browser_shutdown.h"
23#include "chrome/browser/chrome_notification_types.h"
24#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
25#include "chrome/browser/chromeos/boot_times_loader.h"
26#include "chrome/browser/chromeos/input_method/input_method_util.h"
27#include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
28#include "chrome/browser/chromeos/login/hwid_checker.h"
29#include "chrome/browser/chromeos/login/login_display_host_impl.h"
30#include "chrome/browser/chromeos/login/screen_locker.h"
31#include "chrome/browser/chromeos/login/screens/core_oobe_actor.h"
32#include "chrome/browser/chromeos/login/user.h"
33#include "chrome/browser/chromeos/login/webui_login_display.h"
34#include "chrome/browser/chromeos/login/wizard_controller.h"
35#include "chrome/browser/chromeos/net/network_portal_detector.h"
36#include "chrome/browser/chromeos/profiles/profile_helper.h"
37#include "chrome/browser/chromeos/settings/cros_settings.h"
38#include "chrome/browser/io_thread.h"
39#include "chrome/browser/policy/browser_policy_connector.h"
40#include "chrome/browser/profiles/profile.h"
41#include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
42#include "chrome/browser/ui/webui/chromeos/login/native_window_delegate.h"
43#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
44#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
45#include "chrome/common/chrome_switches.h"
46#include "chrome/common/pref_names.h"
47#include "chrome/common/url_constants.h"
48#include "chromeos/chromeos_switches.h"
49#include "chromeos/dbus/dbus_thread_manager.h"
50#include "chromeos/dbus/power_manager_client.h"
51#include "chromeos/ime/input_method_manager.h"
52#include "chromeos/ime/xkeyboard.h"
53#include "chromeos/network/network_state.h"
54#include "chromeos/network/network_state_handler.h"
55#include "content/public/browser/render_view_host.h"
56#include "content/public/browser/web_contents.h"
57#include "google_apis/gaia/gaia_auth_util.h"
58#include "grit/chromium_strings.h"
59#include "grit/generated_resources.h"
60#include "third_party/cros_system_api/dbus/service_constants.h"
61
62#if defined(USE_AURA)
63#include "ash/shell.h"
64#include "ash/wm/lock_state_controller.h"
65#endif
66
67using content::BrowserThread;
68using content::RenderViewHost;
69
70namespace {
71
72// User dictionary keys.
73const char kKeyUsername[] = "username";
74const char kKeyDisplayName[] = "displayName";
75const char kKeyEmailAddress[] = "emailAddress";
76const char kKeyEnterpriseDomain[] = "enterpriseDomain";
77const char kKeyPublicAccount[] = "publicAccount";
78const char kKeyLocallyManagedUser[] = "locallyManagedUser";
79const char kKeySignedIn[] = "signedIn";
80const char kKeyCanRemove[] = "canRemove";
81const char kKeyIsOwner[] = "isOwner";
82const char kKeyOauthTokenStatus[] = "oauthTokenStatus";
83
84// Max number of users to show.
85const size_t kMaxUsers = 18;
86
87// Timeout to delay first notification about offline state for a
88// current network.
89const int kOfflineTimeoutSec = 5;
90
91// Timeout used to prevent infinite connecting to a flaky network.
92const int kConnectingTimeoutSec = 60;
93
94// Type of the login screen UI that is currently presented to user.
95const char kSourceGaiaSignin[] = "gaia-signin";
96const char kSourceAccountPicker[] = "account-picker";
97
98// The Task posted to PostTaskAndReply in StartClearingDnsCache on the IO
99// thread.
100void ClearDnsCache(IOThread* io_thread) {
101  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
102  if (browser_shutdown::IsTryingToQuit())
103    return;
104
105  io_thread->ClearHostCache();
106}
107
108static bool Contains(const std::vector<std::string>& container,
109                     const std::string& value) {
110  return std::find(container.begin(), container.end(), value) !=
111         container.end();
112}
113
114}  // namespace
115
116namespace chromeos {
117
118namespace {
119
120bool IsOnline(NetworkStateInformer::State state,
121              ErrorScreenActor::ErrorReason reason) {
122  return state == NetworkStateInformer::ONLINE &&
123      reason != ErrorScreenActor::ERROR_REASON_PORTAL_DETECTED &&
124      reason != ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT;
125}
126
127bool IsUnderCaptivePortal(NetworkStateInformer::State state,
128                          ErrorScreenActor::ErrorReason reason) {
129  return state == NetworkStateInformer::CAPTIVE_PORTAL ||
130      reason == ErrorScreenActor::ERROR_REASON_PORTAL_DETECTED;
131}
132
133bool IsProxyError(NetworkStateInformer::State state,
134                  ErrorScreenActor::ErrorReason reason,
135                  net::Error frame_error) {
136  return state == NetworkStateInformer::PROXY_AUTH_REQUIRED ||
137      reason == ErrorScreenActor::ERROR_REASON_PROXY_AUTH_CANCELLED ||
138      reason == ErrorScreenActor::ERROR_REASON_PROXY_CONNECTION_FAILED ||
139      (reason == ErrorScreenActor::ERROR_REASON_FRAME_ERROR &&
140       (frame_error == net::ERR_PROXY_CONNECTION_FAILED ||
141        frame_error == net::ERR_TUNNEL_CONNECTION_FAILED));
142}
143
144bool IsSigninScreen(const OobeUI::Screen screen) {
145  return screen == OobeUI::SCREEN_GAIA_SIGNIN ||
146      screen == OobeUI::SCREEN_ACCOUNT_PICKER;
147}
148
149bool IsSigninScreenError(ErrorScreen::ErrorState error_state) {
150  return error_state == ErrorScreen::ERROR_STATE_PORTAL ||
151      error_state == ErrorScreen::ERROR_STATE_OFFLINE ||
152      error_state == ErrorScreen::ERROR_STATE_PROXY ||
153      error_state == ErrorScreen::ERROR_STATE_AUTH_EXT_TIMEOUT;
154}
155
156// Returns network name by service path.
157std::string GetNetworkName(const std::string& service_path) {
158  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
159      GetNetworkState(service_path);
160  if (!network)
161    return std::string();
162  return network->name();
163}
164
165// Returns captive portal state for a network by its service path.
166NetworkPortalDetector::CaptivePortalState GetCaptivePortalState(
167    const std::string& service_path) {
168  NetworkPortalDetector* detector = NetworkPortalDetector::Get();
169  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
170      GetNetworkState(service_path);
171  if (!detector || !network)
172    return NetworkPortalDetector::CaptivePortalState();
173  return detector->GetCaptivePortalState(network);
174}
175
176void RecordDiscrepancyWithShill(
177    const NetworkState* network,
178    const NetworkPortalDetector::CaptivePortalStatus status) {
179  if (network->connection_state() == shill::kStateOnline) {
180    UMA_HISTOGRAM_ENUMERATION(
181        "CaptivePortal.OOBE.DiscrepancyWithShill_Online",
182        status,
183        NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
184  } else if (network->connection_state() == shill::kStatePortal) {
185    UMA_HISTOGRAM_ENUMERATION(
186        "CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool",
187        status,
188        NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
189  } else {
190    UMA_HISTOGRAM_ENUMERATION(
191        "CaptivePortal.OOBE.DiscrepancyWithShill_Offline",
192        status,
193        NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
194  }
195}
196
197// Record state and descripancies with shill (e.g. shill thinks that
198// network is online but NetworkPortalDetector claims that it's behind
199// portal) for the network identified by |service_path|.
200void RecordNetworkPortalDetectorStats(const std::string& service_path) {
201  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
202      GetNetworkState(service_path);
203  if (!network)
204    return;
205  NetworkPortalDetector::CaptivePortalState state =
206      GetCaptivePortalState(service_path);
207  if (state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN)
208    return;
209
210  UMA_HISTOGRAM_ENUMERATION("CaptivePortal.OOBE.DetectionResult",
211                            state.status,
212                            NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
213
214  switch (state.status) {
215    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
216      NOTREACHED();
217      break;
218    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
219      if (network->connection_state() == shill::kStateOnline ||
220          network->connection_state() == shill::kStatePortal)
221        RecordDiscrepancyWithShill(network, state.status);
222      break;
223    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
224      if (network->connection_state() != shill::kStateOnline)
225        RecordDiscrepancyWithShill(network, state.status);
226      break;
227    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
228      if (network->connection_state() != shill::kStatePortal)
229        RecordDiscrepancyWithShill(network, state.status);
230      break;
231    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
232      if (network->connection_state() != shill::kStateOnline)
233        RecordDiscrepancyWithShill(network, state.status);
234      break;
235    case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT:
236      NOTREACHED();
237      break;
238  }
239}
240
241static bool SetUserInputMethodImpl(
242    const std::string& username,
243    chromeos::input_method::InputMethodManager* manager) {
244  PrefService* const local_state = g_browser_process->local_state();
245
246  const base::DictionaryValue* users_lru_input_methods =
247      local_state->GetDictionary(prefs::kUsersLRUInputMethod);
248
249  if (users_lru_input_methods == NULL) {
250    DLOG(WARNING) << "SetUserInputMethod('" << username
251                  << "'): no kUsersLRUInputMethod";
252    return false;
253  }
254
255  std::string input_method;
256
257  if (!users_lru_input_methods->GetStringWithoutPathExpansion(username,
258                                                              &input_method)) {
259    DVLOG(0) << "SetUserInputMethod('" << username
260               << "'): no input method for this user";
261    return false;
262  }
263
264  if (input_method.empty())
265    return false;
266
267  if (!manager->IsLoginKeyboard(input_method)) {
268    LOG(WARNING) << "SetUserInputMethod('" << username
269                 << "'): stored user LRU input method '" << input_method
270                 << "' is no longer Full Latin Keyboard Language"
271                 << " (entry dropped). Use hardware default instead.";
272
273    DictionaryPrefUpdate updater(local_state, prefs::kUsersLRUInputMethod);
274
275    base::DictionaryValue* const users_lru_input_methods = updater.Get();
276    if (users_lru_input_methods != NULL) {
277      users_lru_input_methods->SetStringWithoutPathExpansion(username, "");
278    }
279    return false;
280  }
281
282  if (!Contains(manager->GetActiveInputMethodIds(), input_method)) {
283    if (!manager->EnableInputMethod(input_method)) {
284      DLOG(ERROR) << "SigninScreenHandler::SetUserInputMethod('" << username
285                  << "'): user input method '" << input_method
286                  << "' is not enabled and enabling failed (ignored!).";
287    }
288  }
289  manager->ChangeInputMethod(input_method);
290
291  return true;
292}
293
294}  // namespace
295
296// LoginScreenContext implementation ------------------------------------------
297
298LoginScreenContext::LoginScreenContext() {
299  Init();
300}
301
302LoginScreenContext::LoginScreenContext(const base::ListValue* args) {
303  Init();
304
305  if (!args || args->GetSize() == 0)
306    return;
307  std::string email;
308  if (args->GetString(0, &email))
309    email_ = email;
310}
311
312void LoginScreenContext::Init() {
313  oobe_ui_ = false;
314}
315
316// SigninScreenHandler implementation ------------------------------------------
317
318SigninScreenHandler::SigninScreenHandler(
319    const scoped_refptr<NetworkStateInformer>& network_state_informer,
320    ErrorScreenActor* error_screen_actor,
321    CoreOobeActor* core_oobe_actor,
322    GaiaScreenHandler* gaia_screen_handler)
323    : ui_state_(UI_STATE_UNKNOWN),
324      delegate_(NULL),
325      native_window_delegate_(NULL),
326      show_on_init_(false),
327      oobe_ui_(false),
328      focus_stolen_(false),
329      gaia_silent_load_(false),
330      is_account_picker_showing_first_time_(false),
331      dns_cleared_(false),
332      dns_clear_task_running_(false),
333      cookies_cleared_(false),
334      network_state_informer_(network_state_informer),
335      test_expects_complete_login_(false),
336      weak_factory_(this),
337      webui_visible_(false),
338      preferences_changed_delayed_(false),
339      error_screen_actor_(error_screen_actor),
340      core_oobe_actor_(core_oobe_actor),
341      is_first_update_state_call_(true),
342      offline_login_active_(false),
343      last_network_state_(NetworkStateInformer::UNKNOWN),
344      has_pending_auth_ui_(false),
345      wait_for_auto_enrollment_check_(false),
346      gaia_screen_handler_(gaia_screen_handler) {
347  DCHECK(network_state_informer_.get());
348  DCHECK(error_screen_actor_);
349  DCHECK(core_oobe_actor_);
350  DCHECK(gaia_screen_handler_);
351  gaia_screen_handler_->SetSigninScreenHandler(this);
352  network_state_informer_->AddObserver(this);
353  allow_new_user_subscription_ = CrosSettings::Get()->AddSettingsObserver(
354      kAccountsPrefAllowNewUser,
355      base::Bind(&SigninScreenHandler::UserSettingsChanged,
356                 base::Unretained(this)));
357  allow_guest_subscription_ = CrosSettings::Get()->AddSettingsObserver(
358      kAccountsPrefAllowGuest,
359      base::Bind(&SigninScreenHandler::UserSettingsChanged,
360                 base::Unretained(this)));
361
362  registrar_.Add(this,
363                 chrome::NOTIFICATION_AUTH_NEEDED,
364                 content::NotificationService::AllSources());
365  registrar_.Add(this,
366                 chrome::NOTIFICATION_AUTH_SUPPLIED,
367                 content::NotificationService::AllSources());
368  registrar_.Add(this,
369                 chrome::NOTIFICATION_AUTH_CANCELLED,
370                 content::NotificationService::AllSources());
371
372  WallpaperManager::Get()->AddObserver(this);
373}
374
375SigninScreenHandler::~SigninScreenHandler() {
376  WallpaperManager::Get()->RemoveObserver(this);
377  weak_factory_.InvalidateWeakPtrs();
378  SystemKeyEventListener* key_event_listener =
379      SystemKeyEventListener::GetInstance();
380  if (key_event_listener)
381    key_event_listener->RemoveCapsLockObserver(this);
382  if (delegate_)
383    delegate_->SetWebUIHandler(NULL);
384  network_state_informer_->RemoveObserver(this);
385}
386
387void SigninScreenHandler::DeclareLocalizedValues(
388    LocalizedValuesBuilder* builder) {
389  builder->Add("passwordHint", IDS_LOGIN_POD_EMPTY_PASSWORD_TEXT);
390  builder->Add("podMenuButtonAccessibleName",
391               IDS_LOGIN_POD_MENU_BUTTON_ACCESSIBLE_NAME);
392  builder->Add("podMenuRemoveItemAccessibleName",
393               IDS_LOGIN_POD_MENU_REMOVE_ITEM_ACCESSIBLE_NAME);
394  builder->Add("passwordFieldAccessibleName",
395               IDS_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME);
396  builder->Add("signedIn", IDS_SCREEN_LOCK_ACTIVE_USER);
397  builder->Add("signinButton", IDS_LOGIN_BUTTON);
398  builder->Add("shutDown", IDS_SHUTDOWN_BUTTON);
399  builder->Add("addUser", IDS_ADD_USER_BUTTON);
400  builder->Add("browseAsGuest", IDS_GO_INCOGNITO_BUTTON);
401  builder->Add("cancel", IDS_CANCEL);
402  builder->Add("signOutUser", IDS_SCREEN_LOCK_SIGN_OUT);
403  builder->Add("offlineLogin", IDS_OFFLINE_LOGIN_HTML);
404  builder->Add("ownerUserPattern", IDS_LOGIN_POD_OWNER_USER);
405  builder->Add("removeUser", IDS_LOGIN_POD_REMOVE_USER);
406  builder->Add("errorTpmFailureTitle", IDS_LOGIN_ERROR_TPM_FAILURE_TITLE);
407  builder->Add("errorTpmFailureReboot", IDS_LOGIN_ERROR_TPM_FAILURE_REBOOT);
408  builder->Add("errorTpmFailureRebootButton",
409               IDS_LOGIN_ERROR_TPM_FAILURE_REBOOT_BUTTON);
410  builder->Add(
411      "disabledAddUserTooltip",
412      g_browser_process->browser_policy_connector()->IsEnterpriseManaged() ?
413          IDS_DISABLED_ADD_USER_TOOLTIP_ENTERPRISE :
414          IDS_DISABLED_ADD_USER_TOOLTIP);
415  builder->Add("supervisedUserExpiredTokenWarning",
416               IDS_SUPERVISED_USER_EXPIRED_TOKEN_WARNING);
417  builder->Add("signinBannerText", IDS_LOGIN_USER_ADDING_BANNER);
418
419  // Strings used by password changed dialog.
420  builder->Add("passwordChangedTitle", IDS_LOGIN_PASSWORD_CHANGED_TITLE);
421  builder->Add("passwordChangedDesc", IDS_LOGIN_PASSWORD_CHANGED_DESC);
422  builder->AddF("passwordChangedMoreInfo",
423                IDS_LOGIN_PASSWORD_CHANGED_MORE_INFO,
424                IDS_SHORT_PRODUCT_OS_NAME);
425
426  builder->Add("oldPasswordHint", IDS_LOGIN_PASSWORD_CHANGED_OLD_PASSWORD_HINT);
427  builder->Add("oldPasswordIncorrect",
428               IDS_LOGIN_PASSWORD_CHANGED_INCORRECT_OLD_PASSWORD);
429  builder->Add("passwordChangedCantRemember",
430               IDS_LOGIN_PASSWORD_CHANGED_CANT_REMEMBER);
431  builder->Add("passwordChangedBackButton",
432               IDS_LOGIN_PASSWORD_CHANGED_BACK_BUTTON);
433  builder->Add("passwordChangedsOkButton", IDS_OK);
434  builder->Add("passwordChangedProceedAnyway",
435               IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY);
436  builder->Add("proceedAnywayButton",
437               IDS_LOGIN_PASSWORD_CHANGED_PROCEED_ANYWAY_BUTTON);
438  builder->Add("publicAccountInfoFormat", IDS_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT);
439  builder->Add("publicAccountReminder",
440               IDS_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER);
441  builder->Add("publicAccountEnter", IDS_LOGIN_PUBLIC_ACCOUNT_ENTER);
442  builder->Add("publicAccountEnterAccessibleName",
443               IDS_LOGIN_PUBLIC_ACCOUNT_ENTER_ACCESSIBLE_NAME);
444  builder->AddF("removeUserWarningText",
445               IDS_LOGIN_POD_USER_REMOVE_WARNING,
446               UTF8ToUTF16(chrome::kSupervisedUserManagementDisplayURL));
447  builder->Add("removeUserWarningButtonTitle",
448               IDS_LOGIN_POD_USER_REMOVE_WARNING_BUTTON);
449
450  // Strings used by confirm password dialog.
451  builder->Add("confirmPasswordTitle", IDS_LOGIN_CONFIRM_PASSWORD_TITLE);
452  builder->Add("confirmPasswordHint", IDS_LOGIN_CONFIRM_PASSWORD_HINT);
453  builder->Add("confirmPasswordConfirmButton",
454               IDS_LOGIN_CONFIRM_PASSWORD_CONFIRM_BUTTON);
455
456  if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
457    builder->Add("demoLoginMessage", IDS_KIOSK_MODE_LOGIN_MESSAGE);
458}
459
460void SigninScreenHandler::Show(const LoginScreenContext& context) {
461  CHECK(delegate_);
462
463  // Just initialize internal fields from context and call ShowImpl().
464  oobe_ui_ = context.oobe_ui();
465  if (!context.email().empty())
466    email_ = context.email();
467  else
468    email_.clear();
469  ShowImpl();
470}
471
472void SigninScreenHandler::ShowRetailModeLoginSpinner() {
473  CallJS("showLoginSpinner");
474}
475
476void SigninScreenHandler::SetDelegate(SigninScreenHandlerDelegate* delegate) {
477  delegate_ = delegate;
478  if (delegate_)
479    delegate_->SetWebUIHandler(this);
480}
481
482void SigninScreenHandler::SetNativeWindowDelegate(
483    NativeWindowDelegate* native_window_delegate) {
484  native_window_delegate_ = native_window_delegate;
485}
486
487void SigninScreenHandler::OnNetworkReady() {
488  LOG(WARNING) << "OnNetworkReady() call.";
489  MaybePreloadAuthExtension();
490}
491
492void SigninScreenHandler::UpdateState(ErrorScreenActor::ErrorReason reason) {
493  UpdateStateInternal(reason, false);
494}
495
496// SigninScreenHandler, private: -----------------------------------------------
497
498void SigninScreenHandler::ShowImpl() {
499  if (!page_is_ready()) {
500    show_on_init_ = true;
501    return;
502  }
503
504  if (oobe_ui_) {
505    // Shows new user sign-in for OOBE.
506    OnShowAddUser(email_);
507  } else {
508    // Populates account picker. Animation is turned off for now until we
509    // figure out how to make it fast enough.
510    SendUserList(false);
511
512    // Reset Caps Lock state when login screen is shown.
513    input_method::InputMethodManager::Get()->GetXKeyboard()->
514        SetCapsLockEnabled(false);
515
516    DictionaryValue params;
517    params.SetBoolean("disableAddUser", AllWhitelistedUsersPresent());
518    UpdateUIState(UI_STATE_ACCOUNT_PICKER, &params);
519  }
520}
521
522void SigninScreenHandler::UpdateUIState(UIState ui_state,
523                                        DictionaryValue* params) {
524  switch (ui_state) {
525    case UI_STATE_GAIA_SIGNIN:
526      ui_state_ = UI_STATE_GAIA_SIGNIN;
527      ShowScreen(OobeUI::kScreenGaiaSignin, params);
528      break;
529    case UI_STATE_ACCOUNT_PICKER:
530      ui_state_ = UI_STATE_ACCOUNT_PICKER;
531      ShowScreen(OobeUI::kScreenAccountPicker, params);
532      break;
533    default:
534      NOTREACHED();
535      break;
536  }
537}
538
539// TODO (ygorshenin@): split this method into small parts.
540// TODO (ygorshenin@): move this logic to GaiaScreenHandler.
541void SigninScreenHandler::UpdateStateInternal(
542    ErrorScreenActor::ErrorReason reason,
543    bool force_update) {
544  // Do nothing once user has signed in or sign in is in progress.
545  // TODO(ygorshenin): We will end up here when processing network state
546  // notification but no ShowSigninScreen() was called so delegate_ will be
547  // NULL. Network state processing logic does not belong here.
548  if (delegate_ &&
549      (delegate_->IsUserSigninCompleted() || delegate_->IsSigninInProgress())) {
550    return;
551  }
552
553  NetworkStateInformer::State state = network_state_informer_->state();
554  const std::string network_path = network_state_informer_->network_path();
555  const std::string network_name = GetNetworkName(network_path);
556
557  // Skip "update" notification about OFFLINE state from
558  // NetworkStateInformer if previous notification already was
559  // delayed.
560  if ((state == NetworkStateInformer::OFFLINE || has_pending_auth_ui_) &&
561      !force_update && !update_state_closure_.IsCancelled()) {
562    return;
563  }
564
565  // TODO (ygorshenin@): switch log level to INFO once signin screen
566  // will be tested well.
567  LOG(WARNING) << "SigninScreenHandler::UpdateStateInternal(): "
568               << "state=" << NetworkStateInformer::StatusString(state) << ", "
569               << "network_name=" << network_name << ", "
570               << "reason=" << ErrorScreenActor::ErrorReasonString(reason)
571               << ", force_update=" << force_update;
572  update_state_closure_.Cancel();
573
574  if ((state == NetworkStateInformer::OFFLINE && !force_update) ||
575      has_pending_auth_ui_) {
576    update_state_closure_.Reset(
577        base::Bind(&SigninScreenHandler::UpdateStateInternal,
578                   weak_factory_.GetWeakPtr(),
579                   reason,
580                   true));
581    base::MessageLoop::current()->PostDelayedTask(
582        FROM_HERE,
583        update_state_closure_.callback(),
584        base::TimeDelta::FromSeconds(kOfflineTimeoutSec));
585    return;
586  }
587
588  // Don't show or hide error screen if we're in connecting state.
589  if (state == NetworkStateInformer::CONNECTING && !force_update) {
590    if (connecting_closure_.IsCancelled()) {
591      // First notification about CONNECTING state.
592      connecting_closure_.Reset(
593          base::Bind(&SigninScreenHandler::UpdateStateInternal,
594                     weak_factory_.GetWeakPtr(),
595                     reason,
596                     true));
597      base::MessageLoop::current()->PostDelayedTask(
598          FROM_HERE,
599          connecting_closure_.callback(),
600          base::TimeDelta::FromSeconds(kConnectingTimeoutSec));
601    }
602    return;
603  }
604  connecting_closure_.Cancel();
605
606  const bool is_online = IsOnline(state, reason);
607  const bool is_under_captive_portal = IsUnderCaptivePortal(state, reason);
608  const bool is_gaia_loading_timeout =
609      (reason == ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT);
610  const bool is_gaia_error =
611      FrameError() != net::OK && FrameError() != net::ERR_NETWORK_CHANGED;
612  const bool is_gaia_signin = IsGaiaVisible() || IsGaiaHiddenByError();
613  const bool error_screen_should_overlay =
614      !offline_login_active_ && IsGaiaVisible();
615  const bool from_not_online_to_online_transition =
616      is_online && last_network_state_ != NetworkStateInformer::ONLINE;
617  last_network_state_ = state;
618
619  if (is_online || !is_under_captive_portal)
620    error_screen_actor_->HideCaptivePortal();
621
622  // Hide offline message (if needed) and return if current screen is
623  // not a Gaia frame.
624  if (!is_gaia_signin) {
625    if (!IsSigninScreenHiddenByError())
626      HideOfflineMessage(state, reason);
627    return;
628  }
629
630  // Reload frame if network state is changed from {!ONLINE} -> ONLINE state.
631  if (reason == ErrorScreenActor::ERROR_REASON_NETWORK_STATE_CHANGED &&
632      from_not_online_to_online_transition) {
633    // Schedules a immediate retry.
634    LOG(WARNING) << "Retry page load since network has been changed.";
635    ReloadGaiaScreen();
636  }
637
638  if (reason == ErrorScreenActor::ERROR_REASON_PROXY_CONFIG_CHANGED &&
639      error_screen_should_overlay) {
640    // Schedules a immediate retry.
641    LOG(WARNING) << "Retry page load since proxy settings has been changed.";
642    ReloadGaiaScreen();
643  }
644
645  if (reason == ErrorScreenActor::ERROR_REASON_FRAME_ERROR &&
646      !IsProxyError(state, reason, FrameError())) {
647    LOG(WARNING) << "Retry page load due to reason: "
648                 << ErrorScreenActor::ErrorReasonString(reason);
649    ReloadGaiaScreen();
650  }
651
652  if ((!is_online || is_gaia_loading_timeout || is_gaia_error) &&
653      !offline_login_active_) {
654    SetupAndShowOfflineMessage(state, reason);
655  } else {
656    HideOfflineMessage(state, reason);
657  }
658}
659
660void SigninScreenHandler::SetupAndShowOfflineMessage(
661    NetworkStateInformer:: State state,
662    ErrorScreenActor::ErrorReason reason) {
663  const std::string network_path = network_state_informer_->network_path();
664  const bool is_under_captive_portal = IsUnderCaptivePortal(state, reason);
665  const bool is_proxy_error = IsProxyError(state, reason, FrameError());
666  const bool is_gaia_loading_timeout =
667      (reason == ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT);
668
669  // Record portal detection stats only if we're going to show or
670  // change state of the error screen.
671  RecordNetworkPortalDetectorStats(network_path);
672
673  if (is_proxy_error) {
674    error_screen_actor_->SetErrorState(ErrorScreen::ERROR_STATE_PROXY,
675                                       std::string());
676  } else if (is_under_captive_portal) {
677    // Do not bother a user with obsessive captive portal showing. This
678    // check makes captive portal being shown only once: either when error
679    // screen is shown for the first time or when switching from another
680    // error screen (offline, proxy).
681    if (IsGaiaVisible() ||
682        (error_screen_actor_->error_state() !=
683         ErrorScreen::ERROR_STATE_PORTAL)) {
684      error_screen_actor_->FixCaptivePortal();
685    }
686    const std::string network_name = GetNetworkName(network_path);
687    error_screen_actor_->SetErrorState(ErrorScreen::ERROR_STATE_PORTAL,
688                                       network_name);
689  } else if (is_gaia_loading_timeout) {
690    error_screen_actor_->SetErrorState(
691        ErrorScreen::ERROR_STATE_AUTH_EXT_TIMEOUT, std::string());
692  } else {
693    error_screen_actor_->SetErrorState(ErrorScreen::ERROR_STATE_OFFLINE,
694                                       std::string());
695  }
696
697  const bool guest_signin_allowed = IsGuestSigninAllowed() &&
698      IsSigninScreenError(error_screen_actor_->error_state());
699  error_screen_actor_->AllowGuestSignin(guest_signin_allowed);
700
701  const bool offline_login_allowed = IsOfflineLoginAllowed() &&
702      IsSigninScreenError(error_screen_actor_->error_state()) &&
703      error_screen_actor_->error_state() !=
704      ErrorScreen::ERROR_STATE_AUTH_EXT_TIMEOUT;
705  error_screen_actor_->AllowOfflineLogin(offline_login_allowed);
706
707  if (GetCurrentScreen() != OobeUI::SCREEN_ERROR_MESSAGE) {
708    DictionaryValue params;
709    const std::string network_type = network_state_informer_->network_type();
710    params.SetString("lastNetworkType", network_type);
711    error_screen_actor_->SetUIState(ErrorScreen::UI_STATE_SIGNIN);
712    error_screen_actor_->Show(OobeUI::SCREEN_GAIA_SIGNIN, &params);
713  }
714}
715
716void SigninScreenHandler::HideOfflineMessage(
717    NetworkStateInformer::State state,
718    ErrorScreenActor::ErrorReason reason) {
719  if (!IsSigninScreenHiddenByError())
720    return;
721
722  error_screen_actor_->Hide();
723
724  // Forces a reload for Gaia screen on hiding error message.
725  if (IsGaiaVisible() || IsGaiaHiddenByError())
726    ReloadGaiaScreen();
727}
728
729void SigninScreenHandler::ReloadGaiaScreen() {
730  gaia_screen_handler_->ReloadGaia();
731}
732
733void SigninScreenHandler::Initialize() {
734  // If delegate_ is NULL here (e.g. WebUIScreenLocker has been destroyed),
735  // don't do anything, just return.
736  if (!delegate_)
737    return;
738
739  // Register for Caps Lock state change notifications;
740  SystemKeyEventListener* key_event_listener =
741      SystemKeyEventListener::GetInstance();
742  if (key_event_listener)
743    key_event_listener->AddCapsLockObserver(this);
744
745  if (show_on_init_) {
746    show_on_init_ = false;
747    ShowImpl();
748  }
749}
750
751gfx::NativeWindow SigninScreenHandler::GetNativeWindow() {
752  if (native_window_delegate_)
753    return native_window_delegate_->GetNativeWindow();
754  return NULL;
755}
756
757void SigninScreenHandler::RegisterMessages() {
758  AddCallback("authenticateUser", &SigninScreenHandler::HandleAuthenticateUser);
759  AddCallback("completeLogin", &SigninScreenHandler::HandleCompleteLogin);
760  AddCallback("completeAuthentication",
761              &SigninScreenHandler::HandleCompleteAuthentication);
762  AddCallback("getUsers", &SigninScreenHandler::HandleGetUsers);
763  AddCallback("launchDemoUser", &SigninScreenHandler::HandleLaunchDemoUser);
764  AddCallback("launchIncognito", &SigninScreenHandler::HandleLaunchIncognito);
765  AddCallback("showLocallyManagedUserCreationScreen",
766              &SigninScreenHandler::HandleShowLocallyManagedUserCreationScreen);
767  AddCallback("launchPublicAccount",
768              &SigninScreenHandler::HandleLaunchPublicAccount);
769  AddRawCallback("offlineLogin", &SigninScreenHandler::HandleOfflineLogin);
770  AddCallback("rebootSystem", &SigninScreenHandler::HandleRebootSystem);
771  AddRawCallback("showAddUser", &SigninScreenHandler::HandleShowAddUser);
772  AddCallback("shutdownSystem", &SigninScreenHandler::HandleShutdownSystem);
773  AddCallback("loadWallpaper", &SigninScreenHandler::HandleLoadWallpaper);
774  AddCallback("removeUser", &SigninScreenHandler::HandleRemoveUser);
775  AddCallback("toggleEnrollmentScreen",
776              &SigninScreenHandler::HandleToggleEnrollmentScreen);
777  AddCallback("toggleKioskEnableScreen",
778              &SigninScreenHandler::HandleToggleKioskEnableScreen);
779  AddCallback("toggleResetScreen",
780              &SigninScreenHandler::HandleToggleResetScreen);
781  AddCallback("launchHelpApp", &SigninScreenHandler::HandleLaunchHelpApp);
782  AddCallback("createAccount", &SigninScreenHandler::HandleCreateAccount);
783  AddCallback("accountPickerReady",
784              &SigninScreenHandler::HandleAccountPickerReady);
785  AddCallback("wallpaperReady", &SigninScreenHandler::HandleWallpaperReady);
786  AddCallback("loginWebuiReady", &SigninScreenHandler::HandleLoginWebuiReady);
787  AddCallback("signOutUser", &SigninScreenHandler::HandleSignOutUser);
788  AddCallback("networkErrorShown",
789              &SigninScreenHandler::HandleNetworkErrorShown);
790  AddCallback("openProxySettings",
791              &SigninScreenHandler::HandleOpenProxySettings);
792  AddCallback("loginVisible", &SigninScreenHandler::HandleLoginVisible);
793  AddCallback("cancelPasswordChangedFlow",
794              &SigninScreenHandler::HandleCancelPasswordChangedFlow);
795  AddCallback("cancelUserAdding",
796              &SigninScreenHandler::HandleCancelUserAdding);
797  AddCallback("migrateUserData", &SigninScreenHandler::HandleMigrateUserData);
798  AddCallback("resyncUserData", &SigninScreenHandler::HandleResyncUserData);
799  AddCallback("loginUIStateChanged",
800              &SigninScreenHandler::HandleLoginUIStateChanged);
801  AddCallback("unlockOnLoginSuccess",
802              &SigninScreenHandler::HandleUnlockOnLoginSuccess);
803  AddCallback("showLoadingTimeoutError",
804              &SigninScreenHandler::HandleShowLoadingTimeoutError);
805  AddCallback("updateOfflineLogin",
806              &SigninScreenHandler::HandleUpdateOfflineLogin);
807  AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod);
808  AddCallback("customButtonClicked",
809              &SigninScreenHandler::HandleCustomButtonClicked);
810
811  // This message is sent by the kiosk app menu, but is handled here
812  // so we can tell the delegate to launch the app.
813  AddCallback("launchKioskApp", &SigninScreenHandler::HandleLaunchKioskApp);
814}
815
816void SigninScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
817  registry->RegisterDictionaryPref(prefs::kUsersLRUInputMethod);
818}
819
820void SigninScreenHandler::HandleGetUsers() {
821  SendUserList(false);
822}
823
824void SigninScreenHandler::ClearAndEnablePassword() {
825  core_oobe_actor_->ResetSignInUI(false);
826}
827
828void SigninScreenHandler::ClearUserPodPassword() {
829  core_oobe_actor_->ClearUserPodPassword();
830}
831
832void SigninScreenHandler::RefocusCurrentPod() {
833  core_oobe_actor_->RefocusCurrentPod();
834}
835
836void SigninScreenHandler::OnLoginSuccess(const std::string& username) {
837  core_oobe_actor_->OnLoginSuccess(username);
838}
839
840void SigninScreenHandler::OnUserRemoved(const std::string& username) {
841  CallJS("login.AccountPickerScreen.removeUser", username);
842}
843
844void SigninScreenHandler::OnUserImageChanged(const User& user) {
845  if (page_is_ready())
846    CallJS("login.AccountPickerScreen.updateUserImage", user.email());
847}
848
849void SigninScreenHandler::OnPreferencesChanged() {
850  // Make sure that one of the login UI is fully functional now, otherwise
851  // preferences update would be picked up next time it will be shown.
852  if (!webui_visible_) {
853    LOG(WARNING) << "Login UI is not active - postponed prefs change.";
854    preferences_changed_delayed_ = true;
855    return;
856  }
857
858  if (delegate_ && !delegate_->IsShowUsers()) {
859    HandleShowAddUser(NULL);
860  } else {
861    SendUserList(false);
862    UpdateUIState(UI_STATE_ACCOUNT_PICKER, NULL);
863  }
864  preferences_changed_delayed_ = false;
865}
866
867void SigninScreenHandler::ResetSigninScreenHandlerDelegate() {
868  SetDelegate(NULL);
869}
870
871void SigninScreenHandler::ShowBannerMessage(const std::string& message) {
872  CallJS("login.AccountPickerScreen.showBannerMessage", message);
873}
874
875void SigninScreenHandler::ShowUserPodButton(
876    const std::string& username,
877    const std::string& iconURL,
878    const base::Closure& click_callback) {
879  user_pod_button_callback_map_[username] = click_callback;
880  CallJS("login.AccountPickerScreen.showUserPodButton", username, iconURL);
881}
882
883void SigninScreenHandler::ShowError(int login_attempts,
884                                    const std::string& error_text,
885                                    const std::string& help_link_text,
886                                    HelpAppLauncher::HelpTopic help_topic_id) {
887  core_oobe_actor_->ShowSignInError(login_attempts, error_text, help_link_text,
888                                    help_topic_id);
889}
890
891void SigninScreenHandler::ShowErrorScreen(LoginDisplay::SigninError error_id) {
892  switch (error_id) {
893    case LoginDisplay::TPM_ERROR:
894      core_oobe_actor_->ShowTpmError();
895      break;
896    default:
897      NOTREACHED() << "Unknown sign in error";
898      break;
899  }
900}
901
902void SigninScreenHandler::ShowSigninUI(const std::string& email) {
903  core_oobe_actor_->ShowSignInUI(email);
904}
905
906void SigninScreenHandler::ShowGaiaPasswordChanged(const std::string& username) {
907  email_ = username;
908  password_changed_for_.insert(email_);
909  core_oobe_actor_->ShowSignInUI(email_);
910  CallJS("login.AccountPickerScreen.updateUserGaiaNeeded", email_);
911}
912
913void SigninScreenHandler::ShowPasswordChangedDialog(bool show_password_error) {
914  core_oobe_actor_->ShowPasswordChangedScreen(show_password_error);
915}
916
917void SigninScreenHandler::ShowSigninScreenForCreds(
918    const std::string& username,
919    const std::string& password) {
920  VLOG(2) << "ShowSigninScreenForCreds  for user " << username
921          << ", frame_state=" << FrameState();
922
923  test_user_ = username;
924  test_pass_ = password;
925  test_expects_complete_login_ = true;
926
927  // Submit login form for test if gaia is ready. If gaia is loading, login
928  // will be attempted in HandleLoginWebuiReady after gaia is ready. Otherwise,
929  // reload gaia then follow the loading case.
930  if (FrameState() == GaiaScreenHandler::FRAME_STATE_LOADED)
931    SubmitLoginFormForTest();
932  else if (FrameState() != GaiaScreenHandler::FRAME_STATE_LOADING)
933    HandleShowAddUser(NULL);
934}
935
936void SigninScreenHandler::OnCookiesCleared(base::Closure on_clear_callback) {
937  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
938  cookies_cleared_ = true;
939  on_clear_callback.Run();
940}
941
942void SigninScreenHandler::OnCapsLockChange(bool enabled) {
943  if (page_is_ready())
944    CallJS("login.AccountPickerScreen.setCapsLockState", enabled);
945}
946
947void SigninScreenHandler::Observe(int type,
948                                  const content::NotificationSource& source,
949                                  const content::NotificationDetails& details) {
950  switch (type) {
951    case chrome::NOTIFICATION_AUTH_NEEDED: {
952      has_pending_auth_ui_ = true;
953      break;
954    }
955    case chrome::NOTIFICATION_AUTH_SUPPLIED:
956      has_pending_auth_ui_ = false;
957      if (IsSigninScreenHiddenByError()) {
958        // Hide error screen and reload auth extension.
959        HideOfflineMessage(network_state_informer_->state(),
960                           ErrorScreenActor::ERROR_REASON_PROXY_AUTH_SUPPLIED);
961      } else if (ui_state_ == UI_STATE_GAIA_SIGNIN) {
962        // Reload auth extension as proxy credentials are supplied.
963        ReloadGaiaScreen();
964      }
965      break;
966    case chrome::NOTIFICATION_AUTH_CANCELLED: {
967      // Don't reload auth extension if proxy auth dialog was cancelled.
968      has_pending_auth_ui_ = false;
969      break;
970    }
971    default:
972      NOTREACHED() << "Unexpected notification " << type;
973  }
974}
975
976void SigninScreenHandler::OnDnsCleared() {
977  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
978  dns_clear_task_running_ = false;
979  dns_cleared_ = true;
980  ShowSigninScreenIfReady();
981}
982
983// Update keyboard layout to least recently used by the user.
984void SigninScreenHandler::SetUserInputMethod(const std::string& username) {
985  UserManager* user_manager = UserManager::Get();
986  if (user_manager->IsUserLoggedIn()) {
987    // We are on sign-in screen inside user session (adding new user to
988    // the session or on lock screen), don't switch input methods in this case.
989    // TODO(dpolukhin): adding user and sign-in should be consistent
990    // crbug.com/292774
991    return;
992  }
993
994  chromeos::input_method::InputMethodManager* const manager =
995      chromeos::input_method::InputMethodManager::Get();
996
997  const bool succeed = SetUserInputMethodImpl(username, manager);
998
999  // This is also a case when LRU layout is set only for a few local users,
1000  // thus others need to be switched to default locale.
1001  // Otherwise they will end up using another user's locale to log in.
1002  if (!succeed) {
1003    DVLOG(0) << "SetUserInputMethod('" << username
1004               << "'): failed to set user layout. Switching to default.";
1005
1006    manager->SetInputMethodDefault();
1007  }
1008}
1009
1010void SigninScreenHandler::ShowSigninScreenIfReady() {
1011  LOG(WARNING) << "ShowSigninScreenIfReady() call.";
1012
1013  if (!dns_cleared_ || !cookies_cleared_ || !delegate_)
1014    return;
1015
1016  std::string active_network_path = network_state_informer_->network_path();
1017  if (gaia_silent_load_ &&
1018      (network_state_informer_->state() != NetworkStateInformer::ONLINE ||
1019       gaia_silent_load_network_ != active_network_path)) {
1020    // Network has changed. Force Gaia reload.
1021    gaia_silent_load_ = false;
1022    // Gaia page will be realoded, so focus isn't stolen anymore.
1023    focus_stolen_ = false;
1024  }
1025
1026  // Note that LoadAuthExtension clears |email_|.
1027  if (email_.empty())
1028    delegate_->LoadSigninWallpaper();
1029  else
1030    delegate_->LoadWallpaper(email_);
1031
1032  // Set Least Recently Used input method for the user.
1033  if (!email_.empty())
1034    SetUserInputMethod(email_);
1035
1036  LoadAuthExtension(!gaia_silent_load_, false, false);
1037  UpdateUIState(UI_STATE_GAIA_SIGNIN, NULL);
1038
1039  if (gaia_silent_load_) {
1040    // The variable is assigned to false because silently loaded Gaia page was
1041    // used.
1042    gaia_silent_load_ = false;
1043    if (focus_stolen_)
1044      HandleLoginWebuiReady();
1045  }
1046
1047  UpdateState(ErrorScreenActor::ERROR_REASON_UPDATE);
1048}
1049
1050void SigninScreenHandler::LoadAuthExtension(
1051    bool force, bool silent_load, bool offline) {
1052  GaiaContext context;
1053  context.force_reload = force;
1054  context.is_local = offline;
1055  context.password_changed =
1056      !email_.empty() && password_changed_for_.count(email_);
1057  if (delegate_)
1058    context.show_users = delegate_->IsShowUsers();
1059  context.use_offline = offline;
1060  if (delegate_)
1061    context.has_users = delegate_->GetUsers().size() != 0;
1062  context.email = email_;
1063
1064  email_.clear();
1065
1066  DCHECK(gaia_screen_handler_);
1067  gaia_screen_handler_->LoadGaia(context);
1068}
1069
1070void SigninScreenHandler::UserSettingsChanged() {
1071  DCHECK(gaia_screen_handler_);
1072  GaiaContext context;
1073  if (delegate_)
1074    context.has_users = delegate_->GetUsers().size() != 0;
1075  gaia_screen_handler_->UpdateGaia(context);
1076  UpdateAddButtonStatus();
1077}
1078
1079void SigninScreenHandler::UpdateAddButtonStatus() {
1080  CallJS("cr.ui.login.DisplayManager.updateAddUserButtonStatus",
1081         AllWhitelistedUsersPresent());
1082}
1083
1084void SigninScreenHandler::HandleCompleteLogin(const std::string& typed_email,
1085                                              const std::string& password) {
1086  if (!delegate_)
1087    return;
1088  const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
1089  delegate_->SetDisplayEmail(sanitized_email);
1090  delegate_->CompleteLogin(UserContext(sanitized_email,
1091                                       password,
1092                                       std::string()));  // auth_code
1093
1094  if (test_expects_complete_login_) {
1095    VLOG(2) << "Complete test login for " << typed_email
1096            << ", requested=" << test_user_;
1097
1098    test_expects_complete_login_ = false;
1099    test_user_.clear();
1100    test_pass_.clear();
1101  }
1102}
1103
1104void SigninScreenHandler::HandleCompleteAuthentication(
1105    const std::string& email,
1106    const std::string& password,
1107    const std::string& auth_code) {
1108  if (!delegate_)
1109    return;
1110  const std::string sanitized_email = gaia::SanitizeEmail(email);
1111  delegate_->SetDisplayEmail(sanitized_email);
1112  delegate_->CompleteLogin(UserContext(sanitized_email, password, auth_code));
1113}
1114
1115void SigninScreenHandler::HandleAuthenticateUser(const std::string& username,
1116                                                 const std::string& password) {
1117  if (!delegate_)
1118    return;
1119  delegate_->Login(UserContext(gaia::SanitizeEmail(username),
1120                               password,
1121                               std::string()));  // auth_code
1122}
1123
1124void SigninScreenHandler::HandleLaunchDemoUser() {
1125  if (delegate_)
1126    delegate_->LoginAsRetailModeUser();
1127}
1128
1129void SigninScreenHandler::HandleLaunchIncognito() {
1130  if (delegate_)
1131    delegate_->LoginAsGuest();
1132}
1133
1134void SigninScreenHandler::HandleShowLocallyManagedUserCreationScreen() {
1135  if (!UserManager::Get()->AreLocallyManagedUsersAllowed()) {
1136    LOG(ERROR) << "Managed users not allowed.";
1137    return;
1138  }
1139  scoped_ptr<DictionaryValue> params(new DictionaryValue());
1140  LoginDisplayHostImpl::default_host()->
1141      StartWizard(WizardController::kLocallyManagedUserCreationScreenName,
1142      params.Pass());
1143}
1144
1145void SigninScreenHandler::HandleLaunchPublicAccount(
1146    const std::string& username) {
1147  if (delegate_)
1148    delegate_->LoginAsPublicAccount(username);
1149}
1150
1151void SigninScreenHandler::HandleOfflineLogin(const base::ListValue* args) {
1152  if (!delegate_ || delegate_->IsShowUsers()) {
1153    NOTREACHED();
1154    return;
1155  }
1156  if (!args->GetString(0, &email_))
1157    email_.clear();
1158  // Load auth extension. Parameters are: force reload, do not load extension in
1159  // background, use offline version.
1160  LoadAuthExtension(true, false, true);
1161  UpdateUIState(UI_STATE_GAIA_SIGNIN, NULL);
1162}
1163
1164void SigninScreenHandler::HandleShutdownSystem() {
1165  ash::Shell::GetInstance()->lock_state_controller()->RequestShutdown();
1166}
1167
1168void SigninScreenHandler::HandleLoadWallpaper(const std::string& email) {
1169  if (delegate_)
1170    delegate_->LoadWallpaper(email);
1171}
1172
1173void SigninScreenHandler::OnWallpaperAnimationFinished(
1174    const std::string& email) {
1175  CallJS("login.AccountPickerScreen.onWallpaperLoaded", email);
1176}
1177
1178void SigninScreenHandler::HandleRebootSystem() {
1179  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
1180}
1181
1182void SigninScreenHandler::HandleRemoveUser(const std::string& email) {
1183  if (!delegate_)
1184    return;
1185  delegate_->RemoveUser(email);
1186  UpdateAddButtonStatus();
1187}
1188
1189void SigninScreenHandler::HandleShowAddUser(const base::ListValue* args) {
1190  TRACE_EVENT_ASYNC_STEP_INTO0("ui",
1191                               "ShowLoginWebUI",
1192                               LoginDisplayHostImpl::kShowLoginWebUIid,
1193                               "ShowAddUser");
1194  std::string email;
1195  // |args| can be null if it's OOBE.
1196  if (args)
1197    args->GetString(0, &email);
1198  OnShowAddUser(email);
1199}
1200
1201void SigninScreenHandler::HandleToggleEnrollmentScreen() {
1202  if (delegate_)
1203    delegate_->ShowEnterpriseEnrollmentScreen();
1204}
1205
1206void SigninScreenHandler::HandleToggleKioskEnableScreen() {
1207  if (delegate_ &&
1208      !wait_for_auto_enrollment_check_ &&
1209      !g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
1210    wait_for_auto_enrollment_check_ = true;
1211
1212    LoginDisplayHostImpl::default_host()->GetAutoEnrollmentCheckResult(
1213        base::Bind(&SigninScreenHandler::ContinueKioskEnableFlow,
1214                   weak_factory_.GetWeakPtr()));
1215  }
1216}
1217
1218void SigninScreenHandler::HandleToggleResetScreen() {
1219  if (delegate_ &&
1220      !g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
1221    delegate_->ShowResetScreen();
1222  }
1223}
1224
1225void SigninScreenHandler::HandleToggleKioskAutolaunchScreen() {
1226  if (delegate_ &&
1227      !g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
1228    delegate_->ShowKioskAutolaunchScreen();
1229  }
1230}
1231
1232void SigninScreenHandler::HandleLaunchHelpApp(double help_topic_id) {
1233  if (!delegate_)
1234    return;
1235  if (!help_app_.get())
1236    help_app_ = new HelpAppLauncher(GetNativeWindow());
1237  help_app_->ShowHelpTopic(
1238      static_cast<HelpAppLauncher::HelpTopic>(help_topic_id));
1239}
1240
1241void SigninScreenHandler::FillUserDictionary(User* user,
1242                                             bool is_owner,
1243                                             DictionaryValue* user_dict) {
1244  const std::string& email = user->email();
1245  bool is_public_account =
1246      user->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT;
1247  bool is_locally_managed_user =
1248      user->GetType() == User::USER_TYPE_LOCALLY_MANAGED;
1249  User::OAuthTokenStatus token_status = user->oauth_token_status();
1250
1251  // If supervised user has unknown token status consider that as valid token.
1252  // It will be invalidated inside session in case it has been revoked.
1253  if (is_locally_managed_user &&
1254      token_status == User::OAUTH_TOKEN_STATUS_UNKNOWN) {
1255    token_status = User::OAUTH2_TOKEN_STATUS_VALID;
1256  }
1257
1258  user_dict->SetString(kKeyUsername, email);
1259  user_dict->SetString(kKeyEmailAddress, user->display_email());
1260  user_dict->SetString(kKeyDisplayName, user->GetDisplayName());
1261  user_dict->SetBoolean(kKeyPublicAccount, is_public_account);
1262  user_dict->SetBoolean(kKeyLocallyManagedUser, is_locally_managed_user);
1263  user_dict->SetInteger(kKeyOauthTokenStatus, token_status);
1264  user_dict->SetBoolean(kKeySignedIn, user->is_logged_in());
1265  user_dict->SetBoolean(kKeyIsOwner, is_owner);
1266
1267  if (is_public_account) {
1268    policy::BrowserPolicyConnector* policy_connector =
1269        g_browser_process->browser_policy_connector();
1270
1271    if (policy_connector->IsEnterpriseManaged()) {
1272      user_dict->SetString(kKeyEnterpriseDomain,
1273                           policy_connector->GetEnterpriseDomain());
1274    }
1275  }
1276}
1277
1278void SigninScreenHandler::SendUserList(bool animated) {
1279  if (!delegate_)
1280    return;
1281  TRACE_EVENT_ASYNC_STEP_INTO0("ui",
1282                               "ShowLoginWebUI",
1283                               LoginDisplayHostImpl::kShowLoginWebUIid,
1284                               "SendUserList");
1285  BootTimesLoader::Get()->RecordCurrentStats("login-send-user-list");
1286
1287  size_t max_non_owner_users = kMaxUsers - 1;
1288  size_t non_owner_count = 0;
1289
1290  ListValue users_list;
1291  const UserList& users = delegate_->GetUsers();
1292
1293  // TODO(nkostylev): Show optional intro dialog about multi-profiles feature
1294  // based on user preferences. http://crbug.com/230862
1295
1296  // TODO(nkostylev): Move to a separate method in UserManager.
1297  // http://crbug.com/230852
1298  bool is_signin_to_add = LoginDisplayHostImpl::default_host() &&
1299      UserManager::Get()->IsUserLoggedIn();
1300
1301  bool single_user = users.size() == 1;
1302  for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
1303    const std::string& email = (*it)->email();
1304
1305    std::string owner;
1306    chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
1307    bool is_owner = (email == owner);
1308
1309    if (non_owner_count < max_non_owner_users || is_owner) {
1310      DictionaryValue* user_dict = new DictionaryValue();
1311      FillUserDictionary(*it, is_owner, user_dict);
1312      bool is_public_account =
1313          ((*it)->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT);
1314      bool signed_in = (*it)->is_logged_in();
1315      // Single user check here is necessary because owner info might not be
1316      // available when running into login screen on first boot.
1317      // See http://crosbug.com/12723
1318      user_dict->SetBoolean(kKeyCanRemove,
1319                            !single_user &&
1320                            !email.empty() &&
1321                            !is_owner &&
1322                            !is_public_account &&
1323                            !signed_in &&
1324                            !is_signin_to_add);
1325
1326      users_list.Append(user_dict);
1327      if (!is_owner)
1328        ++non_owner_count;
1329    }
1330  }
1331
1332  CallJS("login.AccountPickerScreen.loadUsers", users_list, animated,
1333         delegate_->IsShowGuest());
1334}
1335
1336void SigninScreenHandler::HandleAccountPickerReady() {
1337  VLOG(0) << "Login WebUI >> AccountPickerReady";
1338
1339  if (delegate_ && !ScreenLocker::default_screen_locker() &&
1340      !chromeos::IsMachineHWIDCorrect() &&
1341      !oobe_ui_) {
1342    delegate_->ShowWrongHWIDScreen();
1343    return;
1344  }
1345
1346  PrefService* prefs = g_browser_process->local_state();
1347  if (prefs->GetBoolean(prefs::kFactoryResetRequested)) {
1348    prefs->SetBoolean(prefs::kFactoryResetRequested, false);
1349    prefs->CommitPendingWrite();
1350    HandleToggleResetScreen();
1351    return;
1352  }
1353
1354  is_account_picker_showing_first_time_ = true;
1355  MaybePreloadAuthExtension();
1356
1357  if (ScreenLocker::default_screen_locker())
1358    ScreenLocker::default_screen_locker()->delegate()->OnLockWebUIReady();
1359
1360  if (delegate_)
1361    delegate_->OnSigninScreenReady();
1362}
1363
1364void SigninScreenHandler::HandleWallpaperReady() {
1365  if (ScreenLocker::default_screen_locker()) {
1366    ScreenLocker::default_screen_locker()->delegate()->
1367        OnLockBackgroundDisplayed();
1368  }
1369}
1370
1371void SigninScreenHandler::HandleLoginWebuiReady() {
1372  if (focus_stolen_) {
1373    // Set focus to the Gaia page.
1374    // TODO(altimofeev): temporary solution, until focus parameters are
1375    // implemented on the Gaia side.
1376    // Do this only once. Any subsequent call would relod GAIA frame.
1377    focus_stolen_ = false;
1378    const char code[] = "gWindowOnLoad();";
1379    RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
1380    rvh->ExecuteJavascriptInWebFrame(
1381        ASCIIToUTF16("//iframe[@id='signin-frame']\n//iframe"),
1382        ASCIIToUTF16(code));
1383  }
1384  if (!gaia_silent_load_) {
1385    content::NotificationService::current()->Notify(
1386        chrome::NOTIFICATION_LOGIN_WEBUI_LOADED,
1387        content::NotificationService::AllSources(),
1388        content::NotificationService::NoDetails());
1389  } else {
1390    focus_stolen_ = true;
1391    // Prevent focus stealing by the Gaia page.
1392    // TODO(altimofeev): temporary solution, until focus parameters are
1393    // implemented on the Gaia side.
1394    const char code[] = "var gWindowOnLoad = window.onload; "
1395                        "window.onload=function() {};";
1396    RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
1397    rvh->ExecuteJavascriptInWebFrame(
1398        ASCIIToUTF16("//iframe[@id='signin-frame']\n//iframe"),
1399        ASCIIToUTF16(code));
1400    // As we could miss and window.onload could already be called, restore
1401    // focus to current pod (see crbug/175243).
1402    RefocusCurrentPod();
1403  }
1404  DCHECK(gaia_screen_handler_);
1405  gaia_screen_handler_->HandleFrameLoadingCompleted(0);
1406
1407  if (test_expects_complete_login_)
1408    SubmitLoginFormForTest();
1409}
1410
1411void SigninScreenHandler::HandleSignOutUser() {
1412  if (delegate_)
1413    delegate_->Signout();
1414}
1415
1416void SigninScreenHandler::HandleNetworkErrorShown() {
1417  content::NotificationService::current()->Notify(
1418      chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
1419      content::NotificationService::AllSources(),
1420      content::NotificationService::NoDetails());
1421}
1422
1423void SigninScreenHandler::HandleCreateAccount() {
1424  if (delegate_)
1425    delegate_->CreateAccount();
1426}
1427
1428void SigninScreenHandler::HandleOpenProxySettings() {
1429  LoginDisplayHostImpl::default_host()->OpenProxySettings();
1430}
1431
1432void SigninScreenHandler::HandleLoginVisible(const std::string& source) {
1433  LOG(WARNING) << "Login WebUI >> loginVisible, src: " << source << ", "
1434               << "webui_visible_: " << webui_visible_;
1435  if (!webui_visible_) {
1436    // There might be multiple messages from OOBE UI so send notifications after
1437    // the first one only.
1438    content::NotificationService::current()->Notify(
1439        chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
1440        content::NotificationService::AllSources(),
1441        content::NotificationService::NoDetails());
1442    TRACE_EVENT_ASYNC_END0(
1443        "ui", "ShowLoginWebUI", LoginDisplayHostImpl::kShowLoginWebUIid);
1444  }
1445  webui_visible_ = true;
1446  if (preferences_changed_delayed_)
1447    OnPreferencesChanged();
1448}
1449
1450void SigninScreenHandler::HandleCancelPasswordChangedFlow() {
1451  StartClearingCookies(base::Bind(
1452      &SigninScreenHandler::CancelPasswordChangedFlowInternal,
1453      weak_factory_.GetWeakPtr()));
1454}
1455
1456void SigninScreenHandler::HandleCancelUserAdding() {
1457  if (delegate_)
1458    delegate_->CancelUserAdding();
1459}
1460
1461void SigninScreenHandler::HandleMigrateUserData(
1462    const std::string& old_password) {
1463  if (delegate_)
1464    delegate_->MigrateUserData(old_password);
1465}
1466
1467void SigninScreenHandler::HandleResyncUserData() {
1468  if (delegate_)
1469    delegate_->ResyncUserData();
1470}
1471
1472void SigninScreenHandler::HandleLoginUIStateChanged(const std::string& source,
1473                                                    bool new_value) {
1474  VLOG(0) << "Login WebUI >> active: " << new_value << ", "
1475            << "source: " << source;
1476
1477  if (!KioskAppManager::Get()->GetAutoLaunchApp().empty() &&
1478      KioskAppManager::Get()->IsAutoLaunchRequested()) {
1479    VLOG(0) << "Showing auto-launch warning";
1480    // On slow devices, the wallpaper animation is not shown initially, so we
1481    // must explicitly load the wallpaper. This is also the case for the
1482    // account-picker and gaia-signin UI states.
1483    delegate_->LoadSigninWallpaper();
1484    HandleToggleKioskAutolaunchScreen();
1485    return;
1486  }
1487
1488  if (source == kSourceGaiaSignin) {
1489    ui_state_ = UI_STATE_GAIA_SIGNIN;
1490  } else if (source == kSourceAccountPicker) {
1491    ui_state_ = UI_STATE_ACCOUNT_PICKER;
1492  } else {
1493    NOTREACHED();
1494    return;
1495  }
1496}
1497
1498void SigninScreenHandler::HandleUnlockOnLoginSuccess() {
1499  DCHECK(UserManager::Get()->IsUserLoggedIn());
1500  if (ScreenLocker::default_screen_locker())
1501    ScreenLocker::default_screen_locker()->UnlockOnLoginSuccess();
1502}
1503
1504void SigninScreenHandler::HandleShowLoadingTimeoutError() {
1505  UpdateState(ErrorScreenActor::ERROR_REASON_LOADING_TIMEOUT);
1506}
1507
1508void SigninScreenHandler::HandleUpdateOfflineLogin(bool offline_login_active) {
1509  offline_login_active_ = offline_login_active;
1510}
1511
1512void SigninScreenHandler::HandleFocusPod(const std::string& user_id) {
1513  SetUserInputMethod(user_id);
1514}
1515
1516void SigninScreenHandler::HandleCustomButtonClicked(
1517    const std::string& username) {
1518  if (user_pod_button_callback_map_.find(username)
1519      == user_pod_button_callback_map_.end()) {
1520    LOG(WARNING) << "User pod custom button clicked but no callback found";
1521    return;
1522  }
1523  user_pod_button_callback_map_[username].Run();
1524}
1525
1526void SigninScreenHandler::HandleLaunchKioskApp(const std::string& app_id) {
1527  delegate_->LoginAsKioskApp(app_id);
1528}
1529
1530void SigninScreenHandler::StartClearingDnsCache() {
1531  if (dns_clear_task_running_ || !g_browser_process->io_thread())
1532    return;
1533
1534  dns_cleared_ = false;
1535  BrowserThread::PostTaskAndReply(
1536      BrowserThread::IO, FROM_HERE,
1537      base::Bind(&ClearDnsCache, g_browser_process->io_thread()),
1538      base::Bind(&SigninScreenHandler::OnDnsCleared,
1539                 weak_factory_.GetWeakPtr()));
1540  dns_clear_task_running_ = true;
1541}
1542
1543void SigninScreenHandler::StartClearingCookies(
1544    const base::Closure& on_clear_callback) {
1545  cookies_cleared_ = false;
1546  ProfileHelper* profile_helper =
1547      g_browser_process->platform_part()->profile_helper();
1548  LOG_ASSERT(
1549      Profile::FromWebUI(web_ui()) == profile_helper->GetSigninProfile());
1550  profile_helper->ClearSigninProfile(base::Bind(
1551      &SigninScreenHandler::OnCookiesCleared,
1552      weak_factory_.GetWeakPtr(), on_clear_callback));
1553}
1554
1555void SigninScreenHandler::MaybePreloadAuthExtension() {
1556  LOG(WARNING) << "MaybePreloadAuthExtension() call.";
1557
1558  // Fetching of the extension is not started before account picker page is
1559  // loaded because it can affect the loading speed. Also if cookies clearing
1560  // was initiated or |dns_clear_task_running_| then auth extension showing has
1561  // already been initiated and preloading is senseless.
1562  // Do not load the extension for the screen locker, see crosbug.com/25018.
1563  if (is_account_picker_showing_first_time_ &&
1564      !gaia_silent_load_ &&
1565      !ScreenLocker::default_screen_locker() &&
1566      !cookies_cleared_ &&
1567      !dns_clear_task_running_ &&
1568      network_state_informer_->state() == NetworkStateInformer::ONLINE) {
1569    gaia_silent_load_ = true;
1570    gaia_silent_load_network_ = network_state_informer_->network_path();
1571    LoadAuthExtension(true, true, false);
1572  }
1573}
1574
1575bool SigninScreenHandler::AllWhitelistedUsersPresent() {
1576  CrosSettings* cros_settings = CrosSettings::Get();
1577  bool allow_new_user = false;
1578  cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
1579  if (allow_new_user)
1580    return false;
1581  UserManager* user_manager = UserManager::Get();
1582  const UserList& users = user_manager->GetUsers();
1583  if (!delegate_ || users.size() > kMaxUsers) {
1584    return false;
1585  }
1586  const base::ListValue* whitelist = NULL;
1587  if (!cros_settings->GetList(kAccountsPrefUsers, &whitelist) || !whitelist)
1588    return false;
1589  for (size_t i = 0; i < whitelist->GetSize(); ++i) {
1590    std::string whitelisted_user;
1591    // NB: Wildcards in the whitelist are also detected as not present here.
1592    if (!whitelist->GetString(i, &whitelisted_user) ||
1593        !user_manager->IsKnownUser(whitelisted_user)) {
1594      return false;
1595    }
1596  }
1597  return true;
1598}
1599
1600void SigninScreenHandler::CancelPasswordChangedFlowInternal() {
1601  if (delegate_) {
1602    ShowImpl();
1603    delegate_->CancelPasswordChangedFlow();
1604  }
1605}
1606
1607OobeUI::Screen SigninScreenHandler::GetCurrentScreen() const {
1608  OobeUI::Screen screen = OobeUI::SCREEN_UNKNOWN;
1609  OobeUI* oobe_ui = static_cast<OobeUI*>(web_ui()->GetController());
1610  if (oobe_ui)
1611    screen = oobe_ui->current_screen();
1612  return screen;
1613}
1614
1615bool SigninScreenHandler::IsGaiaVisible() const {
1616  return IsSigninScreen(GetCurrentScreen()) &&
1617      ui_state_ == UI_STATE_GAIA_SIGNIN;
1618}
1619
1620bool SigninScreenHandler::IsGaiaHiddenByError() const {
1621  return IsSigninScreenHiddenByError() &&
1622      ui_state_ == UI_STATE_GAIA_SIGNIN;
1623}
1624
1625bool SigninScreenHandler::IsSigninScreenHiddenByError() const {
1626  return (GetCurrentScreen() == OobeUI::SCREEN_ERROR_MESSAGE) &&
1627      (IsSigninScreen(error_screen_actor_->parent_screen()));
1628}
1629
1630bool SigninScreenHandler::IsGuestSigninAllowed() const {
1631  CrosSettings* cros_settings = CrosSettings::Get();
1632  if (!cros_settings)
1633    return false;
1634  bool allow_guest;
1635  cros_settings->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
1636  return allow_guest;
1637}
1638
1639bool SigninScreenHandler::IsOfflineLoginAllowed() const {
1640  CrosSettings* cros_settings = CrosSettings::Get();
1641  if (!cros_settings)
1642    return false;
1643
1644  // Offline login is allowed only when user pods are hidden.
1645  bool show_pods;
1646  cros_settings->GetBoolean(kAccountsPrefShowUserNamesOnSignIn, &show_pods);
1647  return !show_pods;
1648}
1649
1650void SigninScreenHandler::SubmitLoginFormForTest() {
1651  VLOG(2) << "Submit login form for test, user=" << test_user_;
1652
1653  std::string code;
1654  code += "document.getElementById('Email').value = '" + test_user_ + "';";
1655  code += "document.getElementById('Passwd').value = '" + test_pass_ + "';";
1656  code += "document.getElementById('signIn').click();";
1657
1658  RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
1659  rvh->ExecuteJavascriptInWebFrame(
1660      ASCIIToUTF16("//iframe[@id='signin-frame']\n//iframe"),
1661      ASCIIToUTF16(code));
1662
1663  // Test properties are cleared in HandleCompleteLogin because the form
1664  // submission might fail and login will not be attempted after reloading
1665  // if they are cleared here.
1666}
1667
1668void SigninScreenHandler::ContinueKioskEnableFlow(bool should_auto_enroll) {
1669  wait_for_auto_enrollment_check_ = false;
1670
1671  // Do not proceed with kiosk enable when auto enroll will be enforced.
1672  // TODO(xiyuan): Add an error UI feedkback so user knows what happens.
1673  if (should_auto_enroll) {
1674    LOG(WARNING) << "Kiosk enable flow aborted because auto enrollment is "
1675                    "going to be enforced.";
1676
1677    if (!kiosk_enable_flow_aborted_callback_for_test_.is_null())
1678      kiosk_enable_flow_aborted_callback_for_test_.Run();
1679    return;
1680  }
1681
1682  if (delegate_)
1683    delegate_->ShowKioskEnableScreen();
1684}
1685
1686void SigninScreenHandler::OnShowAddUser(const std::string& email) {
1687  email_ = email;
1688  is_account_picker_showing_first_time_ = false;
1689
1690  if (gaia_silent_load_ && email_.empty()) {
1691    dns_cleared_ = true;
1692    cookies_cleared_ = true;
1693    ShowSigninScreenIfReady();
1694  } else {
1695    StartClearingDnsCache();
1696    StartClearingCookies(base::Bind(
1697        &SigninScreenHandler::ShowSigninScreenIfReady,
1698        weak_factory_.GetWeakPtr()));
1699  }
1700}
1701
1702GaiaScreenHandler::FrameState SigninScreenHandler::FrameState() const {
1703  DCHECK(gaia_screen_handler_);
1704  return gaia_screen_handler_->frame_state();
1705}
1706
1707net::Error SigninScreenHandler::FrameError() const {
1708  DCHECK(gaia_screen_handler_);
1709  return gaia_screen_handler_->frame_error();
1710}
1711
1712}  // namespace chromeos
1713