• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/ui/views/profile_chooser_view.h"
6 
7 #include "base/command_line.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile_info_util.h"
11 #include "chrome/browser/profiles/profile_manager.h"
12 #include "chrome/browser/profiles/profile_window.h"
13 #include "chrome/browser/profiles/profiles_state.h"
14 #include "chrome/browser/signin/profile_oauth2_token_service.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
16 #include "chrome/browser/signin/signin_manager.h"
17 #include "chrome/browser/signin/signin_manager_factory.h"
18 #include "chrome/browser/signin/signin_promo.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_dialogs.h"
21 #include "chrome/browser/ui/singleton_tabs.h"
22 #include "chrome/browser/ui/views/user_manager_view.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/url_constants.h"
25 #include "grit/chromium_strings.h"
26 #include "grit/generated_resources.h"
27 #include "grit/theme_resources.h"
28 #include "third_party/skia/include/core/SkColor.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/gfx/image/image.h"
32 #include "ui/gfx/image/image_skia.h"
33 #include "ui/gfx/text_elider.h"
34 #include "ui/views/controls/button/blue_button.h"
35 #include "ui/views/controls/button/menu_button.h"
36 #include "ui/views/controls/label.h"
37 #include "ui/views/controls/link.h"
38 #include "ui/views/controls/separator.h"
39 #include "ui/views/controls/textfield/textfield.h"
40 #include "ui/views/controls/webview/webview.h"
41 #include "ui/views/layout/grid_layout.h"
42 #include "ui/views/layout/layout_constants.h"
43 #include "ui/views/widget/widget.h"
44 
45 #if defined(USE_AURA)
46 #include "ui/native_theme/native_theme_aura.h"
47 #endif
48 
49 namespace {
50 
51 // Helpers --------------------------------------------------------------------
52 
53 const int kMinMenuWidth = 250;
54 const int kButtonHeight = 29;
55 
56 // Creates a GridLayout with a single column. This ensures that all the child
57 // views added get auto-expanded to fill the full width of the bubble.
CreateSingleColumnLayout(views::View * view)58 views::GridLayout* CreateSingleColumnLayout(views::View* view) {
59   views::GridLayout* layout = new views::GridLayout(view);
60   view->SetLayoutManager(layout);
61 
62   views::ColumnSet* columns = layout->AddColumnSet(0);
63   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
64                      views::GridLayout::USE_PREF, 0, 0);
65   return layout;
66 }
67 
68 // Creates a GridLayout with two columns.
CreateDoubleColumnLayout(views::View * view)69 views::GridLayout* CreateDoubleColumnLayout(views::View* view) {
70   views::GridLayout* layout = new views::GridLayout(view);
71   view->SetLayoutManager(layout);
72 
73   views::ColumnSet* columns = layout->AddColumnSet(0);
74   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
75                      views::GridLayout::USE_PREF, 0, 0);
76   columns->AddPaddingColumn(0, views::kUnrelatedControlLargeHorizontalSpacing);
77   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
78                      views::GridLayout::USE_PREF, 0, 0);
79   return layout;
80 }
81 
CreateLink(const base::string16 & link_text,views::LinkListener * listener)82 views::Link* CreateLink(const base::string16& link_text,
83                         views::LinkListener* listener) {
84   views::Link* link_button = new views::Link(link_text);
85   link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT);
86   link_button->SetUnderline(false);
87   link_button->set_listener(listener);
88   return link_button;
89 }
90 
91 
92 // BackgroundColorHoverButton -------------------------------------------------
93 
94 // A custom button that allows for setting a background color when hovered over.
95 class BackgroundColorHoverButton : public views::TextButton {
96  public:
97   BackgroundColorHoverButton(views::ButtonListener* listener,
98                              const base::string16& text,
99                              const gfx::ImageSkia& normal_icon,
100                              const gfx::ImageSkia& hover_icon);
101   virtual ~BackgroundColorHoverButton();
102 
103  private:
104   // views::TextButton:
105   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
106   virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
107 
108   void OnHighlightStateChanged();
109 
110   DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton);
111 };
112 
BackgroundColorHoverButton(views::ButtonListener * listener,const base::string16 & text,const gfx::ImageSkia & normal_icon,const gfx::ImageSkia & hover_icon)113 BackgroundColorHoverButton::BackgroundColorHoverButton(
114     views::ButtonListener* listener,
115     const base::string16& text,
116     const gfx::ImageSkia& normal_icon,
117     const gfx::ImageSkia& hover_icon)
118     : views::TextButton(listener, text) {
119   scoped_ptr<views::TextButtonBorder> text_button_border(
120       new views::TextButtonBorder());
121   text_button_border->SetInsets(gfx::Insets(0, views::kButtonHEdgeMarginNew,
122                                             0, views::kButtonHEdgeMarginNew));
123   set_border(text_button_border.release());
124   set_min_height(kButtonHeight);
125   set_icon_text_spacing(views::kItemLabelSpacing);
126   SetIcon(normal_icon);
127   SetHoverIcon(hover_icon);
128   SetPushedIcon(hover_icon);
129   SetHoverColor(GetNativeTheme()->GetSystemColor(
130       ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor));
131   OnHighlightStateChanged();
132 }
133 
~BackgroundColorHoverButton()134 BackgroundColorHoverButton::~BackgroundColorHoverButton() {
135 }
136 
OnMouseEntered(const ui::MouseEvent & event)137 void BackgroundColorHoverButton::OnMouseEntered(const ui::MouseEvent& event) {
138   views::TextButton::OnMouseEntered(event);
139   OnHighlightStateChanged();
140 }
141 
OnMouseExited(const ui::MouseEvent & event)142 void BackgroundColorHoverButton::OnMouseExited(const ui::MouseEvent& event) {
143   views::TextButton::OnMouseExited(event);
144   OnHighlightStateChanged();
145 }
146 
OnHighlightStateChanged()147 void BackgroundColorHoverButton::OnHighlightStateChanged() {
148   bool is_highlighted = (state() == views::TextButton::STATE_PRESSED) ||
149       (state() == views::TextButton::STATE_HOVERED) || HasFocus();
150   set_background(views::Background::CreateSolidBackground(
151       GetNativeTheme()->GetSystemColor(is_highlighted ?
152           ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor :
153           ui::NativeTheme::kColorId_MenuBackgroundColor)));
154   SchedulePaint();
155 }
156 
157 }  // namespace
158 
159 
160 // EditableProfilePhoto -------------------------------------------------
161 
162 // A custom Image control that shows a "change" button when moused over.
163 class EditableProfilePhoto : public views::ImageView {
164  public:
EditableProfilePhoto(views::ButtonListener * listener,const gfx::Image & icon,bool is_editing_allowed)165   EditableProfilePhoto(views::ButtonListener* listener,
166                        const gfx::Image& icon,
167                        bool is_editing_allowed)
168       : views::ImageView(),
169         change_photo_button_(NULL) {
170     const int kLargeImageSide = 64;
171     const SkColor kBackgroundColor = SkColorSetARGB(125, 0, 0, 0);
172     const int kOverlayHeight = 20;
173 
174     gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
175         icon, true,
176         kLargeImageSide + profiles::kAvatarIconPadding,
177         kLargeImageSide + profiles::kAvatarIconPadding);
178     SetImage(image.ToImageSkia());
179 
180     if (!is_editing_allowed)
181       return;
182 
183     set_notify_enter_exit_on_child(true);
184 
185     // Button overlay that appears when hovering over the image.
186     change_photo_button_ = new views::TextButton(listener,
187         l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_CHANGE_PHOTO_BUTTON));
188     change_photo_button_->set_alignment(views::TextButton::ALIGN_CENTER);
189     change_photo_button_->set_border(NULL);
190     change_photo_button_->SetEnabledColor(SK_ColorWHITE);
191     change_photo_button_->SetHoverColor(SK_ColorWHITE);
192 
193     change_photo_button_->set_background(
194         views::Background::CreateSolidBackground(kBackgroundColor));
195     // Need to take in account the border padding on the avatar.
196     change_photo_button_->SetBounds(
197         profiles::kAvatarIconPadding,
198         kLargeImageSide - kOverlayHeight,
199         kLargeImageSide - profiles::kAvatarIconPadding,
200         kOverlayHeight);
201     change_photo_button_->SetVisible(false);
202     AddChildView(change_photo_button_);
203   }
204 
change_photo_button()205   views::TextButton* change_photo_button() {
206     return change_photo_button_;
207   }
208 
209  private:
210   // views::View:
OnMouseEntered(const ui::MouseEvent & event)211   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
212     if (change_photo_button_)
213       change_photo_button_->SetVisible(true);
214   }
215 
OnMouseExited(const ui::MouseEvent & event)216   virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
217     if (change_photo_button_)
218       change_photo_button_->SetVisible(false);
219   }
220 
221   // Button that is shown when hovering over the image view. Can be NULL if
222   // the photo isn't allowed to be edited (e.g. for guest profiles).
223   views::TextButton* change_photo_button_;
224 
225   DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto);
226 };
227 
228 
229 // EditableProfileName -------------------------------------------------
230 
231 // A custom text control that turns into a textfield for editing when clicked.
232 class EditableProfileName : public views::TextButton,
233                             public views::ButtonListener {
234  public:
EditableProfileName(views::TextfieldController * controller,const base::string16 & text,bool is_editing_allowed)235   EditableProfileName(views::TextfieldController* controller,
236                       const base::string16& text,
237                       bool is_editing_allowed)
238       : views::TextButton(this, text),
239         profile_name_textfield_(NULL) {
240     ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
241     gfx::Font medium_font = rb->GetFont(ui::ResourceBundle::MediumFont);
242     SetFont(medium_font);
243     set_border(NULL);
244 
245     if (!is_editing_allowed)
246       return;
247 
248     SetIcon(*rb->GetImageSkiaNamed(IDR_INFOBAR_AUTOFILL));
249     set_icon_placement(views::TextButton::ICON_ON_RIGHT);
250 
251     // Textfield that overlaps the button.
252     profile_name_textfield_ = new views::Textfield();
253     profile_name_textfield_->SetController(controller);
254     profile_name_textfield_->SetFont(medium_font);
255     profile_name_textfield_->SetVisible(false);
256     AddChildView(profile_name_textfield_);
257   }
258 
profile_name_textfield()259   views::Textfield* profile_name_textfield() {
260     return profile_name_textfield_;
261   }
262 
263   // Hide the editable textfield and show the button displaying the profile
264   // name instead.
ShowReadOnlyView()265   void ShowReadOnlyView() {
266     if (profile_name_textfield_)
267       profile_name_textfield_->SetVisible(false);
268   }
269 
270  private:
271   // views::ButtonListener:
ButtonPressed(views::Button * sender,const ui::Event & event)272   virtual void ButtonPressed(views::Button* sender,
273                             const ui::Event& event) OVERRIDE {
274     if (profile_name_textfield_) {
275       profile_name_textfield_->SetVisible(true);
276       profile_name_textfield_->SetText(text());
277       profile_name_textfield_->SelectAll(false);
278       profile_name_textfield_->RequestFocus();
279     }
280   }
281 
282   // views::CustomButton:
OnKeyReleased(const ui::KeyEvent & event)283   virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE {
284     // Override CustomButton's implementation, which presses the button when
285     // you press space and clicks it when you release space, as the space can be
286     // part of the new profile name typed in the textfield.
287     return false;
288   }
289 
290   // views::View:
Layout()291   virtual void Layout() OVERRIDE {
292     if (profile_name_textfield_)
293       profile_name_textfield_->SetBounds(0, 0, width(), height());
294     views::View::Layout();
295   }
296 
297   // Button that is shown when hovering over the image view. Can be NULL if
298   // the profile name isn't allowed to be edited (e.g. for guest profiles).
299   views::Textfield* profile_name_textfield_;
300 
301   DISALLOW_COPY_AND_ASSIGN(EditableProfileName);
302 };
303 
304 
305 // ProfileChooserView ---------------------------------------------------------
306 
307 // static
308 ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL;
309 bool ProfileChooserView::close_on_deactivate_for_testing_ = true;
310 
311 // static
ShowBubble(views::View * anchor_view,views::BubbleBorder::Arrow arrow,views::BubbleBorder::BubbleAlignment border_alignment,const gfx::Rect & anchor_rect,Browser * browser)312 void ProfileChooserView::ShowBubble(
313     views::View* anchor_view,
314     views::BubbleBorder::Arrow arrow,
315     views::BubbleBorder::BubbleAlignment border_alignment,
316     const gfx::Rect& anchor_rect,
317     Browser* browser) {
318   if (IsShowing())
319     // TODO(bcwhite): handle case where we should show on different window
320     return;
321 
322   profile_bubble_ = new ProfileChooserView(
323       anchor_view, arrow, anchor_rect, browser);
324   views::BubbleDelegateView::CreateBubble(profile_bubble_);
325   profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_);
326   profile_bubble_->SetAlignment(border_alignment);
327   profile_bubble_->GetWidget()->Show();
328   profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
329 }
330 
331 // static
IsShowing()332 bool ProfileChooserView::IsShowing() {
333   return profile_bubble_ != NULL;
334 }
335 
336 // static
Hide()337 void ProfileChooserView::Hide() {
338   if (IsShowing())
339     profile_bubble_->GetWidget()->Close();
340 }
341 
ProfileChooserView(views::View * anchor_view,views::BubbleBorder::Arrow arrow,const gfx::Rect & anchor_rect,Browser * browser)342 ProfileChooserView::ProfileChooserView(views::View* anchor_view,
343                                        views::BubbleBorder::Arrow arrow,
344                                        const gfx::Rect& anchor_rect,
345                                        Browser* browser)
346     : BubbleDelegateView(anchor_view, arrow),
347       browser_(browser),
348       view_mode_(PROFILE_CHOOSER_VIEW) {
349   // Reset the default margins inherited from the BubbleDelegateView.
350   set_margins(gfx::Insets());
351 
352   ResetView();
353 
354   avatar_menu_.reset(new AvatarMenu(
355       &g_browser_process->profile_manager()->GetProfileInfoCache(),
356       this,
357       browser_));
358   avatar_menu_->RebuildMenu();
359 
360   ProfileOAuth2TokenService* oauth2_token_service =
361       ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
362   if (oauth2_token_service)
363     oauth2_token_service->AddObserver(this);
364 }
365 
~ProfileChooserView()366 ProfileChooserView::~ProfileChooserView() {
367   ProfileOAuth2TokenService* oauth2_token_service =
368       ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
369   if (oauth2_token_service)
370     oauth2_token_service->RemoveObserver(this);
371 }
372 
ResetView()373 void ProfileChooserView::ResetView() {
374   manage_accounts_link_ = NULL;
375   signout_current_profile_link_ = NULL;
376   signin_current_profile_link_ = NULL;
377   guest_button_ = NULL;
378   end_guest_button_ = NULL;
379   users_button_ = NULL;
380   add_user_button_ = NULL;
381   add_account_button_ = NULL;
382   current_profile_photo_ = NULL;
383   current_profile_name_ = NULL;
384   open_other_profile_indexes_map_.clear();
385   current_profile_accounts_map_.clear();
386 }
387 
Init()388 void ProfileChooserView::Init() {
389   ShowView(PROFILE_CHOOSER_VIEW, avatar_menu_.get());
390 }
391 
OnAvatarMenuChanged(AvatarMenu * avatar_menu)392 void ProfileChooserView::OnAvatarMenuChanged(
393     AvatarMenu* avatar_menu) {
394   // Refresh the view with the new menu. We can't just update the local copy
395   // as this may have been triggered by a sign out action, in which case
396   // the view is being destroyed.
397   ShowView(PROFILE_CHOOSER_VIEW, avatar_menu);
398 }
399 
OnRefreshTokenAvailable(const std::string & account_id)400 void ProfileChooserView::OnRefreshTokenAvailable(
401     const std::string& account_id) {
402   // Refresh the account management view when a new account is added to the
403   // profile.
404   if (view_mode_ == ACCOUNT_MANAGEMENT_VIEW ||
405       view_mode_ == GAIA_SIGNIN_VIEW ||
406       view_mode_ == GAIA_ADD_ACCOUNT_VIEW) {
407     ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
408   }
409 }
410 
OnRefreshTokenRevoked(const std::string & account_id)411 void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) {
412   // Refresh the account management view when an account is removed from the
413   // profile.
414   if (view_mode_ == ACCOUNT_MANAGEMENT_VIEW)
415     ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
416 }
417 
ShowView(BubbleViewMode view_to_display,AvatarMenu * avatar_menu)418 void ProfileChooserView::ShowView(BubbleViewMode view_to_display,
419                                   AvatarMenu* avatar_menu) {
420   // The account management view should only be displayed if the active profile
421   // is signed in.
422   if (view_to_display == ACCOUNT_MANAGEMENT_VIEW) {
423     const AvatarMenu::Item& active_item = avatar_menu->GetItemAt(
424         avatar_menu->GetActiveProfileIndex());
425     DCHECK(active_item.signed_in);
426   }
427 
428   ResetView();
429   RemoveAllChildViews(true);
430   view_mode_ = view_to_display;
431 
432   views::GridLayout* layout = CreateSingleColumnLayout(this);
433   layout->set_minimum_size(gfx::Size(kMinMenuWidth, 0));
434 
435   if (view_to_display == GAIA_SIGNIN_VIEW ||
436       view_to_display == GAIA_ADD_ACCOUNT_VIEW) {
437     // Minimum size for embedded sign in pages as defined in Gaia.
438     const int kMinGaiaViewWidth = 320;
439     const int kMinGaiaViewHeight = 440;
440     Profile* profile = browser_->profile();
441     views::WebView* web_view = new views::WebView(profile);
442     signin::Source source = (view_to_display == GAIA_SIGNIN_VIEW) ?
443         signin::SOURCE_AVATAR_BUBBLE_SIGN_IN :
444         signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT;
445     GURL url(signin::GetPromoURL(
446         source, false /* auto_close */, true /* is_constrained */));
447     web_view->LoadInitialURL(url);
448     layout->StartRow(1, 0);
449     layout->AddView(web_view);
450     layout->set_minimum_size(
451         gfx::Size(kMinGaiaViewWidth, kMinGaiaViewHeight));
452     Layout();
453     if (GetBubbleFrameView())
454       SizeToContents();
455     return;
456   }
457 
458   // Separate items into active and alternatives.
459   Indexes other_profiles;
460   bool is_guest_view = true;
461   views::View* current_profile_view = NULL;
462   views::View* current_profile_accounts = NULL;
463   for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) {
464     const AvatarMenu::Item& item = avatar_menu->GetItemAt(i);
465     if (item.active) {
466       if (view_to_display == PROFILE_CHOOSER_VIEW) {
467         current_profile_view = CreateCurrentProfileView(item, false);
468       } else {
469         current_profile_view = CreateCurrentProfileEditableView(item);
470         current_profile_accounts = CreateCurrentProfileAccountsView(item);
471       }
472       is_guest_view = false;
473     } else {
474       other_profiles.push_back(i);
475     }
476   }
477 
478   if (!current_profile_view)  // Guest windows don't have an active profile.
479     current_profile_view = CreateGuestProfileView();
480 
481   layout->StartRow(1, 0);
482   layout->AddView(current_profile_view);
483 
484   if (view_to_display == PROFILE_CHOOSER_VIEW) {
485     layout->StartRow(1, 0);
486     layout->AddView(CreateOtherProfilesView(other_profiles));
487   } else {
488     DCHECK(current_profile_accounts);
489     layout->StartRow(0, 0);
490     layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
491     layout->StartRow(1, 0);
492     layout->AddView(current_profile_accounts);
493   }
494 
495   layout->StartRow(0, 0);
496   layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
497 
498   // Action buttons.
499   views::View* option_buttons_view = CreateOptionsView(is_guest_view);
500   layout->StartRow(0, 0);
501   layout->AddView(option_buttons_view);
502 
503   Layout();
504   if (GetBubbleFrameView())
505     SizeToContents();
506 }
507 
WindowClosing()508 void ProfileChooserView::WindowClosing() {
509   DCHECK_EQ(profile_bubble_, this);
510   profile_bubble_ = NULL;
511 }
512 
ButtonPressed(views::Button * sender,const ui::Event & event)513 void ProfileChooserView::ButtonPressed(views::Button* sender,
514                                        const ui::Event& event) {
515   // Disable button after clicking so that it doesn't get clicked twice and
516   // start a second action... which can crash Chrome.  But don't disable if it
517   // has no parent (like in tests) because that will also crash.
518   if (sender->parent())
519     sender->SetEnabled(false);
520 
521   if (sender == guest_button_) {
522     profiles::SwitchToGuestProfile(browser_->host_desktop_type(),
523                                    profiles::ProfileSwitchingDoneCallback());
524   } else if (sender == end_guest_button_) {
525     profiles::CloseGuestProfileWindows();
526   } else if (sender == users_button_) {
527     // Only non-guest users appear in the User Manager.
528     base::FilePath profile_path;
529     if (!end_guest_button_) {
530       size_t active_index = avatar_menu_->GetActiveProfileIndex();
531       profile_path = avatar_menu_->GetItemAt(active_index).profile_path;
532     }
533     chrome::ShowUserManager(profile_path);
534   } else if (sender == add_user_button_) {
535     profiles::CreateAndSwitchToNewProfile(
536         browser_->host_desktop_type(),
537         profiles::ProfileSwitchingDoneCallback());
538   } else if (sender == add_account_button_) {
539     ShowView(GAIA_ADD_ACCOUNT_VIEW, avatar_menu_.get());
540   } else if (sender == current_profile_photo_->change_photo_button()) {
541     avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex());
542   } else {
543     // One of the "other profiles" buttons was pressed.
544     ButtonIndexes::const_iterator match =
545         open_other_profile_indexes_map_.find(sender);
546     DCHECK(match != open_other_profile_indexes_map_.end());
547     avatar_menu_->SwitchToProfile(
548         match->second,
549         ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW);
550   }
551 }
552 
OnMenuButtonClicked(views::View * source,const gfx::Point & point)553 void ProfileChooserView::OnMenuButtonClicked(views::View* source,
554                                              const gfx::Point& point) {
555   AccountButtonIndexes::const_iterator match =
556       current_profile_accounts_map_.find(source);
557   DCHECK(match != current_profile_accounts_map_.end());
558 
559   ProfileOAuth2TokenService* oauth2_token_service =
560       ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
561   if (oauth2_token_service)
562     oauth2_token_service->RevokeCredentials(match->second);
563 }
564 
LinkClicked(views::Link * sender,int event_flags)565 void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) {
566   if (sender == manage_accounts_link_) {
567     // ShowView() will DCHECK if this view is displayed for non signed-in users.
568     ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
569   } else if (sender == signout_current_profile_link_) {
570     profiles::LockProfile(browser_->profile());
571   } else {
572     DCHECK(sender == signin_current_profile_link_);
573     if (CommandLine::ForCurrentProcess()->HasSwitch(
574         switches::kEnableInlineSignin)) {
575       ShowView(GAIA_SIGNIN_VIEW, avatar_menu_.get());
576     } else {
577       GURL page = signin::GetPromoURL(signin::SOURCE_MENU, false);
578       chrome::ShowSingletonTab(browser_, page);
579     }
580   }
581 }
582 
HandleKeyEvent(views::Textfield * sender,const ui::KeyEvent & key_event)583 bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender,
584                                         const ui::KeyEvent& key_event) {
585   views::Textfield* name_textfield =
586       current_profile_name_->profile_name_textfield();
587   DCHECK(sender == name_textfield);
588 
589   if (key_event.key_code() == ui::VKEY_RETURN ||
590       key_event.key_code() == ui::VKEY_TAB) {
591     // Pressing Tab/Enter commits the new profile name, unless it's empty.
592     base::string16 new_profile_name = name_textfield->text();
593     if (new_profile_name.empty())
594       return true;
595 
596     const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt(
597         avatar_menu_->GetActiveProfileIndex());
598     Profile* profile = g_browser_process->profile_manager()->GetProfile(
599         active_item.profile_path);
600     DCHECK(profile);
601 
602     if (profile->IsManaged())
603       return true;
604 
605     profiles::UpdateProfileName(profile, new_profile_name);
606     current_profile_name_->ShowReadOnlyView();
607     return true;
608   }
609   return false;
610 }
611 
CreateCurrentProfileView(const AvatarMenu::Item & avatar_item,bool is_guest)612 views::View* ProfileChooserView::CreateCurrentProfileView(
613     const AvatarMenu::Item& avatar_item,
614     bool is_guest) {
615   views::View* view = new views::View();
616   views::GridLayout* layout = CreateDoubleColumnLayout(view);
617   layout->SetInsets(views::kButtonVEdgeMarginNew,
618                     views::kButtonHEdgeMarginNew,
619                     views::kButtonVEdgeMarginNew,
620                     views::kButtonHEdgeMarginNew);
621 
622   current_profile_photo_ =
623       new EditableProfilePhoto(this, avatar_item.icon, !is_guest);
624   view->SetBoundsRect(current_profile_photo_->bounds());
625   current_profile_name_ =
626       new EditableProfileName(this, avatar_item.name, !is_guest);
627 
628   layout->StartRow(1, 0);
629   layout->AddView(current_profile_photo_, 1, 3);
630   layout->AddView(current_profile_name_);
631 
632   if (is_guest) {
633     layout->StartRow(1, 0);
634     layout->SkipColumns(1);
635     layout->StartRow(1, 0);
636     layout->SkipColumns(1);
637   } else if (avatar_item.signed_in) {
638     manage_accounts_link_ = CreateLink(
639         l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON),
640         this);
641     signout_current_profile_link_ = CreateLink(
642         l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON), this);
643     layout->StartRow(1, 0);
644     layout->SkipColumns(1);
645     layout->AddView(signout_current_profile_link_);
646     layout->StartRow(1, 0);
647     layout->SkipColumns(1);
648     layout->AddView(manage_accounts_link_);
649   } else {
650     signin_current_profile_link_ = CreateLink(
651         l10n_util::GetStringFUTF16(
652             IDS_SYNC_START_SYNC_BUTTON_LABEL,
653             l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
654         this);
655     layout->StartRow(1, 0);
656     layout->SkipColumns(1);
657     layout->AddView(signin_current_profile_link_);
658     layout->StartRow(1, 0);
659     layout->SkipColumns(1);
660   }
661 
662   return view;
663 }
664 
CreateCurrentProfileEditableView(const AvatarMenu::Item & avatar_item)665 views::View* ProfileChooserView::CreateCurrentProfileEditableView(
666     const AvatarMenu::Item& avatar_item) {
667   DCHECK(avatar_item.signed_in);
668   views::View* view = new views::View();
669   views::GridLayout* layout = CreateDoubleColumnLayout(view);
670   layout->SetInsets(views::kButtonVEdgeMarginNew,
671                     views::kButtonHEdgeMarginNew,
672                     views::kButtonVEdgeMarginNew,
673                     views::kButtonHEdgeMarginNew);
674 
675   current_profile_photo_ =
676       new EditableProfilePhoto(this, avatar_item.icon, true);
677   view->SetBoundsRect(current_profile_photo_->bounds());
678   current_profile_name_ =
679       new EditableProfileName(this, avatar_item.name, true);
680 
681   layout->StartRow(1, 0);
682   layout->AddView(current_profile_photo_, 1, 3);
683   layout->AddView(current_profile_name_);
684 
685   layout->StartRow(1, 0);
686   layout->SkipColumns(1);
687 
688   layout->StartRow(1, 0);
689   layout->SkipColumns(1);
690   return view;
691 }
692 
CreateGuestProfileView()693 views::View* ProfileChooserView::CreateGuestProfileView() {
694   gfx::Image guest_icon =
695       ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_LOGIN_GUEST);
696   AvatarMenu::Item guest_avatar_item(0, 0, guest_icon);
697   guest_avatar_item.active = true;
698   guest_avatar_item.name = l10n_util::GetStringUTF16(
699       IDS_PROFILES_GUEST_PROFILE_NAME);
700   guest_avatar_item.signed_in = false;
701 
702   return CreateCurrentProfileView(guest_avatar_item, true);
703 }
704 
CreateOtherProfilesView(const Indexes & avatars_to_show)705 views::View* ProfileChooserView::CreateOtherProfilesView(
706     const Indexes& avatars_to_show) {
707   views::View* view = new views::View();
708   views::GridLayout* layout = CreateSingleColumnLayout(view);
709   layout->SetInsets(0, views::kButtonHEdgeMarginNew,
710                     views::kButtonVEdgeMarginNew, views::kButtonHEdgeMarginNew);
711   int num_avatars_to_show = avatars_to_show.size();
712   for (int i = 0; i < num_avatars_to_show; ++i) {
713     const size_t index = avatars_to_show[i];
714     const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index);
715     const int kSmallImageSide = 32;
716 
717     gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
718         item.icon, true,
719         kSmallImageSide + profiles::kAvatarIconPadding,
720         kSmallImageSide + profiles::kAvatarIconPadding);
721 
722     views::TextButton* button = new views::TextButton(this, item.name);
723     open_other_profile_indexes_map_[button] = index;
724     button->SetIcon(*image.ToImageSkia());
725     button->set_icon_text_spacing(views::kItemLabelSpacing);
726     button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont(
727         ui::ResourceBundle::MediumFont));
728     button->set_border(NULL);
729 
730     layout->StartRow(1, 0);
731     layout->AddView(button);
732 
733     // The last avatar in the list does not need any bottom padding.
734     if (i < num_avatars_to_show - 1)
735       layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
736   }
737 
738   return view;
739 }
740 
CreateOptionsView(bool is_guest_view)741 views::View* ProfileChooserView::CreateOptionsView(bool is_guest_view) {
742   views::View* view = new views::View();
743   views::GridLayout* layout = CreateSingleColumnLayout(view);
744   // The horizontal padding will be set by each button individually, so that
745   // in the hovered state the button spans the entire parent view.
746   layout->SetInsets(views::kRelatedControlVerticalSpacing, 0,
747                     views::kRelatedControlVerticalSpacing, 0);
748 
749   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
750 
751   layout->StartRow(1, 0);
752   if (is_guest_view) {
753     end_guest_button_ = new BackgroundColorHoverButton(
754         this,
755         l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST_BUTTON),
756         *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST),
757         *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE));
758     layout->AddView(end_guest_button_);
759   } else {
760     guest_button_ = new BackgroundColorHoverButton(
761         this,
762         l10n_util::GetStringUTF16(IDS_PROFILES_GUEST_BUTTON),
763         *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST),
764         *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE));
765     layout->AddView(guest_button_);
766   }
767 
768   add_user_button_ = new BackgroundColorHoverButton(
769       this,
770       l10n_util::GetStringUTF16(IDS_PROFILES_ADD_PERSON_BUTTON),
771       *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER),
772       *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE));
773   layout->StartRow(1, 0);
774   layout->AddView(add_user_button_);
775 
776   users_button_ = new BackgroundColorHoverButton(
777       this,
778       l10n_util::GetStringUTF16(IDS_PROFILES_ALL_PEOPLE_BUTTON),
779       *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER),
780       *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE));
781   layout->StartRow(1, 0);
782   layout->AddView(users_button_);
783 
784   return view;
785 }
786 
CreateCurrentProfileAccountsView(const AvatarMenu::Item & avatar_item)787 views::View* ProfileChooserView::CreateCurrentProfileAccountsView(
788     const AvatarMenu::Item& avatar_item) {
789   DCHECK(avatar_item.signed_in);
790   views::View* view = new views::View();
791   views::GridLayout* layout = CreateSingleColumnLayout(view);
792   layout->SetInsets(views::kButtonVEdgeMarginNew,
793                     views::kButtonHEdgeMarginNew,
794                     views::kButtonVEdgeMarginNew,
795                     views::kButtonHEdgeMarginNew);
796 
797   Profile* profile = browser_->profile();
798   std::string primary_account =
799       SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
800   DCHECK(!primary_account.empty());
801   std::vector<std::string> accounts(
802       ProfileOAuth2TokenServiceFactory::GetForProfile(profile)->GetAccounts());
803   DCHECK_EQ(1, std::count_if(accounts.begin(), accounts.end(),
804                              std::bind1st(std::equal_to<std::string>(),
805                                           primary_account)));
806 
807   // The primary account should always be listed first.  However, the vector
808   // returned by ProfileOAuth2TokenService::GetAccounts() will contain the
809   // primary account too.  Ignore it when it appears later.
810   // TODO(rogerta): we still need to further differentiate the primary account
811   // from the others, so more work is likely required here: crbug.com/311124.
812   CreateAccountButton(layout, primary_account, true);
813   for (size_t i = 0; i < accounts.size(); ++i) {
814     if (primary_account != accounts[i])
815       CreateAccountButton(layout, accounts[i], false);
816   }
817 
818   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
819 
820   add_account_button_ = new views::BlueButton(
821       this,
822       l10n_util::GetStringFUTF16(IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON,
823                                  avatar_item.name));
824   layout->StartRow(1, 0);
825   layout->AddView(add_account_button_);
826   return view;
827 }
828 
CreateAccountButton(views::GridLayout * layout,const std::string & account,bool is_primary_account)829 void ProfileChooserView::CreateAccountButton(views::GridLayout* layout,
830                                              const std::string& account,
831                                              bool is_primary_account) {
832   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
833   // Use a MenuButtonListener and not a regular ButtonListener to be
834   // able to distinguish between the unnamed "other profile" buttons and the
835   // unnamed "multiple accounts" buttons.
836   views::MenuButton* email_button = new views::MenuButton(
837       NULL,
838       gfx::ElideEmail(UTF8ToUTF16(account),
839                       rb->GetFontList(ui::ResourceBundle::BaseFont),
840                       width()),
841       is_primary_account ? NULL : this,  // Cannot delete the primary account.
842       !is_primary_account);
843   email_button->SetFont(rb->GetFont(ui::ResourceBundle::BaseFont));
844   email_button->set_border(views::Border::CreateEmptyBorder(0, 0, 0, 0));
845   if (!is_primary_account) {
846     email_button->set_menu_marker(
847         rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia());
848     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
849   }
850   layout->StartRow(1, 0);
851   layout->AddView(email_button);
852 
853   // Save the original email address, as the button text could be elided.
854   current_profile_accounts_map_[email_button] = account;
855 }
856