Home | History | Annotate | Download | only in login
      1 // Copyright (c) 2011 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/views_login_display.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/stl_util-inl.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "chrome/browser/chromeos/login/help_app_launcher.h"
     12 #include "chrome/browser/chromeos/login/message_bubble.h"
     13 #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
     14 #include "chrome/browser/chromeos/view_ids.h"
     15 #include "chrome/browser/chromeos/wm_ipc.h"
     16 #include "chrome/browser/ui/views/window.h"
     17 #include "grit/chromium_strings.h"
     18 #include "grit/generated_resources.h"
     19 #include "grit/theme_resources.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 #include "ui/base/resource/resource_bundle.h"
     22 #include "views/widget/widget_gtk.h"
     23 #include "views/window/window.h"
     24 
     25 namespace {
     26 
     27 // Max number of users we'll show. The true max is the min of this and the
     28 // number of windows that fit on the screen.
     29 const size_t kMaxUsers = 6;
     30 
     31 // Minimum number of users we'll show (including Guest and New User pods).
     32 const size_t kMinUsers = 3;
     33 
     34 // Used to indicate no user has been selected.
     35 const size_t kNotSelected = -1;
     36 
     37 // Offset of cursor in first position from edit left side. It's used to position
     38 // info bubble arrow to cursor.
     39 const int kCursorOffset = 5;
     40 
     41 // Checks if display names are unique. If there are duplicates, enables
     42 // tooltips with full emails to let users distinguish their accounts.
     43 // Otherwise, disables the tooltips.
     44 void EnableTooltipsIfNeeded(
     45     const std::vector<chromeos::UserController*>& controllers) {
     46   std::map<std::string, int> visible_display_names;
     47   for (size_t i = 0; i + 1 < controllers.size(); ++i) {
     48     const std::string& display_name =
     49         controllers[i]->user().GetDisplayName();
     50     ++visible_display_names[display_name];
     51   }
     52   for (size_t i = 0; i < controllers.size(); ++i) {
     53     const std::string& display_name =
     54         controllers[i]->user().GetDisplayName();
     55     bool show_tooltip = controllers[i]->is_new_user() ||
     56                         controllers[i]->is_guest() ||
     57                         visible_display_names[display_name] > 1;
     58     controllers[i]->EnableNameTooltip(show_tooltip);
     59   }
     60 }
     61 
     62 }  // namespace
     63 
     64 namespace chromeos {
     65 
     66 ViewsLoginDisplay::ViewsLoginDisplay(LoginDisplay::Delegate* delegate,
     67                                      const gfx::Rect& background_bounds)
     68     : LoginDisplay(delegate, background_bounds),
     69       bubble_(NULL),
     70       controller_for_removal_(NULL),
     71       selected_view_index_(kNotSelected) {
     72 }
     73 
     74 ViewsLoginDisplay::~ViewsLoginDisplay() {
     75   ClearErrors();
     76   STLDeleteElements(&controllers_);
     77   STLDeleteElements(&invisible_controllers_);
     78 }
     79 
     80 ////////////////////////////////////////////////////////////////////////////////
     81 // ViewsLoginDisplay, LoginDisplay implementation:
     82 //
     83 
     84 void ViewsLoginDisplay::Init(const std::vector<UserManager::User>& users,
     85                              bool show_guest,
     86                              bool show_new_user) {
     87   size_t max_users = kMaxUsers;
     88   if (width() > 0) {
     89     size_t users_per_screen = (width() - login::kUserImageSize) /
     90         (UserController::kUnselectedSize + UserController::kPadding);
     91     max_users = std::max(kMinUsers, std::min(kMaxUsers, users_per_screen));
     92   }
     93 
     94   size_t visible_users_count = std::min(users.size(), max_users -
     95       static_cast<int>(show_guest) - static_cast<int>(show_new_user));
     96   for (size_t i = 0; i < users.size(); ++i) {
     97     UserController* user_controller = new UserController(this, users[i]);
     98     if (controllers_.size() < visible_users_count) {
     99       controllers_.push_back(user_controller);
    100     } else if (user_controller->is_owner()) {
    101       // Make sure that owner of the device is always visible on login screen.
    102       invisible_controllers_.insert(invisible_controllers_.begin(),
    103                                     controllers_.back());
    104       controllers_.back() = user_controller;
    105     } else {
    106       invisible_controllers_.push_back(user_controller);
    107     }
    108   }
    109   if (show_guest)
    110     controllers_.push_back(new UserController(this, true));
    111 
    112   if (show_new_user)
    113     controllers_.push_back(new UserController(this, false));
    114 
    115   // If there's only new user pod, show the guest session link on it.
    116   bool show_guest_link = controllers_.size() == 1;
    117   for (size_t i = 0; i < controllers_.size(); ++i) {
    118     (controllers_[i])->Init(static_cast<int>(i),
    119                             static_cast<int>(controllers_.size()),
    120                             show_guest_link);
    121   }
    122   EnableTooltipsIfNeeded(controllers_);
    123 }
    124 
    125 void ViewsLoginDisplay::OnBeforeUserRemoved(const std::string& username) {
    126   controller_for_removal_ = controllers_[selected_view_index_];
    127   controllers_.erase(controllers_.begin() + selected_view_index_);
    128   EnableTooltipsIfNeeded(controllers_);
    129 
    130   // Update user count before unmapping windows, otherwise window manager won't
    131   // be in the right state.
    132   int new_size = static_cast<int>(controllers_.size());
    133   for (int i = 0; i < new_size; ++i)
    134     controllers_[i]->UpdateUserCount(i, new_size);
    135 }
    136 
    137 void ViewsLoginDisplay::OnUserImageChanged(UserManager::User* user) {
    138   UserController* controller = GetUserControllerByEmail(user->email());
    139   if (controller)
    140     controller->OnUserImageChanged(user);
    141 }
    142 
    143 void ViewsLoginDisplay::OnUserRemoved(const std::string& username) {
    144   // We need to unmap entry windows, the windows will be unmapped in destructor.
    145   MessageLoop::current()->DeleteSoon(FROM_HERE, controller_for_removal_);
    146   controller_for_removal_ = NULL;
    147 
    148   // Nothing to insert.
    149   if (invisible_controllers_.empty())
    150     return;
    151 
    152   // Insert just before guest or add new user pods if any.
    153   size_t new_size = controllers_.size();
    154   size_t insert_position = new_size;
    155   while (insert_position > 0 &&
    156          (controllers_[insert_position - 1]->is_new_user() ||
    157           controllers_[insert_position - 1]->is_guest())) {
    158     --insert_position;
    159   }
    160 
    161   controllers_.insert(controllers_.begin() + insert_position,
    162                       invisible_controllers_[0]);
    163   invisible_controllers_.erase(invisible_controllers_.begin());
    164 
    165   // Update counts for exiting pods.
    166   new_size = controllers_.size();
    167   for (size_t i = 0; i < new_size; ++i) {
    168     if (i != insert_position)
    169       controllers_[i]->UpdateUserCount(i, new_size);
    170   }
    171 
    172   // And initialize new one that was invisible.
    173   controllers_[insert_position]->Init(insert_position, new_size, false);
    174 
    175   EnableTooltipsIfNeeded(controllers_);
    176 }
    177 
    178 void ViewsLoginDisplay::OnFadeOut() {
    179   controllers_[selected_view_index_]->StopThrobber();
    180 }
    181 
    182 void ViewsLoginDisplay::SetUIEnabled(bool is_enabled) {
    183   // Send message to WM to enable/disable click on windows.
    184   WmIpc::Message message(WM_IPC_MESSAGE_WM_SET_LOGIN_STATE);
    185   message.set_param(0, is_enabled);
    186   WmIpc::instance()->SendMessage(message);
    187 
    188   if (is_enabled)
    189     controllers_[selected_view_index_]->ClearAndEnablePassword();
    190 }
    191 
    192 void ViewsLoginDisplay::ShowError(int error_msg_id,
    193                                   int login_attempts,
    194                                   HelpAppLauncher::HelpTopic help_topic_id) {
    195   ClearErrors();
    196   string16 error_text;
    197   error_msg_id_ = error_msg_id;
    198   help_topic_id_ = help_topic_id;
    199 
    200   // GetStringF fails on debug build if there's no replacement in the string.
    201   if (error_msg_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED) {
    202     error_text = l10n_util::GetStringFUTF16(
    203         error_msg_id, l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
    204   } else if (error_msg_id == IDS_LOGIN_ERROR_CAPTIVE_PORTAL) {
    205     error_text = l10n_util::GetStringFUTF16(
    206         error_msg_id, delegate()->GetConnectedNetworkName());
    207   } else {
    208     error_text = l10n_util::GetStringUTF16(error_msg_id);
    209   }
    210 
    211   gfx::Rect bounds =
    212       controllers_[selected_view_index_]->GetMainInputScreenBounds();
    213   BubbleBorder::ArrowLocation arrow;
    214   if (controllers_[selected_view_index_]->is_new_user()) {
    215     arrow = BubbleBorder::LEFT_TOP;
    216   } else {
    217     // Point info bubble arrow to cursor position (approximately).
    218     bounds.set_width(kCursorOffset * 2);
    219     arrow = BubbleBorder::BOTTOM_LEFT;
    220   }
    221 
    222   string16 help_link;
    223   if (error_msg_id == IDS_LOGIN_ERROR_CAPTIVE_PORTAL) {
    224     help_link = l10n_util::GetStringUTF16(IDS_LOGIN_FIX_CAPTIVE_PORTAL);
    225   } else if (error_msg_id == IDS_LOGIN_ERROR_CAPTIVE_PORTAL_NO_GUEST_MODE) {
    226     // No help link is needed.
    227   } else if (error_msg_id == IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED ||
    228              login_attempts > 1) {
    229     help_link = l10n_util::GetStringUTF16(IDS_LEARN_MORE);
    230   }
    231 
    232   bubble_ = MessageBubble::Show(
    233       controllers_[selected_view_index_]->controls_window(),
    234       bounds,
    235       arrow,
    236       ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_WARNING),
    237       UTF16ToWide(error_text),
    238       UTF16ToWide(help_link),
    239       this);
    240   WizardAccessibilityHelper::GetInstance()->MaybeSpeak(
    241       UTF16ToUTF8(error_text).c_str(), false, false);
    242 }
    243 
    244 ////////////////////////////////////////////////////////////////////////////////
    245 // ViewsLoginDisplay, UserController::Delegate implementation:
    246 //
    247 
    248 void ViewsLoginDisplay::CreateAccount() {
    249   delegate()->CreateAccount();
    250 }
    251 
    252 void ViewsLoginDisplay::Login(UserController* source,
    253                               const string16& password) {
    254   delegate()->Login(source->user().email(), UTF16ToUTF8(password));
    255 }
    256 
    257 void ViewsLoginDisplay::LoginAsGuest() {
    258   delegate()->LoginAsGuest();
    259 }
    260 
    261 void ViewsLoginDisplay::ClearErrors() {
    262   // bubble_ will be set to NULL in callback.
    263   if (bubble_)
    264     bubble_->Close();
    265 }
    266 
    267 void ViewsLoginDisplay::OnUserSelected(UserController* source) {
    268   std::vector<UserController*>::const_iterator i =
    269       std::find(controllers_.begin(), controllers_.end(), source);
    270   DCHECK(i != controllers_.end());
    271   size_t new_selected_index = i - controllers_.begin();
    272   if (new_selected_index != selected_view_index_ &&
    273       selected_view_index_ != kNotSelected) {
    274     controllers_[selected_view_index_]->ClearAndEnableFields();
    275     controllers_[new_selected_index]->ClearAndEnableFields();
    276     delegate()->OnUserSelected(source->user().email());
    277   }
    278   selected_view_index_ = new_selected_index;
    279   WizardAccessibilityHelper::GetInstance()->MaybeSpeak(
    280       source->GetAccessibleUserLabel().c_str(), false, true);
    281 }
    282 
    283 void ViewsLoginDisplay::RemoveUser(UserController* source) {
    284   ClearErrors();
    285   UserManager::Get()->RemoveUser(source->user().email(), this);
    286 }
    287 
    288 void ViewsLoginDisplay::SelectUser(int index) {
    289   if (index >= 0 && index < static_cast<int>(controllers_.size()) &&
    290       index != static_cast<int>(selected_view_index_)) {
    291     WmIpc::Message message(WM_IPC_MESSAGE_WM_SELECT_LOGIN_USER);
    292     message.set_param(0, index);
    293     WmIpc::instance()->SendMessage(message);
    294   }
    295 }
    296 
    297 void ViewsLoginDisplay::StartEnterpriseEnrollment() {
    298   delegate()->OnStartEnterpriseEnrollment();
    299 }
    300 
    301 ////////////////////////////////////////////////////////////////////////////////
    302 // ViewsLoginDisplay, views::MessageBubbleDelegate implementation:
    303 //
    304 
    305 void ViewsLoginDisplay::OnHelpLinkActivated() {
    306   ClearErrors();
    307   if (error_msg_id_ == IDS_LOGIN_ERROR_CAPTIVE_PORTAL) {
    308     delegate()->FixCaptivePortal();
    309     return;
    310   }
    311   if (!parent_window())
    312     return;
    313   if (!help_app_.get())
    314     help_app_ = new HelpAppLauncher(parent_window());
    315   help_app_->ShowHelpTopic(help_topic_id_);
    316 }
    317 
    318 ////////////////////////////////////////////////////////////////////////////////
    319 // ViewsLoginDisplay, private:
    320 //
    321 
    322 UserController* ViewsLoginDisplay::GetUserControllerByEmail(
    323     const std::string& email) {
    324   std::vector<UserController*>::iterator i;
    325   for (i = controllers_.begin(); i != controllers_.end(); ++i) {
    326     if ((*i)->user().email() == email)
    327       return *i;
    328   }
    329   for (i = invisible_controllers_.begin();
    330        i != invisible_controllers_.end(); ++i) {
    331     if ((*i)->user().email() == email)
    332       return *i;
    333   }
    334   return NULL;
    335 }
    336 
    337 }  // namespace chromeos
    338