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