• 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/new_user_view.h"
6 
7 #include <signal.h>
8 #include <sys/types.h>
9 
10 #include <algorithm>
11 #include <vector>
12 
13 #include "base/callback.h"
14 #include "base/command_line.h"
15 #include "base/message_loop.h"
16 #include "base/process_util.h"
17 #include "base/string_util.h"
18 #include "base/utf_string_conversions.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chromeos/cros/cros_library.h"
21 #include "chrome/browser/chromeos/login/rounded_rect_painter.h"
22 #include "chrome/browser/chromeos/login/textfield_with_margin.h"
23 #include "chrome/browser/chromeos/login/wizard_accessibility_helper.h"
24 #include "chrome/browser/chromeos/user_cros_settings_provider.h"
25 #include "chrome/browser/chromeos/views/copy_background.h"
26 #include "chrome/browser/prefs/pref_service.h"
27 #include "chrome/common/pref_names.h"
28 #include "grit/app_resources.h"
29 #include "grit/chromium_strings.h"
30 #include "grit/generated_resources.h"
31 #include "ui/base/keycodes/keyboard_codes.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/base/resource/resource_bundle.h"
34 #include "ui/gfx/font.h"
35 #include "views/controls/button/menu_button.h"
36 #include "views/controls/label.h"
37 #include "views/controls/textfield/textfield.h"
38 #include "views/controls/throbber.h"
39 #include "views/widget/widget_gtk.h"
40 
41 using views::View;
42 
43 namespace {
44 
45 const int kTextfieldWidth = 230;
46 const int kSplitterHeight = 1;
47 const int kTitlePad = 20;
48 const int kRowPad = 13;
49 const int kBottomPad = 33;
50 const int kLeftPad = 33;
51 const int kColumnPad = 7;
52 const int kLanguagesMenuHeight = 25;
53 const int kLanguagesMenuPad = 5;
54 const SkColor kLanguagesMenuTextColor = 0xFF999999;
55 const SkColor kErrorColor = 0xFF8F384F;
56 const SkColor kSplitterUp1Color = 0xFFD0D2D3;
57 const SkColor kSplitterUp2Color = 0xFFE1E3E4;
58 const SkColor kSplitterDown1Color = 0xFFE3E6E8;
59 const SkColor kSplitterDown2Color = 0xFFEAEDEE;
60 const char kDefaultDomain[] = "@gmail.com";
61 
62 // Textfield that adds domain to the entered username if focus is lost and
63 // username doesn't have full domain.
64 class UsernameField : public chromeos::TextfieldWithMargin {
65  public:
UsernameField(chromeos::NewUserView * controller)66   explicit UsernameField(chromeos::NewUserView* controller)
67       : controller_(controller) {}
68 
69   // views::Textfield overrides:
OnBlur()70   virtual void OnBlur() OVERRIDE {
71     string16 user_input;
72     bool was_trim = TrimWhitespace(text(), TRIM_ALL, &user_input) != TRIM_NONE;
73     if (!user_input.empty()) {
74       std::string username = UTF16ToUTF8(user_input);
75 
76       if (username.find('@') == std::string::npos) {
77         username += kDefaultDomain;
78         SetText(UTF8ToUTF16(username));
79         was_trim = false;
80       }
81     }
82 
83     if (was_trim)
84       SetText(user_input);
85   }
86 
87   // Overridden from views::View:
OnKeyPressed(const views::KeyEvent & e)88   virtual bool OnKeyPressed(const views::KeyEvent& e) OVERRIDE {
89     if (e.key_code() == ui::VKEY_LEFT) {
90       return controller_->NavigateAway();
91     }
92     return TextfieldWithMargin::OnKeyPressed(e);
93   }
94 
95  private:
96   chromeos::NewUserView* controller_;
97   DISALLOW_COPY_AND_ASSIGN(UsernameField);
98 };
99 
100 }  // namespace
101 
102 namespace chromeos {
103 
NewUserView(Delegate * delegate,bool need_border,bool need_guest_link)104 NewUserView::NewUserView(Delegate* delegate,
105                          bool need_border,
106                          bool need_guest_link)
107     : username_field_(NULL),
108       password_field_(NULL),
109       title_label_(NULL),
110       title_hint_label_(NULL),
111       splitter_up1_(NULL),
112       splitter_up2_(NULL),
113       splitter_down1_(NULL),
114       splitter_down2_(NULL),
115       sign_in_button_(NULL),
116       guest_link_(NULL),
117       create_account_link_(NULL),
118       languages_menubutton_(NULL),
119       accel_focus_pass_(ui::VKEY_P, false, false, true),
120       accel_focus_user_(ui::VKEY_U, false, false, true),
121       accel_enterprise_enrollment_(ui::VKEY_E, false, true, true),
122       accel_login_off_the_record_(ui::VKEY_B, false, false, true),
123       accel_toggle_accessibility_(WizardAccessibilityHelper::GetAccelerator()),
124       delegate_(delegate),
125       ALLOW_THIS_IN_INITIALIZER_LIST(focus_grabber_factory_(this)),
126       login_in_process_(false),
127       need_border_(need_border),
128       need_guest_link_(false),
129       need_create_account_(false),
130       languages_menubutton_order_(-1),
131       sign_in_button_order_(-1) {
132   if (UserCrosSettingsProvider::cached_allow_guest()) {
133     need_create_account_ = true;
134     if (need_guest_link)
135       need_guest_link_ = true;
136   }
137 }
138 
~NewUserView()139 NewUserView::~NewUserView() {
140 }
141 
Init()142 void NewUserView::Init() {
143   if (need_border_) {
144     // Use rounded rect background.
145     set_border(CreateWizardBorder(&BorderDefinition::kUserBorder));
146     views::Painter* painter = CreateWizardPainter(
147         &BorderDefinition::kUserBorder);
148     set_background(views::Background::CreateBackgroundPainter(true, painter));
149   }
150 
151   title_label_ = new views::Label();
152   title_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
153   title_label_->SetMultiLine(true);
154   AddChildView(title_label_);
155 
156   title_hint_label_ = new views::Label();
157   title_hint_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
158   title_hint_label_->SetColor(SK_ColorGRAY);
159   title_hint_label_->SetMultiLine(true);
160   AddChildView(title_hint_label_);
161 
162   splitter_up1_ = CreateSplitter(kSplitterUp1Color);
163   splitter_up2_ = CreateSplitter(kSplitterUp2Color);
164   splitter_down1_ = CreateSplitter(kSplitterDown1Color);
165   splitter_down2_ = CreateSplitter(kSplitterDown2Color);
166 
167   username_field_ = new UsernameField(this);
168   username_field_->set_background(new CopyBackground(this));
169   username_field_->SetAccessibleName(
170       l10n_util::GetStringUTF16(IDS_CHROMEOS_ACC_USERNAME_LABEL));
171   AddChildView(username_field_);
172 
173   password_field_ = new TextfieldWithMargin(views::Textfield::STYLE_PASSWORD);
174   password_field_->set_background(new CopyBackground(this));
175   AddChildView(password_field_);
176 
177   language_switch_menu_.InitLanguageMenu();
178 
179   RecreatePeculiarControls();
180 
181   AddChildView(sign_in_button_);
182   if (need_guest_link_) {
183     InitLink(&guest_link_);
184   }
185   if (need_create_account_) {
186     InitLink(&create_account_link_);
187   }
188   AddChildView(languages_menubutton_);
189 
190   // Set up accelerators.
191   AddAccelerator(accel_focus_user_);
192   AddAccelerator(accel_focus_pass_);
193   AddAccelerator(accel_enterprise_enrollment_);
194   AddAccelerator(accel_login_off_the_record_);
195   AddAccelerator(accel_toggle_accessibility_);
196 
197   OnLocaleChanged();
198 
199   // Controller to handle events from textfields
200   username_field_->SetController(this);
201   password_field_->SetController(this);
202   if (!CrosLibrary::Get()->EnsureLoaded()) {
203     EnableInputControls(false);
204   }
205 
206   // The 'Sign in' button should be disabled when there is no text in the
207   // username and password fields.
208   sign_in_button_->SetEnabled(false);
209 }
210 
AcceleratorPressed(const views::Accelerator & accelerator)211 bool NewUserView::AcceleratorPressed(const views::Accelerator& accelerator) {
212   if (accelerator == accel_focus_user_) {
213     username_field_->RequestFocus();
214   } else if (accelerator == accel_focus_pass_) {
215     password_field_->RequestFocus();
216   } else if (accelerator == accel_enterprise_enrollment_) {
217     delegate_->OnStartEnterpriseEnrollment();
218   } else if (accelerator == accel_login_off_the_record_) {
219     delegate_->OnLoginAsGuest();
220   } else if (accelerator == accel_toggle_accessibility_) {
221     WizardAccessibilityHelper::GetInstance()->ToggleAccessibility();
222   } else {
223     return false;
224   }
225   return true;
226 }
227 
RecreatePeculiarControls()228 void NewUserView::RecreatePeculiarControls() {
229   // PreferredSize reported by MenuButton (and TextField) is not able
230   // to shrink, only grow; so recreate on text change.
231   delete languages_menubutton_;
232   languages_menubutton_ = new views::MenuButton(
233       NULL, std::wstring(), &language_switch_menu_, true);
234   languages_menubutton_->set_menu_marker(
235       ResourceBundle::GetSharedInstance().GetBitmapNamed(
236           IDR_MENU_DROPARROW_SHARP));
237   languages_menubutton_->SetEnabledColor(kLanguagesMenuTextColor);
238   languages_menubutton_->SetFocusable(true);
239   languages_menubutton_->SetEnabled(!g_browser_process->local_state()->
240       IsManagedPreference(prefs::kApplicationLocale));
241 
242   // There is no way to get native button preferred size after the button was
243   // sized so delete and recreate the button on text update.
244   delete sign_in_button_;
245   sign_in_button_ = new login::WideButton(this, std::wstring());
246   UpdateSignInButtonState();
247 
248   if (!CrosLibrary::Get()->EnsureLoaded())
249     sign_in_button_->SetEnabled(false);
250 }
251 
UpdateSignInButtonState()252 void NewUserView::UpdateSignInButtonState() {
253   bool enabled = !username_field_->text().empty() &&
254                  !password_field_->text().empty();
255   sign_in_button_->SetEnabled(enabled);
256 }
257 
CreateSplitter(SkColor color)258 views::View* NewUserView::CreateSplitter(SkColor color) {
259   views::View* splitter = new views::View();
260   splitter->set_background(views::Background::CreateSolidBackground(color));
261   AddChildView(splitter);
262   return splitter;
263 }
264 
AddChildView(View * view)265 void NewUserView::AddChildView(View* view) {
266   // languages_menubutton_ and sign_in_button_ are recreated on text change,
267   // so we restore their original position in layout.
268   if (view == languages_menubutton_) {
269     if (languages_menubutton_order_ < 0) {
270       languages_menubutton_order_ = child_count();
271     }
272     views::View::AddChildViewAt(view, languages_menubutton_order_);
273   } else if (view == sign_in_button_) {
274     if (sign_in_button_order_ < 0) {
275       sign_in_button_order_ = child_count();
276     }
277     views::View::AddChildViewAt(view, sign_in_button_order_);
278   } else {
279     views::View::AddChildView(view);
280   }
281 }
282 
UpdateLocalizedStringsAndFonts()283 void NewUserView::UpdateLocalizedStringsAndFonts() {
284   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
285   gfx::Font title_font = rb.GetFont(ResourceBundle::MediumBoldFont).DeriveFont(
286       kLoginTitleFontDelta);
287   const gfx::Font& title_hint_font = rb.GetFont(ResourceBundle::BoldFont);
288   const gfx::Font& base_font = rb.GetFont(ResourceBundle::BaseFont);
289 
290   title_label_->SetFont(title_font);
291   title_label_->SetText(UTF16ToWide(
292       l10n_util::GetStringUTF16(IDS_LOGIN_TITLE)));
293   title_hint_label_->SetFont(title_hint_font);
294   title_hint_label_->SetText(UTF16ToWide(
295       l10n_util::GetStringUTF16(IDS_LOGIN_TITLE_HINT)));
296   SetAndCorrectTextfieldFont(username_field_, base_font);
297   username_field_->set_text_to_display_when_empty(
298       l10n_util::GetStringUTF16(IDS_LOGIN_USERNAME));
299   SetAndCorrectTextfieldFont(password_field_, base_font);
300   password_field_->set_text_to_display_when_empty(
301       l10n_util::GetStringUTF16(IDS_LOGIN_PASSWORD));
302   sign_in_button_->SetLabel(UTF16ToWide(
303       l10n_util::GetStringUTF16(IDS_LOGIN_BUTTON)));
304   if (need_guest_link_) {
305     guest_link_->SetFont(base_font);
306     guest_link_->SetText(UTF16ToWide(
307         l10n_util::GetStringUTF16(IDS_BROWSE_WITHOUT_SIGNING_IN_BUTTON)));
308   }
309   if (need_create_account_) {
310     create_account_link_->SetFont(base_font);
311     create_account_link_->SetText(
312         UTF16ToWide(l10n_util::GetStringUTF16(IDS_CREATE_ACCOUNT_BUTTON)));
313   }
314   delegate_->ClearErrors();
315   languages_menubutton_->SetText(
316       UTF16ToWide(language_switch_menu_.GetCurrentLocaleName()));
317 }
318 
OnLocaleChanged()319 void NewUserView::OnLocaleChanged() {
320   RecreatePeculiarControls();
321   UpdateLocalizedStringsAndFonts();
322   AddChildView(sign_in_button_);
323   AddChildView(languages_menubutton_);
324 
325   Layout();
326   SchedulePaint();
327   RequestFocus();
328 }
329 
RequestFocus()330 void NewUserView::RequestFocus() {
331   if (username_field_->text().empty())
332     username_field_->RequestFocus();
333   else
334     password_field_->RequestFocus();
335 }
336 
ViewHierarchyChanged(bool is_add,View * parent,View * child)337 void NewUserView::ViewHierarchyChanged(bool is_add,
338                                        View *parent,
339                                        View *child) {
340   if (is_add && (child == username_field_ || child == password_field_)) {
341     MessageLoop::current()->PostTask(FROM_HERE,
342         focus_grabber_factory_.NewRunnableMethod(
343             &NewUserView::Layout));
344   }
345 }
346 
347 // Sets the bounds of the view, using x and y as the origin.
348 // The width is determined by the min of width and the preferred size
349 // of the view, unless force_width is true in which case it is always used.
350 // The height is gotten from the preferred size and returned.
setViewBounds(views::View * view,int x,int y,int width,bool force_width)351 static int setViewBounds(
352     views::View* view, int x, int y, int width, bool force_width) {
353   gfx::Size pref_size = view->GetPreferredSize();
354   if (!force_width) {
355     if (pref_size.width() < width) {
356       width = pref_size.width();
357     }
358   }
359   int height = pref_size.height();
360   view->SetBounds(x, y, width, height);
361   return height;
362 }
363 
Layout()364 void NewUserView::Layout() {
365   gfx::Insets insets = GetInsets();
366 
367   // Place language selection in top right corner.
368   int x = std::max(0,
369       this->width() - insets.right() -
370           languages_menubutton_->GetPreferredSize().width() - kColumnPad);
371   int y = insets.top() + kLanguagesMenuPad;
372   int width = std::min(this->width() - insets.width() - 2 * kColumnPad,
373                        languages_menubutton_->GetPreferredSize().width());
374   int height = kLanguagesMenuHeight;
375   languages_menubutton_->SetBounds(x, y, width, height);
376   y += height + kTitlePad;
377 
378   width = std::min(this->width() - insets.width() - 2 * kColumnPad,
379                    kTextfieldWidth);
380   x = insets.left() + kLeftPad;
381   int max_width = this->width() - x - std::max(insets.right(), x);
382   title_label_->SizeToFit(max_width);
383   title_hint_label_->SizeToFit(max_width);
384 
385   // Top align title and title hint.
386   y += setViewBounds(title_label_, x, y, max_width, false);
387   y += setViewBounds(title_hint_label_, x, y, max_width, false);
388   int title_end = y + kTitlePad;
389 
390   splitter_up1_->SetBounds(0, title_end, this->width(), kSplitterHeight);
391   splitter_up2_->SetBounds(0, title_end + 1, this->width(), kSplitterHeight);
392 
393   // Bottom controls.
394   int links_height = 0;
395   if (need_create_account_)
396     links_height += create_account_link_->GetPreferredSize().height();
397   if (need_guest_link_)
398     links_height += guest_link_->GetPreferredSize().height();
399   if (need_create_account_ && need_guest_link_)
400     links_height += kRowPad;
401   y = this->height() - insets.bottom() - kBottomPad;
402   if (links_height)
403     y -= links_height + kBottomPad;
404   int bottom_start = y;
405 
406   splitter_down1_->SetBounds(0, y, this->width(), kSplitterHeight);
407   splitter_down2_->SetBounds(0, y + 1, this->width(), kSplitterHeight);
408 
409   y += kBottomPad;
410   if (need_guest_link_) {
411     y += setViewBounds(guest_link_,
412                        x, y, max_width, false) + kRowPad;
413   }
414   if (need_create_account_) {
415     y += setViewBounds(create_account_link_, x, y, max_width, false);
416   }
417 
418   // Center main controls.
419   height = username_field_->GetPreferredSize().height() +
420            password_field_->GetPreferredSize().height() +
421            sign_in_button_->GetPreferredSize().height() +
422            2 * kRowPad;
423   y = title_end + (bottom_start - title_end - height) / 2;
424 
425   y += setViewBounds(username_field_, x, y, width, true) + kRowPad;
426   y += setViewBounds(password_field_, x, y, width, true) + kRowPad;
427 
428   int sign_in_button_width = sign_in_button_->GetPreferredSize().width();
429   setViewBounds(sign_in_button_, x, y, sign_in_button_width,true);
430 
431   SchedulePaint();
432 }
433 
GetPreferredSize()434 gfx::Size NewUserView::GetPreferredSize() {
435   return need_guest_link_ ?
436       gfx::Size(kNewUserPodFullWidth, kNewUserPodFullHeight) :
437       gfx::Size(kNewUserPodSmallWidth, kNewUserPodSmallHeight);
438 }
439 
SetUsername(const std::string & username)440 void NewUserView::SetUsername(const std::string& username) {
441   username_field_->SetText(UTF8ToUTF16(username));
442 }
443 
SetPassword(const std::string & password)444 void NewUserView::SetPassword(const std::string& password) {
445   password_field_->SetText(UTF8ToUTF16(password));
446 }
447 
Login()448 void NewUserView::Login() {
449   if (login_in_process_ ||
450       username_field_->text().empty() ||
451       password_field_->text().empty()) {
452     UpdateSignInButtonState();
453     return;
454   }
455 
456   login_in_process_ = true;
457   std::string username = UTF16ToUTF8(username_field_->text());
458   // todo(cmasone) Need to sanitize memory used to store password.
459   std::string password = UTF16ToUTF8(password_field_->text());
460 
461   if (username.find('@') == std::string::npos) {
462     username += kDefaultDomain;
463     username_field_->SetText(UTF8ToUTF16(username));
464   }
465 
466   delegate_->OnLogin(username, password);
467 }
468 
469 // Sign in button causes a login attempt.
ButtonPressed(views::Button * sender,const views::Event & event)470 void NewUserView::ButtonPressed(views::Button* sender,
471                                 const views::Event& event) {
472   DCHECK(sender == sign_in_button_);
473   Login();
474 }
475 
LinkActivated(views::Link * source,int event_flags)476 void NewUserView::LinkActivated(views::Link* source, int event_flags) {
477   if (source == create_account_link_) {
478     delegate_->OnCreateAccount();
479   } else if (source == guest_link_) {
480     delegate_->OnLoginAsGuest();
481   }
482 }
483 
ClearAndFocusControls()484 void NewUserView::ClearAndFocusControls() {
485   login_in_process_ = false;
486   SetUsername(std::string());
487   SetPassword(std::string());
488   username_field_->RequestFocus();
489   UpdateSignInButtonState();
490 }
491 
ClearAndFocusPassword()492 void NewUserView::ClearAndFocusPassword() {
493   login_in_process_ = false;
494   SetPassword(std::string());
495   password_field_->RequestFocus();
496   UpdateSignInButtonState();
497 }
498 
GetMainInputScreenBounds() const499 gfx::Rect NewUserView::GetMainInputScreenBounds() const {
500   return GetUsernameBounds();
501 }
502 
CalculateThrobberBounds(views::Throbber * throbber)503 gfx::Rect NewUserView::CalculateThrobberBounds(views::Throbber* throbber) {
504   DCHECK(password_field_);
505   DCHECK(sign_in_button_);
506 
507   gfx::Size throbber_size = throbber->GetPreferredSize();
508   int x = password_field_->x();
509   x += password_field_->width() - throbber_size.width();
510   int y = sign_in_button_->y();
511   y += (sign_in_button_->height() - throbber_size.height()) / 2;
512 
513   return gfx::Rect(gfx::Point(x, y), throbber_size);
514 }
515 
GetPasswordBounds() const516 gfx::Rect NewUserView::GetPasswordBounds() const {
517   return password_field_->GetScreenBounds();
518 }
519 
GetUsernameBounds() const520 gfx::Rect NewUserView::GetUsernameBounds() const {
521   return username_field_->GetScreenBounds();
522 }
523 
HandleKeyEvent(views::Textfield * sender,const views::KeyEvent & key_event)524 bool NewUserView::HandleKeyEvent(views::Textfield* sender,
525                                  const views::KeyEvent& key_event) {
526   if (!CrosLibrary::Get()->EnsureLoaded() || login_in_process_)
527     return false;
528 
529   if (key_event.key_code() == ui::VKEY_RETURN) {
530     if (!username_field_->text().empty() && !password_field_->text().empty())
531       Login();
532     // Return true so that processing ends
533     return true;
534   }
535   delegate_->ClearErrors();
536   // Return false so that processing does not end
537   return false;
538 }
539 
ContentsChanged(views::Textfield * sender,const string16 & new_contents)540 void NewUserView::ContentsChanged(views::Textfield* sender,
541                                   const string16& new_contents) {
542   UpdateSignInButtonState();
543   if (!new_contents.empty())
544     delegate_->ClearErrors();
545 }
546 
EnableInputControls(bool enabled)547 void NewUserView::EnableInputControls(bool enabled) {
548   languages_menubutton_->SetEnabled(enabled &&
549       !g_browser_process->local_state()->IsManagedPreference(
550           prefs::kApplicationLocale));
551   username_field_->SetEnabled(enabled);
552   password_field_->SetEnabled(enabled);
553   if (need_guest_link_) {
554     guest_link_->SetEnabled(enabled);
555   }
556   if (need_create_account_) {
557     create_account_link_->SetEnabled(enabled);
558   }
559   UpdateSignInButtonState();
560 }
561 
NavigateAway()562 bool NewUserView::NavigateAway() {
563   if (username_field_->text().empty() &&
564       password_field_->text().empty()) {
565     delegate_->NavigateAway();
566     return true;
567   } else {
568     return false;
569   }
570 }
571 
InitLink(views::Link ** link)572 void NewUserView::InitLink(views::Link** link) {
573   *link = new views::Link(std::wstring());
574   (*link)->SetController(this);
575   (*link)->SetNormalColor(login::kLinkColor);
576   (*link)->SetHighlightedColor(login::kLinkColor);
577   AddChildView(*link);
578 }
579 
580 }  // namespace chromeos
581