• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
EnableTooltipsIfNeeded(const std::vector<chromeos::UserController * > & controllers)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 
ViewsLoginDisplay(LoginDisplay::Delegate * delegate,const gfx::Rect & background_bounds)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 
~ViewsLoginDisplay()74 ViewsLoginDisplay::~ViewsLoginDisplay() {
75   ClearErrors();
76   STLDeleteElements(&controllers_);
77   STLDeleteElements(&invisible_controllers_);
78 }
79 
80 ////////////////////////////////////////////////////////////////////////////////
81 // ViewsLoginDisplay, LoginDisplay implementation:
82 //
83 
Init(const std::vector<UserManager::User> & users,bool show_guest,bool show_new_user)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 
OnBeforeUserRemoved(const std::string & username)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 
OnUserImageChanged(UserManager::User * user)137 void ViewsLoginDisplay::OnUserImageChanged(UserManager::User* user) {
138   UserController* controller = GetUserControllerByEmail(user->email());
139   if (controller)
140     controller->OnUserImageChanged(user);
141 }
142 
OnUserRemoved(const std::string & username)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 
OnFadeOut()178 void ViewsLoginDisplay::OnFadeOut() {
179   controllers_[selected_view_index_]->StopThrobber();
180 }
181 
SetUIEnabled(bool is_enabled)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 
ShowError(int error_msg_id,int login_attempts,HelpAppLauncher::HelpTopic help_topic_id)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 
CreateAccount()248 void ViewsLoginDisplay::CreateAccount() {
249   delegate()->CreateAccount();
250 }
251 
Login(UserController * source,const string16 & password)252 void ViewsLoginDisplay::Login(UserController* source,
253                               const string16& password) {
254   delegate()->Login(source->user().email(), UTF16ToUTF8(password));
255 }
256 
LoginAsGuest()257 void ViewsLoginDisplay::LoginAsGuest() {
258   delegate()->LoginAsGuest();
259 }
260 
ClearErrors()261 void ViewsLoginDisplay::ClearErrors() {
262   // bubble_ will be set to NULL in callback.
263   if (bubble_)
264     bubble_->Close();
265 }
266 
OnUserSelected(UserController * source)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 
RemoveUser(UserController * source)283 void ViewsLoginDisplay::RemoveUser(UserController* source) {
284   ClearErrors();
285   UserManager::Get()->RemoveUser(source->user().email(), this);
286 }
287 
SelectUser(int index)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 
StartEnterpriseEnrollment()297 void ViewsLoginDisplay::StartEnterpriseEnrollment() {
298   delegate()->OnStartEnterpriseEnrollment();
299 }
300 
301 ////////////////////////////////////////////////////////////////////////////////
302 // ViewsLoginDisplay, views::MessageBubbleDelegate implementation:
303 //
304 
OnHelpLinkActivated()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 
GetUserControllerByEmail(const std::string & email)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