Home | History | Annotate | Download | only in views
      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.
     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.
     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 
     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 
    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 
    134 BackgroundColorHoverButton::~BackgroundColorHoverButton() {
    135 }
    136 
    137 void BackgroundColorHoverButton::OnMouseEntered(const ui::MouseEvent& event) {
    138   views::TextButton::OnMouseEntered(event);
    139   OnHighlightStateChanged();
    140 }
    141 
    142 void BackgroundColorHoverButton::OnMouseExited(const ui::MouseEvent& event) {
    143   views::TextButton::OnMouseExited(event);
    144   OnHighlightStateChanged();
    145 }
    146 
    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:
    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 
    205   views::TextButton* change_photo_button() {
    206     return change_photo_button_;
    207   }
    208 
    209  private:
    210   // views::View:
    211   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
    212     if (change_photo_button_)
    213       change_photo_button_->SetVisible(true);
    214   }
    215 
    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:
    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 
    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.
    265   void ShowReadOnlyView() {
    266     if (profile_name_textfield_)
    267       profile_name_textfield_->SetVisible(false);
    268   }
    269 
    270  private:
    271   // views::ButtonListener:
    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:
    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:
    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
    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
    332 bool ProfileChooserView::IsShowing() {
    333   return profile_bubble_ != NULL;
    334 }
    335 
    336 // static
    337 void ProfileChooserView::Hide() {
    338   if (IsShowing())
    339     profile_bubble_->GetWidget()->Close();
    340 }
    341 
    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 
    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 
    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 
    388 void ProfileChooserView::Init() {
    389   ShowView(PROFILE_CHOOSER_VIEW, avatar_menu_.get());
    390 }
    391 
    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 
    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 
    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 
    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 
    508 void ProfileChooserView::WindowClosing() {
    509   DCHECK_EQ(profile_bubble_, this);
    510   profile_bubble_ = NULL;
    511 }
    512 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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