existing_user_controller.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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/existing_user_controller.h"
6
7#include <algorithm>
8#include <functional>
9#include <map>
10
11#include "app/l10n_util.h"
12#include "app/resource_bundle.h"
13#include "base/command_line.h"
14#include "base/message_loop.h"
15#include "base/stl_util-inl.h"
16#include "base/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/browser/chromeos/boot_times_loader.h"
19#include "chrome/browser/chromeos/cros/cros_library.h"
20#include "chrome/browser/chromeos/cros/cryptohome_library.h"
21#include "chrome/browser/chromeos/cros/login_library.h"
22#include "chrome/browser/chromeos/cros/network_library.h"
23#include "chrome/browser/chromeos/login/background_view.h"
24#include "chrome/browser/chromeos/login/help_app_launcher.h"
25#include "chrome/browser/chromeos/login/helper.h"
26#include "chrome/browser/chromeos/login/login_utils.h"
27#include "chrome/browser/chromeos/login/message_bubble.h"
28#include "chrome/browser/chromeos/login/wizard_controller.h"
29#include "chrome/browser/chromeos/status/status_area_view.h"
30#include "chrome/browser/chromeos/user_cros_settings_provider.h"
31#include "chrome/browser/chromeos/view_ids.h"
32#include "chrome/browser/chromeos/wm_ipc.h"
33#include "chrome/browser/profiles/profile_manager.h"
34#include "chrome/browser/views/window.h"
35#include "chrome/common/chrome_switches.h"
36#include "chrome/common/net/gaia/google_service_auth_error.h"
37#include "gfx/native_widget_types.h"
38#include "grit/chromium_strings.h"
39#include "grit/generated_resources.h"
40#include "grit/theme_resources.h"
41#include "views/screen.h"
42#include "views/widget/widget_gtk.h"
43#include "views/window/window.h"
44
45namespace chromeos {
46
47namespace {
48
49// Max number of users we'll show. The true max is the min of this and the
50// number of windows that fit on the screen.
51const size_t kMaxUsers = 6;
52
53// Minimum number of users we'll show (including Guest and New User pods).
54const size_t kMinUsers = 3;
55
56// Used to indicate no user has been selected.
57const size_t kNotSelected = -1;
58
59// Offset of cursor in first position from edit left side. It's used to position
60// info bubble arrow to cursor.
61const int kCursorOffset = 5;
62
63// Url for setting up sync authentication.
64const char kSettingsSyncLoginUrl[] = "chrome://settings/personal";
65
66// URL that will be opened on when user logs in first time on the device.
67const char kGetStartedURL[] =
68    "chrome-extension://nbaambmfhicobichobkkokacjbaoinda/index.html";
69
70// Path to extracted version of Get Started app.
71const char kGetStartedPath[] = "/usr/share/chromeos-assets/getstarted";
72
73// Used to handle the asynchronous response of deleting a cryptohome directory.
74class RemoveAttempt : public CryptohomeLibrary::Delegate {
75 public:
76  explicit RemoveAttempt(const std::string& user_email)
77      : user_email_(user_email) {
78    if (CrosLibrary::Get()->EnsureLoaded()) {
79      CrosLibrary::Get()->GetCryptohomeLibrary()->AsyncRemove(
80          user_email_, this);
81    }
82  }
83
84  void OnComplete(bool success, int return_code) {
85    // Log the error, but there's not much we can do.
86    if (!success) {
87      VLOG(1) << "Removal of cryptohome for " << user_email_
88              << " failed, return code: " << return_code;
89    }
90    delete this;
91  }
92
93 private:
94  std::string user_email_;
95};
96
97// Checks if display names are unique. If there are duplicates, enables
98// tooltips with full emails to let users distinguish their accounts.
99// Otherwise, disables the tooltips.
100void EnableTooltipsIfNeeded(const std::vector<UserController*>& controllers) {
101  std::map<std::string, int> visible_display_names;
102  for (size_t i = 0; i + 1 < controllers.size(); ++i) {
103    const std::string& display_name =
104        controllers[i]->user().GetDisplayName();
105    ++visible_display_names[display_name];
106  }
107  for (size_t i = 0; i < controllers.size(); ++i) {
108    const std::string& display_name =
109        controllers[i]->user().GetDisplayName();
110    bool show_tooltip = controllers[i]->is_new_user() ||
111                        controllers[i]->is_guest() ||
112                        visible_display_names[display_name] > 1;
113    controllers[i]->EnableNameTooltip(show_tooltip);
114  }
115}
116
117}  // namespace
118
119ExistingUserController*
120  ExistingUserController::delete_scheduled_instance_ = NULL;
121
122// TODO(xiyuan): Wait for the cached settings update before using them.
123ExistingUserController::ExistingUserController(
124    const std::vector<UserManager::User>& users,
125    const gfx::Rect& background_bounds)
126    : background_bounds_(background_bounds),
127      background_window_(NULL),
128      background_view_(NULL),
129      selected_view_index_(kNotSelected),
130      num_login_attempts_(0),
131      bubble_(NULL),
132      user_settings_(new UserCrosSettingsProvider),
133      method_factory_(this) {
134  if (delete_scheduled_instance_)
135    delete_scheduled_instance_->Delete();
136
137  // Calculate the max number of users from available screen size.
138  bool show_guest = UserCrosSettingsProvider::cached_allow_guest();
139  bool show_new_user = true;
140  if (UserCrosSettingsProvider::cached_show_users_on_signin()) {
141    size_t max_users = kMaxUsers;
142    int screen_width = background_bounds.width();
143    if (screen_width > 0) {
144      size_t users_per_screen = (screen_width - login::kUserImageSize)
145          / (UserController::kUnselectedSize + UserController::kPadding);
146      max_users = std::max(kMinUsers, std::min(kMaxUsers, users_per_screen));
147    }
148
149    size_t visible_users_count = std::min(users.size(), max_users -
150        static_cast<int>(show_guest) - static_cast<int>(show_new_user));
151    for (size_t i = 0; i < users.size(); ++i) {
152      // TODO(xiyuan): Clean user profile whose email is not in whitelist.
153      if (UserCrosSettingsProvider::cached_allow_new_user() ||
154          UserCrosSettingsProvider::IsEmailInCachedWhitelist(
155              users[i].email())) {
156        UserController* user_controller = new UserController(this, users[i]);
157        if (controllers_.size() < visible_users_count)
158          controllers_.push_back(user_controller);
159        else
160          invisible_controllers_.push_back(user_controller);
161      }
162    }
163  }
164
165  if (!controllers_.empty() && show_guest)
166    controllers_.push_back(new UserController(this, true));
167
168  if (show_new_user)
169    controllers_.push_back(new UserController(this, false));
170}
171
172void ExistingUserController::Init() {
173  if (!background_window_) {
174    std::string url_string =
175        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
176            switches::kScreenSaverUrl);
177
178    background_window_ = BackgroundView::CreateWindowContainingView(
179        background_bounds_,
180        GURL(url_string),
181        &background_view_);
182    background_view_->EnableShutdownButton(true);
183
184    if (!WizardController::IsDeviceRegistered()) {
185      background_view_->SetOobeProgressBarVisible(true);
186      background_view_->SetOobeProgress(chromeos::BackgroundView::SIGNIN);
187    }
188
189    background_window_->Show();
190  }
191  // If there's only new user pod, show the guest session link on it.
192  bool show_guest_link = controllers_.size() == 1;
193  for (size_t i = 0; i < controllers_.size(); ++i) {
194    (controllers_[i])->Init(static_cast<int>(i),
195                            static_cast<int>(controllers_.size()),
196                            show_guest_link);
197  }
198
199  EnableTooltipsIfNeeded(controllers_);
200
201  WmMessageListener::GetInstance()->AddObserver(this);
202
203  LoginUtils::Get()->PrewarmAuthentication();
204  if (CrosLibrary::Get()->EnsureLoaded())
205    CrosLibrary::Get()->GetLoginLibrary()->EmitLoginPromptReady();
206}
207
208void ExistingUserController::OwnBackground(
209    views::Widget* background_widget,
210    chromeos::BackgroundView* background_view) {
211  DCHECK(!background_window_);
212  background_window_ = background_widget;
213  background_view_ = background_view;
214}
215
216void ExistingUserController::LoginNewUser(const std::string& username,
217                                          const std::string& password) {
218  SelectNewUser();
219  UserController* new_user = controllers_.back();
220  DCHECK(new_user->is_new_user());
221  if (!new_user->is_new_user())
222    return;
223
224  if (password.empty())
225    return;
226
227  new_user->OnLogin(username, password);
228}
229
230void ExistingUserController::SelectNewUser() {
231  SelectUser(controllers_.size() - 1);
232}
233
234void ExistingUserController::SetStatusAreaEnabled(bool enable) {
235  if (background_view_) {
236    background_view_->SetStatusAreaEnabled(enable);
237  }
238}
239
240ExistingUserController::~ExistingUserController() {
241  ClearErrors();
242
243  if (background_window_)
244    background_window_->Close();
245
246  WmMessageListener::GetInstance()->RemoveObserver(this);
247
248  STLDeleteElements(&controllers_);
249  STLDeleteElements(&invisible_controllers_);
250}
251
252void ExistingUserController::Delete() {
253  delete_scheduled_instance_ = NULL;
254  delete this;
255}
256
257void ExistingUserController::ProcessWmMessage(const WmIpc::Message& message,
258                                              GdkWindow* window) {
259  if (message.type() != WM_IPC_MESSAGE_CHROME_CREATE_GUEST_WINDOW)
260    return;
261
262  ActivateWizard(std::string());
263}
264
265void ExistingUserController::SendSetLoginState(bool is_enabled) {
266  WmIpc::Message message(WM_IPC_MESSAGE_WM_SET_LOGIN_STATE);
267  message.set_param(0, is_enabled);
268  WmIpc::instance()->SendMessage(message);
269}
270
271void ExistingUserController::Login(UserController* source,
272                                   const string16& password) {
273  BootTimesLoader::Get()->RecordLoginAttempted();
274  std::vector<UserController*>::const_iterator i =
275      std::find(controllers_.begin(), controllers_.end(), source);
276  DCHECK(i != controllers_.end());
277
278  if (i == controllers_.begin() + selected_view_index_) {
279    num_login_attempts_++;
280  } else {
281    selected_view_index_ = i - controllers_.begin();
282    num_login_attempts_ = 0;
283  }
284
285  // Disable clicking on other windows.
286  SendSetLoginState(false);
287
288  // Use the same LoginPerformer for subsequent login as it has state
289  // such as CAPTCHA challenge token & corresponding user input.
290  if (!login_performer_.get() || num_login_attempts_ <= 1) {
291    login_performer_.reset(new LoginPerformer(this));
292  }
293  login_performer_->Login(controllers_[selected_view_index_]->user().email(),
294                          UTF16ToUTF8(password));
295}
296
297void ExistingUserController::WhiteListCheckFailed(const std::string& email) {
298  ShowError(IDS_LOGIN_ERROR_WHITELIST, email);
299
300  // Reenable userview and use ClearAndEnablePassword to keep username on
301  // screen with the error bubble.
302  controllers_[selected_view_index_]->ClearAndEnablePassword();
303
304  // Reenable clicking on other windows.
305  SendSetLoginState(true);
306}
307
308void ExistingUserController::LoginOffTheRecord() {
309  // Check allow_guest in case this call is fired from key accelerator.
310  // Must not proceed without signature verification.
311  bool trusted_setting_available = user_settings_->RequestTrustedAllowGuest(
312      method_factory_.NewRunnableMethod(
313          &ExistingUserController::LoginOffTheRecord));
314  if (!trusted_setting_available) {
315    // Value of AllowGuest setting is still not verified.
316    // Another attempt will be invoked again after verification completion.
317    return;
318  }
319  if (!UserCrosSettingsProvider::cached_allow_guest()) {
320    // Disallowed.
321    return;
322  }
323
324  // Disable clicking on other windows.
325  SendSetLoginState(false);
326
327  login_performer_.reset(new LoginPerformer(this));
328  login_performer_->LoginOffTheRecord();
329}
330
331void ExistingUserController::ClearErrors() {
332  // bubble_ will be set to NULL in callback.
333  if (bubble_)
334    bubble_->Close();
335}
336
337void ExistingUserController::OnUserSelected(UserController* source) {
338  std::vector<UserController*>::const_iterator i =
339      std::find(controllers_.begin(), controllers_.end(), source);
340  DCHECK(i != controllers_.end());
341  size_t new_selected_index = i - controllers_.begin();
342  if (new_selected_index != selected_view_index_ &&
343      selected_view_index_ != kNotSelected) {
344    controllers_[selected_view_index_]->ClearAndEnableFields();
345    controllers_[new_selected_index]->ClearAndEnableFields();
346    login_performer_.reset(NULL);
347    num_login_attempts_ = 0;
348  }
349  selected_view_index_ = new_selected_index;
350}
351
352void ExistingUserController::ActivateWizard(const std::string& screen_name) {
353  // WizardController takes care of deleting itself when done.
354  WizardController* controller = new WizardController();
355
356  // Give the background window to the controller.
357  controller->OwnBackground(background_window_, background_view_);
358  background_window_ = NULL;
359
360  controller->Init(screen_name, background_bounds_);
361  controller->set_start_url(start_url_);
362  controller->Show();
363
364  // And schedule us for deletion. We delay for a second as the window manager
365  // is doing an animation with our windows.
366  DCHECK(!delete_scheduled_instance_);
367  delete_scheduled_instance_ = this;
368
369  delete_timer_.Start(base::TimeDelta::FromSeconds(1), this,
370                      &ExistingUserController::Delete);
371}
372
373void ExistingUserController::RemoveUser(UserController* source) {
374  ClearErrors();
375
376  // Owner is not allowed to be removed from the device.
377  // It must be enforced at upper levels.
378  DCHECK(user_settings_->RequestTrustedOwner(NULL));
379  DCHECK(source->user().email() != UserCrosSettingsProvider::cached_owner());
380
381  UserManager::Get()->RemoveUser(source->user().email());
382
383  controllers_.erase(controllers_.begin() + source->user_index());
384
385  EnableTooltipsIfNeeded(controllers_);
386
387  // Update user count before unmapping windows, otherwise window manager won't
388  // be in the right state.
389  int new_size = static_cast<int>(controllers_.size());
390  for (int i = 0; i < new_size; ++i)
391    controllers_[i]->UpdateUserCount(i, new_size);
392
393  // Delete the encrypted user directory.
394  new RemoveAttempt(source->user().email());
395  // We need to unmap entry windows, the windows will be unmapped in destructor.
396  delete source;
397
398  // Nothing to insert.
399  if (invisible_controllers_.empty())
400    return;
401
402  // Insert just before guest or add new user pods if any.
403  int insert_position = new_size;
404  while (insert_position > 0 &&
405         (controllers_[insert_position - 1]->is_new_user() ||
406          controllers_[insert_position - 1]->is_guest()))
407    --insert_position;
408
409  controllers_.insert(controllers_.begin() + insert_position,
410                      invisible_controllers_[0]);
411  invisible_controllers_.erase(invisible_controllers_.begin());
412
413  // Update counts for exiting pods.
414  new_size = static_cast<int>(controllers_.size());
415  for (int i = 0; i < new_size; ++i) {
416    if (i != insert_position)
417      controllers_[i]->UpdateUserCount(i, new_size);
418  }
419
420  // And initialize new one that was invisible.
421  controllers_[insert_position]->Init(insert_position, new_size, false);
422
423  EnableTooltipsIfNeeded(controllers_);
424}
425
426void ExistingUserController::SelectUser(int index) {
427  if (index >= 0 && index < static_cast<int>(controllers_.size()) &&
428      index != static_cast<int>(selected_view_index_)) {
429    WmIpc::Message message(WM_IPC_MESSAGE_WM_SELECT_LOGIN_USER);
430    message.set_param(0, index);
431    WmIpc::instance()->SendMessage(message);
432  }
433}
434
435void ExistingUserController::OnLoginFailure(const LoginFailure& failure) {
436  std::string error = failure.GetErrorString();
437
438  // Check networking after trying to login in case user is
439  // cached locally or the local admin account.
440  NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary();
441  if (!network || !CrosLibrary::Get()->EnsureLoaded()) {
442    ShowError(IDS_LOGIN_ERROR_NO_NETWORK_LIBRARY, error);
443  } else if (!network->Connected()) {
444    ShowError(IDS_LOGIN_ERROR_OFFLINE_FAILED_NETWORK_NOT_CONNECTED, error);
445  } else {
446    if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED &&
447        failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) {
448      if (!failure.error().captcha().image_url.is_empty()) {
449        CaptchaView* view =
450            new CaptchaView(failure.error().captcha().image_url, false);
451        view->Init();
452        view->set_delegate(this);
453        views::Window* window = browser::CreateViewsWindow(
454            GetNativeWindow(), gfx::Rect(), view);
455        window->SetIsAlwaysOnTop(true);
456        window->Show();
457      } else {
458        LOG(WARNING) << "No captcha image url was found?";
459        ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
460      }
461    } else if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED &&
462               failure.error().state() ==
463                   GoogleServiceAuthError::HOSTED_NOT_ALLOWED) {
464      ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED, error);
465    } else {
466      if (controllers_[selected_view_index_]->is_new_user())
467        ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_NEW, error);
468      else
469        ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
470    }
471  }
472
473  controllers_[selected_view_index_]->ClearAndEnablePassword();
474
475  // Reenable clicking on other windows.
476  SendSetLoginState(true);
477}
478
479void ExistingUserController::AppendStartUrlToCmdline() {
480  if (start_url_.is_valid())
481    CommandLine::ForCurrentProcess()->AppendArg(start_url_.spec());
482}
483
484gfx::NativeWindow ExistingUserController::GetNativeWindow() const {
485  return GTK_WINDOW(
486      static_cast<views::WidgetGtk*>(background_window_)->GetNativeView());
487}
488
489void ExistingUserController::ShowError(int error_id,
490                                       const std::string& details) {
491  ClearErrors();
492  string16 error_text;
493  // GetStringF fails on debug build if there's no replacement in the string.
494  if (error_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED) {
495    error_text = l10n_util::GetStringFUTF16(
496        error_id, l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
497  } else {
498    error_text = l10n_util::GetStringUTF16(error_id);
499  }
500  // TODO(dpolukhin): show detailed error info. |details| string contains
501  // low level error info that is not localized and even is not user friendly.
502  // For now just ignore it because error_text contains all required information
503  // for end users, developers can see details string in Chrome logs.
504
505  gfx::Rect bounds =
506      controllers_[selected_view_index_]->GetMainInputScreenBounds();
507  BubbleBorder::ArrowLocation arrow;
508  if (controllers_[selected_view_index_]->is_new_user()) {
509    arrow = BubbleBorder::LEFT_TOP;
510  } else {
511    // Point info bubble arrow to cursor position (approximately).
512    bounds.set_width(kCursorOffset * 2);
513    arrow = BubbleBorder::BOTTOM_LEFT;
514  }
515  string16 help_link;
516  if (error_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED) {
517    help_link = l10n_util::GetStringUTF16(IDS_LEARN_MORE);
518  } else if (num_login_attempts_ > static_cast<size_t>(1)) {
519    help_link = l10n_util::GetStringUTF16(IDS_CANT_ACCESS_ACCOUNT_BUTTON);
520  }
521
522  bubble_ = MessageBubble::Show(
523      controllers_[selected_view_index_]->controls_window(),
524      bounds,
525      arrow,
526      ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING),
527      UTF16ToWide(error_text),
528      UTF16ToWide(help_link),
529      this);
530}
531
532void ExistingUserController::OnLoginSuccess(
533    const std::string& username,
534    const std::string& password,
535    const GaiaAuthConsumer::ClientLoginResult& credentials,
536    bool pending_requests) {
537  // LoginPerformer instance will delete itself once online auth result is OK.
538  // In case of failure it'll bring up ScreenLock and ask for
539  // correct password/display error message.
540  // Even in case when following online,offline protocol and returning
541  // requests_pending = false, let LoginPerformer delete itself.
542  login_performer_->set_delegate(NULL);
543  LoginPerformer* performer = login_performer_.release();
544  performer = NULL;
545  bool known_user = UserManager::Get()->IsKnownUser(username);
546  AppendStartUrlToCmdline();
547  controllers_[selected_view_index_]->StopThrobber();
548  if (selected_view_index_ + 1 == controllers_.size() && !known_user) {
549#if defined(OFFICIAL_BUILD)
550    CommandLine::ForCurrentProcess()->AppendSwitchPath(
551        switches::kLoadExtension,
552        FilePath(kGetStartedPath));
553    CommandLine::ForCurrentProcess()->AppendArg(kGetStartedURL);
554#endif  // OFFICIAL_BUILD
555    if (credentials.two_factor) {
556      // If we have a two factor error and and this is a new user,
557      // load the personal settings page.
558      // TODO(stevenjb): direct the user to a lightweight sync login page.
559      CommandLine::ForCurrentProcess()->AppendArg(kSettingsSyncLoginUrl);
560    }
561    // For new user login don't launch browser until we pass image screen.
562    LoginUtils::Get()->EnableBrowserLaunch(false);
563    LoginUtils::Get()->CompleteLogin(username,
564                                     password,
565                                     credentials,
566                                     pending_requests);
567    ActivateWizard(WizardController::IsDeviceRegistered() ?
568        WizardController::kUserImageScreenName :
569        WizardController::kRegistrationScreenName);
570  } else {
571    // Hide the login windows now.
572    WmIpc::Message message(WM_IPC_MESSAGE_WM_HIDE_LOGIN);
573    WmIpc::instance()->SendMessage(message);
574
575    LoginUtils::Get()->CompleteLogin(username,
576                                     password,
577                                     credentials,
578                                     pending_requests);
579
580    // Delay deletion as we're on the stack.
581    MessageLoop::current()->DeleteSoon(FROM_HERE, this);
582  }
583}
584
585void ExistingUserController::OnOffTheRecordLoginSuccess() {
586  if (WizardController::IsDeviceRegistered()) {
587    LoginUtils::Get()->CompleteOffTheRecordLogin(start_url_);
588  } else {
589    // Postpone CompleteOffTheRecordLogin until registration completion.
590    ActivateWizard(WizardController::kRegistrationScreenName);
591  }
592}
593
594void ExistingUserController::OnPasswordChangeDetected(
595    const GaiaAuthConsumer::ClientLoginResult& credentials) {
596  // When signing in as a "New user" always remove old cryptohome.
597  if (selected_view_index_ == controllers_.size() - 1) {
598    ResyncEncryptedData();
599    return;
600  }
601
602  // Must not proceed without signature verification.
603  bool trusted_setting_available = user_settings_->RequestTrustedOwner(
604      method_factory_.NewRunnableMethod(
605          &ExistingUserController::OnPasswordChangeDetected,
606          credentials));
607  if (!trusted_setting_available) {
608    // Value of owner email is still not verified.
609    // Another attempt will be invoked after verification completion.
610    return;
611  }
612  // TODO(altimofeev): remove this constrain when full sync for the owner will
613  // be correctly handled.
614  bool full_sync_disabled = (UserCrosSettingsProvider::cached_owner() ==
615      controllers_[selected_view_index_]->user().email());
616
617  PasswordChangedView* view = new PasswordChangedView(this, full_sync_disabled);
618  views::Window* window = browser::CreateViewsWindow(GetNativeWindow(),
619                                                     gfx::Rect(),
620                                                     view);
621  window->SetIsAlwaysOnTop(true);
622  window->Show();
623}
624
625void ExistingUserController::OnHelpLinkActivated() {
626  DCHECK(login_performer_->error().state() != GoogleServiceAuthError::NONE);
627  if (!help_app_.get())
628    help_app_.reset(new HelpAppLauncher(GetNativeWindow()));
629  switch (login_performer_->error().state()) {
630    case GoogleServiceAuthError::CONNECTION_FAILED:
631      help_app_->ShowHelpTopic(
632          HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE);
633      break;
634    case GoogleServiceAuthError::ACCOUNT_DISABLED:
635        help_app_->ShowHelpTopic(
636            HelpAppLauncher::HELP_ACCOUNT_DISABLED);
637        break;
638    case GoogleServiceAuthError::HOSTED_NOT_ALLOWED:
639        help_app_->ShowHelpTopic(
640            HelpAppLauncher::HELP_HOSTED_ACCOUNT);
641        break;
642    default:
643      help_app_->ShowHelpTopic(login_performer_->login_timed_out() ?
644          HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE :
645          HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
646      break;
647  }
648}
649
650void ExistingUserController::OnCaptchaEntered(const std::string& captcha) {
651  login_performer_->set_captcha(captcha);
652}
653
654void ExistingUserController::RecoverEncryptedData(
655    const std::string& old_password) {
656  // LoginPerformer instance has state of the user so it should exist.
657  if (login_performer_.get())
658    login_performer_->RecoverEncryptedData(old_password);
659}
660
661void ExistingUserController::ResyncEncryptedData() {
662  // LoginPerformer instance has state of the user so it should exist.
663  if (login_performer_.get())
664    login_performer_->ResyncEncryptedData();
665}
666
667}  // namespace chromeos
668