profile_chooser_view.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright 2014 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/profiles/profile_chooser_view.h"
6
7#include "base/prefs/pref_service.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/lifetime/application_lifetime.h"
11#include "chrome/browser/profiles/profile_avatar_icon_util.h"
12#include "chrome/browser/profiles/profile_info_cache.h"
13#include "chrome/browser/profiles/profile_manager.h"
14#include "chrome/browser/profiles/profile_metrics.h"
15#include "chrome/browser/profiles/profile_window.h"
16#include "chrome/browser/profiles/profiles_state.h"
17#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18#include "chrome/browser/signin/signin_header_helper.h"
19#include "chrome/browser/signin/signin_manager_factory.h"
20#include "chrome/browser/signin/signin_promo.h"
21#include "chrome/browser/ui/browser.h"
22#include "chrome/browser/ui/browser_commands.h"
23#include "chrome/browser/ui/browser_dialogs.h"
24#include "chrome/browser/ui/chrome_pages.h"
25#include "chrome/browser/ui/singleton_tabs.h"
26#include "chrome/browser/ui/views/profiles/user_manager_view.h"
27#include "chrome/common/pref_names.h"
28#include "chrome/common/url_constants.h"
29#include "components/signin/core/browser/mutable_profile_oauth2_token_service.h"
30#include "components/signin/core/browser/profile_oauth2_token_service.h"
31#include "components/signin/core/browser/signin_error_controller.h"
32#include "components/signin/core/browser/signin_manager.h"
33#include "components/signin/core/common/profile_management_switches.h"
34#include "grit/chromium_strings.h"
35#include "grit/generated_resources.h"
36#include "grit/theme_resources.h"
37#include "third_party/skia/include/core/SkColor.h"
38#include "ui/base/l10n/l10n_util.h"
39#include "ui/base/resource/resource_bundle.h"
40#include "ui/gfx/canvas.h"
41#include "ui/gfx/image/image.h"
42#include "ui/gfx/image/image_skia.h"
43#include "ui/gfx/path.h"
44#include "ui/gfx/skia_util.h"
45#include "ui/gfx/text_elider.h"
46#include "ui/native_theme/native_theme.h"
47#include "ui/views/controls/button/blue_button.h"
48#include "ui/views/controls/button/image_button.h"
49#include "ui/views/controls/button/label_button.h"
50#include "ui/views/controls/button/menu_button.h"
51#include "ui/views/controls/label.h"
52#include "ui/views/controls/link.h"
53#include "ui/views/controls/separator.h"
54#include "ui/views/controls/styled_label.h"
55#include "ui/views/controls/textfield/textfield.h"
56#include "ui/views/controls/webview/webview.h"
57#include "ui/views/layout/grid_layout.h"
58#include "ui/views/layout/layout_constants.h"
59#include "ui/views/widget/widget.h"
60
61namespace {
62
63// Helpers --------------------------------------------------------------------
64
65const int kFixedMenuWidth = 250;
66const int kButtonHeight = 29;
67const int kProfileAvatarTutorialShowMax = 1;
68const int kFixedGaiaViewHeight = 400;
69const int kFixedGaiaViewWidth = 360;
70const int kFixedAccountRemovalViewWidth = 280;
71const int kFixedEndPreviewViewWidth = 280;
72const int kLargeImageSide = 88;
73
74// Creates a GridLayout with a single column. This ensures that all the child
75// views added get auto-expanded to fill the full width of the bubble.
76views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) {
77  views::GridLayout* layout = new views::GridLayout(view);
78  view->SetLayoutManager(layout);
79
80  views::ColumnSet* columns = layout->AddColumnSet(0);
81  columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
82                     views::GridLayout::FIXED, width, width);
83  return layout;
84}
85
86views::Link* CreateLink(const base::string16& link_text,
87                        views::LinkListener* listener) {
88  views::Link* link_button = new views::Link(link_text);
89  link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT);
90  link_button->SetUnderline(false);
91  link_button->set_listener(listener);
92  return link_button;
93}
94
95gfx::ImageSkia CreateSquarePlaceholderImage(int size) {
96  SkBitmap bitmap;
97  bitmap.setConfig(SkBitmap::kA8_Config, size, size);
98  bitmap.allocPixels();
99  bitmap.eraseARGB(0, 0, 0, 0);
100  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
101}
102
103bool HasAuthError(Profile* profile) {
104  const SigninErrorController* error =
105      profiles::GetSigninErrorController(profile);
106  return error && error->HasError();
107}
108
109std::string GetAuthErrorAccountId(Profile* profile) {
110  const SigninErrorController* error =
111      profiles::GetSigninErrorController(profile);
112  if (!error)
113    return std::string();
114
115  return error->error_account_id();
116}
117
118std::string GetAuthErrorUsername(Profile* profile) {
119  const SigninErrorController* error =
120      profiles::GetSigninErrorController(profile);
121  if (!error)
122    return std::string();
123
124  return error->error_username();
125}
126
127// BackgroundColorHoverButton -------------------------------------------------
128
129// A custom button that allows for setting a background color when hovered over.
130class BackgroundColorHoverButton : public views::LabelButton {
131 public:
132  BackgroundColorHoverButton(views::ButtonListener* listener,
133                             const base::string16& text,
134                             const gfx::ImageSkia& normal_icon,
135                             const gfx::ImageSkia& hover_icon);
136  virtual ~BackgroundColorHoverButton();
137
138 private:
139  // views::LabelButton:
140  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
141
142  DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton);
143};
144
145BackgroundColorHoverButton::BackgroundColorHoverButton(
146    views::ButtonListener* listener,
147    const base::string16& text,
148    const gfx::ImageSkia& normal_icon,
149    const gfx::ImageSkia& hover_icon)
150    : views::LabelButton(listener, text) {
151  SetBorder(views::Border::CreateEmptyBorder(0, views::kButtonHEdgeMarginNew,
152                                             0, views::kButtonHEdgeMarginNew));
153  set_min_size(gfx::Size(0, kButtonHeight));
154  SetImage(STATE_NORMAL, normal_icon);
155  SetImage(STATE_HOVERED, hover_icon);
156  SetImage(STATE_PRESSED, hover_icon);
157}
158
159BackgroundColorHoverButton::~BackgroundColorHoverButton() {}
160
161void BackgroundColorHoverButton::OnPaint(gfx::Canvas* canvas) {
162  if ((state() == STATE_PRESSED) || (state() == STATE_HOVERED) || HasFocus()) {
163    canvas->DrawColor(GetNativeTheme()->GetSystemColor(
164        ui::NativeTheme::kColorId_ButtonHoverBackgroundColor));
165  }
166  LabelButton::OnPaint(canvas);
167}
168
169}  // namespace
170
171
172// EditableProfilePhoto -------------------------------------------------
173
174// A custom Image control that shows a "change" button when moused over.
175class EditableProfilePhoto : public views::ImageView {
176 public:
177  EditableProfilePhoto(views::ButtonListener* listener,
178                       const gfx::Image& icon,
179                       bool is_editing_allowed,
180                       const gfx::Rect& bounds)
181      : views::ImageView(),
182        change_photo_button_(NULL) {
183    gfx::Image image = profiles::GetSizedAvatarIcon(
184        icon, true, kLargeImageSide, kLargeImageSide);
185    SetImage(image.ToImageSkia());
186    SetBoundsRect(bounds);
187
188    // Calculate the circular mask that will be used to display the photo.
189    gfx::Point center = bounds.CenterPoint();
190    circular_mask_.addCircle(SkIntToScalar(center.x()),
191                             SkIntToScalar(center.y()),
192                             SkIntToScalar(bounds.width() / 2));
193
194    if (!is_editing_allowed)
195      return;
196
197    set_notify_enter_exit_on_child(true);
198
199    // Button overlay that appears when hovering over the image.
200    change_photo_button_ = new views::LabelButton(listener, base::string16());
201    change_photo_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
202    change_photo_button_->SetBorder(views::Border::NullBorder());
203
204    const SkColor kBackgroundColor = SkColorSetARGB(65, 255, 255, 255);
205    change_photo_button_->set_background(
206        views::Background::CreateSolidBackground(kBackgroundColor));
207    change_photo_button_->SetImage(views::LabelButton::STATE_NORMAL,
208        *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
209            IDR_ICON_PROFILES_EDIT_CAMERA));
210
211    change_photo_button_->SetBoundsRect(bounds);
212    change_photo_button_->SetVisible(false);
213    AddChildView(change_photo_button_);
214  }
215
216  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
217    // Display the profile picture as a circle.
218    canvas->ClipPath(circular_mask_, true);
219    views::ImageView::OnPaint(canvas);
220  }
221
222  virtual void PaintChildren(gfx::Canvas* canvas,
223                     const views::CullSet& cull_set) OVERRIDE {
224    // Display any children (the "change photo" overlay) as a circle.
225    canvas->ClipPath(circular_mask_, true);
226    View::PaintChildren(canvas, cull_set);
227  }
228
229  views::LabelButton* change_photo_button() { return change_photo_button_; }
230
231 private:
232  // views::View:
233  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
234    if (change_photo_button_)
235      change_photo_button_->SetVisible(true);
236  }
237
238  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
239    if (change_photo_button_)
240      change_photo_button_->SetVisible(false);
241  }
242
243  gfx::Path circular_mask_;
244
245  // Button that is shown when hovering over the image view. Can be NULL if
246  // the photo isn't allowed to be edited (e.g. for guest profiles).
247  views::LabelButton* change_photo_button_;
248
249  DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto);
250};
251
252
253// EditableProfileName -------------------------------------------------
254
255// A custom text control that turns into a textfield for editing when clicked.
256class EditableProfileName : public views::LabelButton,
257                            public views::ButtonListener {
258 public:
259  EditableProfileName(views::TextfieldController* controller,
260                      const base::string16& text,
261                      bool is_editing_allowed)
262      : views::LabelButton(this, text),
263        profile_name_textfield_(NULL) {
264    ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
265    const gfx::FontList& medium_font_list =
266        rb->GetFontList(ui::ResourceBundle::MediumFont);
267    SetFontList(medium_font_list);
268    SetHorizontalAlignment(gfx::ALIGN_CENTER);
269
270    if (!is_editing_allowed) {
271      SetBorder(views::Border::CreateEmptyBorder(2, 0, 2, 0));
272      return;
273    }
274
275    // Show an "edit" pencil icon when hovering over. In the default state,
276    // we need to create an empty placeholder of the correct size, so that
277    // the text doesn't jump around when the hovered icon appears.
278    gfx::ImageSkia hover_image =
279        *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER);
280    SetImage(STATE_NORMAL, CreateSquarePlaceholderImage(hover_image.width()));
281    SetImage(STATE_HOVERED, hover_image);
282    SetImage(STATE_PRESSED,
283             *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED));
284    // To center the text, we need to offest it by the width of the icon we
285    // are adding. We need to also add a small top/bottom padding to account
286    // for the textfield's border.
287    SetBorder(views::Border::CreateEmptyBorder(2, hover_image.width(), 2, 0));
288
289    // Textfield that overlaps the button.
290    profile_name_textfield_ = new views::Textfield();
291    profile_name_textfield_->set_controller(controller);
292    profile_name_textfield_->SetFontList(medium_font_list);
293    profile_name_textfield_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
294
295    profile_name_textfield_->SetVisible(false);
296    AddChildView(profile_name_textfield_);
297  }
298
299  views::Textfield* profile_name_textfield() {
300    return profile_name_textfield_;
301  }
302
303  // Hide the editable textfield to show the profile name button instead.
304  void ShowReadOnlyView() {
305    if (profile_name_textfield_)
306      profile_name_textfield_->SetVisible(false);
307  }
308
309 private:
310  // views::ButtonListener:
311  virtual void ButtonPressed(views::Button* sender,
312                            const ui::Event& event) OVERRIDE {
313    if (profile_name_textfield_) {
314      profile_name_textfield_->SetVisible(true);
315      profile_name_textfield_->SetText(GetText());
316      profile_name_textfield_->SelectAll(false);
317      profile_name_textfield_->RequestFocus();
318    }
319  }
320
321  // views::LabelButton:
322  virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE {
323    // Override CustomButton's implementation, which presses the button when
324    // you press space and clicks it when you release space, as the space can be
325    // part of the new profile name typed in the textfield.
326    return false;
327  }
328
329  virtual void Layout() OVERRIDE {
330    if (profile_name_textfield_)
331      profile_name_textfield_->SetBounds(0, 0, width(), height());
332    // This layout trick keeps the text left-aligned and the icon right-aligned.
333    SetHorizontalAlignment(gfx::ALIGN_RIGHT);
334    views::LabelButton::Layout();
335    label()->SetHorizontalAlignment(gfx::ALIGN_CENTER);
336  }
337
338  // Textfield that is shown when editing the profile name. Can be NULL if
339  // the profile name isn't allowed to be edited (e.g. for guest profiles).
340  views::Textfield* profile_name_textfield_;
341
342  DISALLOW_COPY_AND_ASSIGN(EditableProfileName);
343};
344
345// A title card with one back button right aligned and one label center aligned.
346class TitleCard : public views::View {
347 public:
348  TitleCard(int message_id, views::ButtonListener* listener,
349             views::ImageButton** back_button) {
350    back_button_ = new views::ImageButton(listener);
351    back_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
352                                    views::ImageButton::ALIGN_MIDDLE);
353    ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
354    back_button_->SetImage(views::ImageButton::STATE_NORMAL,
355                           rb->GetImageSkiaNamed(IDR_BACK));
356    back_button_->SetImage(views::ImageButton::STATE_HOVERED,
357                           rb->GetImageSkiaNamed(IDR_BACK_H));
358    back_button_->SetImage(views::ImageButton::STATE_PRESSED,
359                           rb->GetImageSkiaNamed(IDR_BACK_P));
360    back_button_->SetImage(views::ImageButton::STATE_DISABLED,
361                           rb->GetImageSkiaNamed(IDR_BACK_D));
362    *back_button = back_button_;
363
364    title_label_ = new views::Label(l10n_util::GetStringUTF16(message_id));
365    title_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
366    const gfx::FontList& medium_font_list =
367        rb->GetFontList(ui::ResourceBundle::MediumFont);
368    title_label_->SetFontList(medium_font_list);
369
370    AddChildView(back_button_);
371    AddChildView(title_label_);
372  }
373
374  // Creates a new view that has the |title_card| with padding at the top, an
375  // edge-to-edge separator below, and the specified |view| at the bottom.
376  static views::View* AddPaddedTitleCard(views::View* view,
377                                         TitleCard* title_card,
378                                         int width) {
379    views::View* titled_view = new views::View();
380    views::GridLayout* layout = new views::GridLayout(titled_view);
381    titled_view->SetLayoutManager(layout);
382
383    // Column set 0 is a single column layout with horizontal padding at left
384    // and right, and column set 1 is a single column layout with no padding.
385    views::ColumnSet* columns = layout->AddColumnSet(0);
386    columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew);
387    int available_width = width - 2 * views::kButtonHEdgeMarginNew;
388    columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
389        views::GridLayout::FIXED, available_width, available_width);
390    columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew);
391    layout->AddColumnSet(1)->AddColumn(views::GridLayout::FILL,
392        views::GridLayout::FILL, 0,views::GridLayout::FIXED, width, width);
393
394    layout->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew);
395    layout->AddView(title_card);
396    layout->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing);
397    layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
398
399    layout->StartRow(1, 1);
400    layout->AddView(view);
401
402    return titled_view;
403  }
404
405 private:
406  virtual void Layout() OVERRIDE{
407    back_button_->SetBounds(
408        0, 0, back_button_->GetPreferredSize().width(), height());
409    title_label_->SetBoundsRect(GetContentsBounds());
410  }
411
412  virtual gfx::Size GetPreferredSize() const OVERRIDE{
413    int height = std::max(title_label_->GetPreferredSize().height(),
414        back_button_->GetPreferredSize().height());
415    return gfx::Size(width(), height);
416  }
417
418  views::ImageButton* back_button_;
419  views::Label* title_label_;
420
421  DISALLOW_COPY_AND_ASSIGN(TitleCard);
422};
423
424// ProfileChooserView ---------------------------------------------------------
425
426// static
427ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL;
428bool ProfileChooserView::close_on_deactivate_for_testing_ = true;
429
430// static
431void ProfileChooserView::ShowBubble(
432    profiles::BubbleViewMode view_mode,
433    signin::GAIAServiceType service_type,
434    views::View* anchor_view,
435    views::BubbleBorder::Arrow arrow,
436    views::BubbleBorder::BubbleAlignment border_alignment,
437    const gfx::Rect& anchor_rect,
438    Browser* browser) {
439  if (IsShowing())
440    return;
441
442  profile_bubble_ = new ProfileChooserView(anchor_view, arrow, anchor_rect,
443                                           browser, view_mode, service_type);
444  views::BubbleDelegateView::CreateBubble(profile_bubble_);
445  profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_);
446  profile_bubble_->SetAlignment(border_alignment);
447  profile_bubble_->GetWidget()->Show();
448  profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
449}
450
451// static
452bool ProfileChooserView::IsShowing() {
453  return profile_bubble_ != NULL;
454}
455
456// static
457void ProfileChooserView::Hide() {
458  if (IsShowing())
459    profile_bubble_->GetWidget()->Close();
460}
461
462ProfileChooserView::ProfileChooserView(views::View* anchor_view,
463                                       views::BubbleBorder::Arrow arrow,
464                                       const gfx::Rect& anchor_rect,
465                                       Browser* browser,
466                                       profiles::BubbleViewMode view_mode,
467                                       signin::GAIAServiceType service_type)
468    : BubbleDelegateView(anchor_view, arrow),
469      browser_(browser),
470      view_mode_(view_mode),
471      tutorial_mode_(profiles::TUTORIAL_MODE_NONE),
472      gaia_service_type_(service_type) {
473  // Reset the default margins inherited from the BubbleDelegateView.
474  set_margins(gfx::Insets());
475
476  ResetView();
477
478  avatar_menu_.reset(new AvatarMenu(
479      &g_browser_process->profile_manager()->GetProfileInfoCache(),
480      this,
481      browser_));
482  avatar_menu_->RebuildMenu();
483
484  ProfileOAuth2TokenService* oauth2_token_service =
485      ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
486  if (oauth2_token_service)
487    oauth2_token_service->AddObserver(this);
488}
489
490ProfileChooserView::~ProfileChooserView() {
491  ProfileOAuth2TokenService* oauth2_token_service =
492      ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
493  if (oauth2_token_service)
494    oauth2_token_service->RemoveObserver(this);
495}
496
497void ProfileChooserView::ResetView() {
498  question_mark_button_ = NULL;
499  manage_accounts_link_ = NULL;
500  signin_current_profile_link_ = NULL;
501  users_button_ = NULL;
502  lock_button_ = NULL;
503  add_account_link_ = NULL;
504  current_profile_photo_ = NULL;
505  current_profile_name_ = NULL;
506  tutorial_ok_button_ = NULL;
507  tutorial_learn_more_link_ = NULL;
508  tutorial_enable_new_profile_management_button_ = NULL;
509  tutorial_end_preview_link_ = NULL;
510  tutorial_send_feedback_button_ = NULL;
511  end_preview_and_relaunch_button_ = NULL;
512  end_preview_cancel_button_ = NULL;
513  remove_account_button_ = NULL;
514  account_removal_cancel_button_ = NULL;
515  gaia_signin_cancel_button_ = NULL;
516  open_other_profile_indexes_map_.clear();
517  delete_account_button_map_.clear();
518  reauth_account_button_map_.clear();
519  tutorial_mode_ = profiles::TUTORIAL_MODE_NONE;
520}
521
522void ProfileChooserView::Init() {
523  // If view mode is PROFILE_CHOOSER but there is an auth error, force
524  // ACCOUNT_MANAGEMENT mode.
525  if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER &&
526      HasAuthError(browser_->profile())) {
527    view_mode_ = profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT;
528  }
529
530  ShowView(view_mode_, avatar_menu_.get());
531}
532
533void ProfileChooserView::OnAvatarMenuChanged(
534    AvatarMenu* avatar_menu) {
535  // Refresh the view with the new menu. We can't just update the local copy
536  // as this may have been triggered by a sign out action, in which case
537  // the view is being destroyed.
538  ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu);
539}
540
541void ProfileChooserView::OnRefreshTokenAvailable(
542    const std::string& account_id) {
543  // Refresh the account management view when a new account is added to the
544  // profile.
545  if (view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ||
546      view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN ||
547      view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT ||
548      view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) {
549    ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
550  }
551}
552
553void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) {
554  // Refresh the account management view when an account is removed from the
555  // profile.
556  if (view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT)
557    ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
558}
559
560void ProfileChooserView::ShowView(profiles::BubbleViewMode view_to_display,
561                                  AvatarMenu* avatar_menu) {
562  // The account management view should only be displayed if the active profile
563  // is signed in.
564  if (view_to_display == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) {
565    const AvatarMenu::Item& active_item = avatar_menu->GetItemAt(
566        avatar_menu->GetActiveProfileIndex());
567    DCHECK(active_item.signed_in);
568  }
569
570  // Records the last tutorial mode.
571  profiles::TutorialMode last_tutorial_mode = tutorial_mode_;
572  ResetView();
573  RemoveAllChildViews(true);
574  view_mode_ = view_to_display;
575
576  views::GridLayout* layout;
577  views::View* sub_view;
578  switch (view_mode_) {
579    case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
580    case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
581    case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH:
582      layout = CreateSingleColumnLayout(this, kFixedGaiaViewWidth);
583      sub_view = CreateGaiaSigninView();
584      break;
585    case profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL:
586      layout = CreateSingleColumnLayout(this, kFixedAccountRemovalViewWidth);
587      sub_view = CreateAccountRemovalView();
588      break;
589    case profiles::BUBBLE_VIEW_MODE_END_PREVIEW:
590      layout = CreateSingleColumnLayout(this, kFixedEndPreviewViewWidth);
591      sub_view = CreateEndPreviewView();
592      break;
593    default:
594      layout = CreateSingleColumnLayout(this, kFixedMenuWidth);
595      sub_view = CreateProfileChooserView(avatar_menu, last_tutorial_mode);
596  }
597  sub_view->set_background(views::Background::CreateSolidBackground(
598      GetNativeTheme()->GetSystemColor(
599          ui::NativeTheme::kColorId_DialogBackground)));
600
601  layout->StartRow(1, 0);
602  layout->AddView(sub_view);
603  Layout();
604  if (GetBubbleFrameView())
605    SizeToContents();
606}
607
608void ProfileChooserView::WindowClosing() {
609  DCHECK_EQ(profile_bubble_, this);
610  profile_bubble_ = NULL;
611}
612
613void ProfileChooserView::ButtonPressed(views::Button* sender,
614                                       const ui::Event& event) {
615  // Disable button after clicking so that it doesn't get clicked twice and
616  // start a second action... which can crash Chrome.  But don't disable if it
617  // has no parent (like in tests) because that will also crash.
618  if (sender->parent())
619    sender->SetEnabled(false);
620
621  if (sender == users_button_) {
622    profiles::ShowUserManagerMaybeWithTutorial(browser_->profile());
623    // If this is a guest session, also close all the guest browser windows.
624    if (browser_->profile()->IsGuestSession())
625      profiles::CloseGuestProfileWindows();
626  } else if (sender == lock_button_) {
627    profiles::LockProfile(browser_->profile());
628    PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_LOCK);
629  } else if (sender == tutorial_ok_button_) {
630    // If the user manually dismissed the tutorial, never show it again by
631    // setting the number of times shown to the maximum plus 1, so that later we
632    // could distinguish between the dismiss case and the case when the tutorial
633    // is indeed shown for the maximum number of times.
634    browser_->profile()->GetPrefs()->SetInteger(
635        prefs::kProfileAvatarTutorialShown, kProfileAvatarTutorialShowMax + 1);
636
637    ProfileMetrics::LogProfileUpgradeEnrollment(
638        ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD);
639    ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get());
640  } else if (sender == tutorial_enable_new_profile_management_button_) {
641    ProfileMetrics::LogProfileUpgradeEnrollment(
642        ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT);
643    profiles::EnableNewProfileManagementPreview();
644  } else if (sender == remove_account_button_) {
645    RemoveAccount();
646  } else if (sender == account_removal_cancel_button_) {
647    account_id_to_remove_.clear();
648    ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
649  } else if (sender == gaia_signin_cancel_button_) {
650    std::string primary_account =
651        SigninManagerFactory::GetForProfile(browser_->profile())->
652        GetAuthenticatedUsername();
653    ShowView(primary_account.empty() ?
654                 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER :
655                 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT,
656             avatar_menu_.get());
657  } else if (sender == question_mark_button_) {
658    tutorial_mode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK;
659    ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get());
660  } else if (sender == tutorial_send_feedback_button_) {
661    ProfileMetrics::LogProfileUpgradeEnrollment(
662        ProfileMetrics::PROFILE_ENROLLMENT_SEND_FEEDBACK);
663    chrome::OpenFeedbackDialog(browser_);
664  } else if (sender == end_preview_and_relaunch_button_) {
665    ProfileMetrics::LogProfileUpgradeEnrollment(
666        ProfileMetrics::PROFILE_ENROLLMENT_DISABLE_NEW_PROFILE_MGMT);
667    profiles::DisableNewProfileManagementPreview();
668  } else if (sender == end_preview_cancel_button_) {
669    tutorial_mode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK;
670    ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get());
671  } else if (current_profile_photo_ &&
672             sender == current_profile_photo_->change_photo_button()) {
673    avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex());
674    PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE);
675  } else if (sender == signin_current_profile_link_) {
676    // Only show the inline signin if the new UI flag is flipped. Otherwise,
677    // use the tab signin page.
678    if (switches::IsNewProfileManagement())
679      ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, avatar_menu_.get());
680    else
681      chrome::ShowBrowserSignin(browser_, signin::SOURCE_MENU);
682  } else {
683    // Either one of the "other profiles", or one of the profile accounts
684    // buttons was pressed.
685    ButtonIndexes::const_iterator profile_match =
686        open_other_profile_indexes_map_.find(sender);
687    if (profile_match != open_other_profile_indexes_map_.end()) {
688      avatar_menu_->SwitchToProfile(
689          profile_match->second,
690          ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW,
691          ProfileMetrics::SWITCH_PROFILE_ICON);
692    } else {
693      // This was a profile accounts button.
694      AccountButtonIndexes::const_iterator account_match =
695          delete_account_button_map_.find(sender);
696      if (account_match != delete_account_button_map_.end()) {
697        account_id_to_remove_ = account_match->second;
698        ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL,
699            avatar_menu_.get());
700      } else {
701        account_match = reauth_account_button_map_.find(sender);
702        DCHECK(account_match != reauth_account_button_map_.end());
703        ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH, avatar_menu_.get());
704      }
705    }
706  }
707}
708
709void ProfileChooserView::RemoveAccount() {
710  DCHECK(!account_id_to_remove_.empty());
711  MutableProfileOAuth2TokenService* oauth2_token_service =
712      ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
713      browser_->profile());
714  if (oauth2_token_service) {
715    oauth2_token_service->RevokeCredentials(account_id_to_remove_);
716    PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_REMOVE_ACCT);
717  }
718  account_id_to_remove_.clear();
719
720  ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
721}
722
723void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) {
724  if (sender == manage_accounts_link_) {
725    // This link can either mean show/hide the account management view,
726    // depending on which view it is displayed. ShowView() will DCHECK if
727    // the account management view is displayed for non signed-in users.
728    ShowView(
729        view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ?
730            profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER :
731            profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT,
732        avatar_menu_.get());
733  } else if (sender == add_account_link_) {
734    ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT, avatar_menu_.get());
735    PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT);
736  } else if (sender == tutorial_learn_more_link_) {
737    ProfileMetrics::LogProfileUpgradeEnrollment(
738        ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE);
739    // TODO(guohui): update |learn_more_url| once it is decided.
740    const GURL lear_more_url("https://support.google.com/chrome/?hl=en#to");
741    chrome::NavigateParams params(
742        browser_->profile(),
743        lear_more_url,
744        content::PAGE_TRANSITION_LINK);
745    params.disposition = NEW_FOREGROUND_TAB;
746    chrome::Navigate(&params);
747  } else {
748    DCHECK(sender == tutorial_end_preview_link_);
749    ShowView(profiles::BUBBLE_VIEW_MODE_END_PREVIEW, avatar_menu_.get());
750  }
751}
752
753void ProfileChooserView::StyledLabelLinkClicked(
754    const gfx::Range& range, int event_flags) {
755  chrome::ShowSettings(browser_);
756}
757
758bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender,
759                                        const ui::KeyEvent& key_event) {
760  views::Textfield* name_textfield =
761      current_profile_name_->profile_name_textfield();
762  DCHECK(sender == name_textfield);
763
764  if (key_event.key_code() == ui::VKEY_RETURN ||
765      key_event.key_code() == ui::VKEY_TAB) {
766    // Pressing Tab/Enter commits the new profile name, unless it's empty.
767    base::string16 new_profile_name = name_textfield->text();
768    if (new_profile_name.empty())
769      return true;
770
771    const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt(
772        avatar_menu_->GetActiveProfileIndex());
773    Profile* profile = g_browser_process->profile_manager()->GetProfile(
774        active_item.profile_path);
775    DCHECK(profile);
776
777    if (profile->IsManaged())
778      return true;
779
780    profiles::UpdateProfileName(profile, new_profile_name);
781    PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME);
782    current_profile_name_->ShowReadOnlyView();
783    return true;
784  }
785  return false;
786}
787
788void ProfileChooserView::PostActionPerformed(
789    ProfileMetrics::ProfileDesktopMenu action_performed) {
790  ProfileMetrics::LogProfileDesktopMenu(action_performed, gaia_service_type_);
791  gaia_service_type_ = signin::GAIA_SERVICE_TYPE_NONE;
792}
793
794views::View* ProfileChooserView::CreateProfileChooserView(
795    AvatarMenu* avatar_menu,
796    profiles::TutorialMode last_tutorial_mode) {
797  // TODO(guohui, noms): the view should be customized based on whether new
798  // profile management preview is enabled or not.
799
800  views::View* view = new views::View();
801  views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
802  // Separate items into active and alternatives.
803  Indexes other_profiles;
804  views::View* tutorial_view = NULL;
805  views::View* current_profile_view = NULL;
806  views::View* current_profile_accounts = NULL;
807  views::View* option_buttons_view = NULL;
808  bool is_new_profile_management = switches::IsNewProfileManagement();
809  for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) {
810    const AvatarMenu::Item& item = avatar_menu->GetItemAt(i);
811    if (item.active) {
812      option_buttons_view = CreateOptionsView(item.signed_in);
813      current_profile_view = CreateCurrentProfileView(item, false);
814      if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
815        if (is_new_profile_management) {
816          tutorial_view =
817              last_tutorial_mode == profiles::TUTORIAL_MODE_SEND_FEEDBACK ?
818              CreateSendPreviewFeedbackView() :
819              CreatePreviewEnabledTutorialView(
820                  item, last_tutorial_mode == profiles::TUTORIAL_MODE_WELCOME);
821        } else {
822          tutorial_view = CreateNewProfileManagementPreviewView();
823        }
824      } else {
825        current_profile_accounts = CreateCurrentProfileAccountsView(item);
826      }
827    } else {
828      other_profiles.push_back(i);
829    }
830  }
831
832  if (tutorial_view) {
833    // Be sure not to track the tutorial display on View refresh, and only count
834    // the preview-promo view, shown when New Profile Management is off.
835    if (tutorial_mode_ != last_tutorial_mode && !is_new_profile_management) {
836      ProfileMetrics::LogProfileUpgradeEnrollment(
837          ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO);
838    }
839    layout->StartRow(1, 0);
840    layout->AddView(tutorial_view);
841  }
842
843  if (!current_profile_view) {
844    // Guest windows don't have an active profile.
845    current_profile_view = CreateGuestProfileView();
846    option_buttons_view = CreateOptionsView(false);
847  }
848
849  layout->StartRow(1, 0);
850  layout->AddView(current_profile_view);
851
852  if (browser_->profile()->IsManaged()) {
853    layout->StartRow(0, 0);
854    layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
855    layout->StartRow(1, 0);
856    layout->AddView(CreateSupervisedUserDisclaimerView());
857  }
858
859  if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
860    layout->StartRow(1, 0);
861    if (switches::IsFastUserSwitching())
862      layout->AddView(CreateOtherProfilesView(other_profiles));
863  } else {
864    DCHECK(current_profile_accounts);
865    layout->StartRow(0, 0);
866    layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
867    layout->StartRow(1, 0);
868    layout->AddView(current_profile_accounts);
869  }
870
871  layout->StartRow(0, 0);
872  layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
873
874  // Option buttons. Only available with the new profile management flag.
875  if (option_buttons_view) {
876    layout->StartRow(0, 0);
877    layout->AddView(option_buttons_view);
878  }
879
880  return view;
881}
882
883views::View* ProfileChooserView::CreatePreviewEnabledTutorialView(
884    const AvatarMenu::Item& current_avatar_item,
885    bool tutorial_shown) {
886  if (!switches::IsNewProfileManagementPreviewEnabled())
887    return NULL;
888
889  Profile* profile = browser_->profile();
890  const int show_count = profile->GetPrefs()->GetInteger(
891      prefs::kProfileAvatarTutorialShown);
892  // Do not show the tutorial if user has dismissed it.
893  if (show_count > kProfileAvatarTutorialShowMax)
894    return NULL;
895
896  if (!tutorial_shown) {
897    if (show_count == kProfileAvatarTutorialShowMax)
898      return NULL;
899    profile->GetPrefs()->SetInteger(
900        prefs::kProfileAvatarTutorialShown, show_count + 1);
901  }
902
903  return CreateTutorialView(
904      profiles::TUTORIAL_MODE_WELCOME,
905      l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE),
906      l10n_util::GetStringUTF16(
907          IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT),
908      l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE),
909      l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_OK_BUTTON),
910      &tutorial_learn_more_link_,
911      &tutorial_ok_button_);
912}
913
914views::View* ProfileChooserView::CreateSendPreviewFeedbackView() {
915  return CreateTutorialView(
916      profiles::TUTORIAL_MODE_SEND_FEEDBACK,
917      l10n_util::GetStringUTF16(IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE),
918      l10n_util::GetStringUTF16(
919          IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT),
920      l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW),
921      l10n_util::GetStringUTF16(IDS_PROFILES_SEND_FEEDBACK_BUTTON),
922      &tutorial_end_preview_link_,
923      &tutorial_send_feedback_button_);
924}
925
926views::View* ProfileChooserView::CreateTutorialView(
927    profiles::TutorialMode tutorial_mode,
928    const base::string16& title_text,
929    const base::string16& content_text,
930    const base::string16& link_text,
931    const base::string16& button_text,
932    views::Link** link,
933    views::LabelButton** button) {
934  tutorial_mode_ = tutorial_mode;
935
936  views::View* view = new views::View();
937  view->set_background(views::Background::CreateSolidBackground(
938      profiles::kAvatarTutorialBackgroundColor));
939  views::GridLayout* layout = CreateSingleColumnLayout(view,
940      kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew);
941  layout->SetInsets(views::kButtonVEdgeMarginNew,
942                    views::kButtonHEdgeMarginNew,
943                    views::kButtonVEdgeMarginNew,
944                    views::kButtonHEdgeMarginNew);
945
946  // Adds title.
947  views::Label* title_label = new views::Label(title_text);
948  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
949  title_label->SetAutoColorReadabilityEnabled(false);
950  title_label->SetEnabledColor(SK_ColorWHITE);
951  title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
952      ui::ResourceBundle::MediumFont));
953  layout->StartRow(1, 0);
954  layout->AddView(title_label);
955
956  // Adds body content.
957  views::Label* content_label = new views::Label(content_text);
958  content_label->SetMultiLine(true);
959  content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
960  content_label->SetAutoColorReadabilityEnabled(false);
961  content_label->SetEnabledColor(profiles::kAvatarTutorialContentTextColor);
962  layout->StartRowWithPadding(1, 0, 0, views::kRelatedControlVerticalSpacing);
963  layout->AddView(content_label);
964
965  // Adds links and buttons.
966  views::View* button_row = new views::View();
967  views::GridLayout* button_layout = new views::GridLayout(button_row);
968  views::ColumnSet* button_columns = button_layout->AddColumnSet(0);
969  button_columns->AddColumn(views::GridLayout::LEADING,
970      views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
971  button_columns->AddPaddingColumn(
972      1, views::kUnrelatedControlHorizontalSpacing);
973  button_columns->AddColumn(views::GridLayout::TRAILING,
974      views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
975  button_row->SetLayoutManager(button_layout);
976
977  *link = CreateLink(link_text, this);
978  (*link)->SetHorizontalAlignment(gfx::ALIGN_LEFT);
979  (*link)->SetAutoColorReadabilityEnabled(false);
980  (*link)->SetEnabledColor(SK_ColorWHITE);
981  button_layout->StartRow(1, 0);
982  button_layout->AddView(*link);
983
984  *button = new views::LabelButton(this, button_text);
985  (*button)->SetHorizontalAlignment(gfx::ALIGN_CENTER);
986  (*button)->SetStyle(views::Button::STYLE_BUTTON);
987  button_layout->AddView(*button);
988
989  layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing);
990  layout->AddView(button_row);
991
992  // Adds a padded caret image at the bottom.
993  views::View* padded_caret_view = new views::View();
994  views::GridLayout* padded_caret_layout =
995      new views::GridLayout(padded_caret_view);
996  views::ColumnSet* padded_columns = padded_caret_layout->AddColumnSet(0);
997  padded_columns->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
998  padded_columns->AddColumn(views::GridLayout::LEADING,
999      views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
1000  padded_caret_view->SetLayoutManager(padded_caret_layout);
1001
1002  views::ImageView* caret_image_view = new views::ImageView();
1003  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1004  caret_image_view->SetImage(
1005      *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_CARET));
1006
1007  padded_caret_layout->StartRow(1, 0);
1008  padded_caret_layout->AddView(caret_image_view);
1009
1010  views::View* view_with_caret = new views::View();
1011  views::GridLayout* layout_with_caret =
1012      CreateSingleColumnLayout(view_with_caret, kFixedMenuWidth);
1013  layout_with_caret->StartRow(1, 0);
1014  layout_with_caret->AddView(view);
1015  layout_with_caret->StartRow(1, 0);
1016  layout_with_caret->AddView(padded_caret_view);
1017  return view_with_caret;
1018}
1019
1020views::View* ProfileChooserView::CreateCurrentProfileView(
1021    const AvatarMenu::Item& avatar_item,
1022    bool is_guest) {
1023  views::View* view = new views::View();
1024  int column_width = kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew;
1025  views::GridLayout* layout = CreateSingleColumnLayout(view, column_width);
1026  layout->SetInsets(views::kButtonVEdgeMarginNew,
1027                    views::kButtonHEdgeMarginNew,
1028                    views::kUnrelatedControlVerticalSpacing,
1029                    views::kButtonHEdgeMarginNew);
1030
1031  // Profile icon, centered.
1032  float x_offset = (column_width - kLargeImageSide) / 2;
1033  current_profile_photo_ = new EditableProfilePhoto(
1034      this, avatar_item.icon, !is_guest,
1035      gfx::Rect(x_offset, 0, kLargeImageSide, kLargeImageSide));
1036
1037  if (switches::IsNewProfileManagementPreviewEnabled()) {
1038    question_mark_button_ = new views::ImageButton(this);
1039    question_mark_button_->SetImageAlignment(
1040        views::ImageButton::ALIGN_LEFT, views::ImageButton::ALIGN_MIDDLE);
1041    ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1042    question_mark_button_->SetImage(views::ImageButton::STATE_NORMAL,
1043        rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_STABLE));
1044    question_mark_button_->SetImage(views::ImageButton::STATE_HOVERED,
1045        rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_HOVER));
1046    question_mark_button_->SetImage(views::ImageButton::STATE_PRESSED,
1047        rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_SELECT));
1048    gfx::Size preferred_size = question_mark_button_->GetPreferredSize();
1049    question_mark_button_->SetBounds(
1050        0, 0, preferred_size.width(), preferred_size.height());
1051    current_profile_photo_->AddChildView(question_mark_button_);
1052  }
1053
1054  layout->StartRow(1, 0);
1055  layout->AddView(current_profile_photo_);
1056
1057  // Profile name, centered.
1058  bool editing_allowed = !is_guest && !browser_->profile()->IsManaged();
1059  current_profile_name_ = new EditableProfileName(
1060      this, profiles::GetAvatarNameForProfile(browser_->profile()),
1061                                              editing_allowed);
1062  layout->StartRow(1, 0);
1063  layout->AddView(current_profile_name_);
1064
1065  if (is_guest)
1066    return view;
1067
1068  // The available links depend on the type of profile that is active.
1069  if (avatar_item.signed_in) {
1070    layout->StartRow(1, 0);
1071    if (switches::IsNewProfileManagement()) {
1072      base::string16 link_title = l10n_util::GetStringUTF16(
1073          view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER ?
1074              IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON :
1075              IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON);
1076      manage_accounts_link_ = CreateLink(link_title, this);
1077      manage_accounts_link_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
1078      layout->AddView(manage_accounts_link_);
1079    } else {
1080      views::Label* email_label = new views::Label(avatar_item.sync_state);
1081      email_label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
1082      layout->AddView(email_label);
1083    }
1084  } else {
1085    SigninManagerBase* signin_manager =
1086        SigninManagerFactory::GetForProfile(
1087            browser_->profile()->GetOriginalProfile());
1088    if (signin_manager->IsSigninAllowed()) {
1089      signin_current_profile_link_ = new views::BlueButton(
1090        this, l10n_util::GetStringFUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL,
1091            l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)));
1092      layout->StartRow(1, 0);
1093      layout->AddView(signin_current_profile_link_);
1094    }
1095  }
1096
1097  return view;
1098}
1099
1100views::View* ProfileChooserView::CreateGuestProfileView() {
1101  gfx::Image guest_icon =
1102      ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1103          profiles::GetPlaceholderAvatarIconResourceID());
1104  AvatarMenu::Item guest_avatar_item(0, 0, guest_icon);
1105  guest_avatar_item.active = true;
1106  guest_avatar_item.name = l10n_util::GetStringUTF16(
1107      IDS_PROFILES_GUEST_PROFILE_NAME);
1108  guest_avatar_item.signed_in = false;
1109
1110  return CreateCurrentProfileView(guest_avatar_item, true);
1111}
1112
1113views::View* ProfileChooserView::CreateOtherProfilesView(
1114    const Indexes& avatars_to_show) {
1115  views::View* view = new views::View();
1116  views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
1117
1118  int num_avatars_to_show = avatars_to_show.size();
1119  for (int i = 0; i < num_avatars_to_show; ++i) {
1120    const size_t index = avatars_to_show[i];
1121    const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index);
1122    const int kSmallImageSide = 32;
1123
1124    gfx::Image image = profiles::GetSizedAvatarIcon(
1125        item.icon, true, kSmallImageSide, kSmallImageSide);
1126
1127    views::LabelButton* button = new BackgroundColorHoverButton(
1128        this,
1129        item.name,
1130        *image.ToImageSkia(),
1131        *image.ToImageSkia());
1132    button->set_min_size(gfx::Size(
1133        0, kButtonHeight + views::kRelatedControlVerticalSpacing));
1134
1135    open_other_profile_indexes_map_[button] = index;
1136
1137    layout->StartRow(1, 0);
1138    layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
1139    layout->StartRow(1, 0);
1140    layout->AddView(button);
1141  }
1142
1143  return view;
1144}
1145
1146views::View* ProfileChooserView::CreateOptionsView(bool enable_lock) {
1147  if (!switches::IsNewProfileManagement())
1148    return NULL;
1149
1150  views::View* view = new views::View();
1151  views::GridLayout* layout;
1152
1153  // Only signed-in users have the ability to lock.
1154  if (enable_lock) {
1155    layout = new views::GridLayout(view);
1156    views::ColumnSet* columns = layout->AddColumnSet(0);
1157    int width_of_lock_button =
1158        2 * views::kUnrelatedControlLargeHorizontalSpacing + 12;
1159    int width_of_users_button = kFixedMenuWidth - width_of_lock_button;
1160    columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
1161                       views::GridLayout::FIXED, width_of_users_button,
1162                       width_of_users_button);
1163    columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
1164                       views::GridLayout::FIXED, width_of_lock_button,
1165                       width_of_lock_button);
1166    view->SetLayoutManager(layout);
1167  } else {
1168    layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
1169  }
1170
1171  base::string16 text = browser_->profile()->IsGuestSession() ?
1172      l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST) :
1173      l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU_BUTTON,
1174          profiles::GetAvatarNameForProfile(browser_->profile()));
1175  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1176  users_button_ = new BackgroundColorHoverButton(
1177      this,
1178      text,
1179      *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR),
1180      *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR));
1181  users_button_->set_min_size(gfx::Size(
1182      0, kButtonHeight + views::kRelatedControlVerticalSpacing));
1183
1184  layout->StartRow(1, 0);
1185  layout->AddView(users_button_);
1186
1187  if (enable_lock) {
1188    lock_button_ = new BackgroundColorHoverButton(
1189        this,
1190        base::string16(),
1191        *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK),
1192        *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK));
1193    lock_button_->set_min_size(gfx::Size(
1194      0, kButtonHeight + views::kRelatedControlVerticalSpacing));
1195    layout->AddView(lock_button_);
1196  }
1197  return view;
1198}
1199
1200views::View* ProfileChooserView::CreateSupervisedUserDisclaimerView() {
1201  views::View* view = new views::View();
1202  views::GridLayout* layout = CreateSingleColumnLayout(
1203      view, kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew);
1204  layout->SetInsets(views::kRelatedControlVerticalSpacing,
1205                    views::kButtonHEdgeMarginNew,
1206                    views::kRelatedControlVerticalSpacing,
1207                    views::kButtonHEdgeMarginNew);
1208  views::Label* disclaimer = new views::Label(
1209      avatar_menu_->GetManagedUserInformation());
1210  disclaimer->SetMultiLine(true);
1211  disclaimer->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1212  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1213  disclaimer->SetFontList(rb->GetFontList(ui::ResourceBundle::SmallFont));
1214  layout->StartRow(1, 0);
1215  layout->AddView(disclaimer);
1216
1217  return view;
1218}
1219
1220views::View* ProfileChooserView::CreateCurrentProfileAccountsView(
1221    const AvatarMenu::Item& avatar_item) {
1222  DCHECK(avatar_item.signed_in);
1223  views::View* view = new views::View();
1224  view->set_background(views::Background::CreateSolidBackground(
1225      profiles::kAvatarBubbleAccountsBackgroundColor));
1226  views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
1227
1228  Profile* profile = browser_->profile();
1229  std::string primary_account =
1230      SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
1231  DCHECK(!primary_account.empty());
1232  std::vector<std::string>accounts =
1233      profiles::GetSecondaryAccountsForProfile(profile, primary_account);
1234
1235  // Get state of authentication error, if any.
1236  std::string error_account_id = GetAuthErrorAccountId(profile);
1237
1238  // The primary account should always be listed first.
1239  // TODO(rogerta): we still need to further differentiate the primary account
1240  // from the others in the UI, so more work is likely required here:
1241  // crbug.com/311124.
1242  CreateAccountButton(layout, primary_account, true,
1243                      error_account_id == primary_account, kFixedMenuWidth);
1244  for (size_t i = 0; i < accounts.size(); ++i)
1245    CreateAccountButton(layout, accounts[i], false,
1246                        error_account_id == accounts[i], kFixedMenuWidth);
1247  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
1248
1249  add_account_link_ = CreateLink(l10n_util::GetStringFUTF16(
1250      IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, avatar_item.name), this);
1251  add_account_link_->SetBorder(views::Border::CreateEmptyBorder(
1252      0, views::kButtonVEdgeMarginNew,
1253      views::kRelatedControlVerticalSpacing, 0));
1254  layout->StartRow(1, 0);
1255  layout->AddView(add_account_link_);
1256  return view;
1257}
1258
1259void ProfileChooserView::CreateAccountButton(views::GridLayout* layout,
1260                                             const std::string& account,
1261                                             bool is_primary_account,
1262                                             bool reauth_required,
1263                                             int width) {
1264  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1265  const gfx::ImageSkia* delete_default_image =
1266      rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia();
1267  const int kDeleteButtonWidth = delete_default_image->width();
1268  const gfx::ImageSkia* warning_default_image = reauth_required ?
1269      rb->GetImageNamed(IDR_WARNING).ToImageSkia() : NULL;
1270  const int kWarningButtonWidth = reauth_required ?
1271      warning_default_image->width() + views::kRelatedButtonHSpacing : 0;
1272  int available_width = width -
1273      kDeleteButtonWidth - kWarningButtonWidth - views::kButtonHEdgeMarginNew;
1274
1275  views::LabelButton* email_button = new BackgroundColorHoverButton(
1276      NULL,
1277      gfx::ElideEmail(base::UTF8ToUTF16(account),
1278                      rb->GetFontList(ui::ResourceBundle::BaseFont),
1279                      available_width),
1280      gfx::ImageSkia(),
1281      gfx::ImageSkia());
1282  layout->StartRow(1, 0);
1283  layout->AddView(email_button);
1284
1285  // Delete button.
1286  views::ImageButton* delete_button = new views::ImageButton(this);
1287  delete_button->SetImageAlignment(views::ImageButton::ALIGN_RIGHT,
1288                                   views::ImageButton::ALIGN_MIDDLE);
1289  delete_button->SetImage(views::ImageButton::STATE_NORMAL,
1290                          delete_default_image);
1291  delete_button->SetImage(views::ImageButton::STATE_HOVERED,
1292                          rb->GetImageSkiaNamed(IDR_CLOSE_1_H));
1293  delete_button->SetImage(views::ImageButton::STATE_PRESSED,
1294                          rb->GetImageSkiaNamed(IDR_CLOSE_1_P));
1295  delete_button->SetBounds(
1296      available_width + kWarningButtonWidth, 0,
1297      kDeleteButtonWidth, kButtonHeight);
1298
1299  email_button->set_notify_enter_exit_on_child(true);
1300  email_button->AddChildView(delete_button);
1301
1302  // Save the original email address, as the button text could be elided.
1303  delete_account_button_map_[delete_button] = account;
1304
1305  // Warning button.
1306  if (reauth_required) {
1307    views::ImageButton* reauth_button = new views::ImageButton(this);
1308    reauth_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1309                                     views::ImageButton::ALIGN_MIDDLE);
1310    reauth_button->SetImage(views::ImageButton::STATE_NORMAL,
1311                            warning_default_image);
1312    reauth_button->SetBounds(
1313        available_width, 0, kWarningButtonWidth, kButtonHeight);
1314
1315    email_button->AddChildView(reauth_button);
1316    reauth_account_button_map_[reauth_button] = account;
1317  }
1318}
1319
1320views::View* ProfileChooserView::CreateGaiaSigninView() {
1321  GURL url;
1322  int message_id;
1323
1324  switch (view_mode_) {
1325    case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
1326      url = signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_SIGN_IN,
1327                                false /* auto_close */,
1328                                true /* is_constrained */);
1329      message_id = IDS_PROFILES_GAIA_SIGNIN_TITLE;
1330      break;
1331    case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
1332      url = signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT,
1333                                false /* auto_close */,
1334                                true /* is_constrained */);
1335      message_id = IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE;
1336      break;
1337    case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: {
1338      DCHECK(HasAuthError(browser_->profile()));
1339      url = signin::GetReauthURL(browser_->profile(),
1340                                 GetAuthErrorUsername(browser_->profile()));
1341      message_id = IDS_PROFILES_GAIA_REAUTH_TITLE;
1342      break;
1343    }
1344    default:
1345      NOTREACHED() << "Called with invalid mode=" << view_mode_;
1346      return NULL;
1347  }
1348
1349  // Adds Gaia signin webview
1350  Profile* profile = browser_->profile();
1351  views::WebView* web_view = new views::WebView(profile);
1352  web_view->LoadInitialURL(url);
1353  web_view->SetPreferredSize(
1354      gfx::Size(kFixedGaiaViewWidth, kFixedGaiaViewHeight));
1355
1356  TitleCard* title_card = new TitleCard(message_id, this,
1357                                        &gaia_signin_cancel_button_);
1358  return TitleCard::AddPaddedTitleCard(
1359      web_view, title_card, kFixedGaiaViewWidth);
1360}
1361
1362views::View* ProfileChooserView::CreateAccountRemovalView() {
1363  views::View* view = new views::View();
1364  views::GridLayout* layout = CreateSingleColumnLayout(
1365      view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew);
1366  layout->SetInsets(0,
1367                    views::kButtonHEdgeMarginNew,
1368                    views::kButtonVEdgeMarginNew,
1369                    views::kButtonHEdgeMarginNew);
1370
1371  const std::string& primary_account = SigninManagerFactory::GetForProfile(
1372      browser_->profile())->GetAuthenticatedUsername();
1373  bool is_primary_account = primary_account == account_id_to_remove_;
1374
1375  // Adds main text.
1376  layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1377  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1378  const gfx::FontList& small_font_list =
1379      rb->GetFontList(ui::ResourceBundle::SmallFont);
1380
1381  if (is_primary_account) {
1382    std::vector<size_t> offsets;
1383    const base::string16 settings_text =
1384        l10n_util::GetStringUTF16(IDS_PROFILES_SETTINGS_LINK);
1385    const base::string16 primary_account_removal_text =
1386        l10n_util::GetStringFUTF16(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT,
1387            base::UTF8ToUTF16(account_id_to_remove_), settings_text, &offsets);
1388    views::StyledLabel* primary_account_removal_label =
1389        new views::StyledLabel(primary_account_removal_text, this);
1390    primary_account_removal_label->AddStyleRange(
1391        gfx::Range(offsets[1], offsets[1] + settings_text.size()),
1392        views::StyledLabel::RangeStyleInfo::CreateForLink());
1393    primary_account_removal_label->SetBaseFontList(small_font_list);
1394    layout->AddView(primary_account_removal_label);
1395  } else {
1396    views::Label* content_label = new views::Label(
1397        l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT));
1398    content_label->SetMultiLine(true);
1399    content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1400    content_label->SetFontList(small_font_list);
1401    layout->AddView(content_label);
1402  }
1403
1404  // Adds button.
1405  if (!is_primary_account) {
1406    remove_account_button_ = new views::BlueButton(
1407        this, l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON));
1408    remove_account_button_->SetHorizontalAlignment(
1409        gfx::ALIGN_CENTER);
1410    layout->StartRowWithPadding(
1411        1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1412    layout->AddView(remove_account_button_);
1413  } else {
1414    layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
1415  }
1416
1417  TitleCard* title_card = new TitleCard(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE,
1418      this, &account_removal_cancel_button_);
1419  return TitleCard::AddPaddedTitleCard(view, title_card,
1420      kFixedAccountRemovalViewWidth);
1421}
1422
1423views::View* ProfileChooserView::CreateNewProfileManagementPreviewView() {
1424  return CreateTutorialView(
1425      profiles::TUTORIAL_MODE_ENABLE_PREVIEW,
1426      l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_TITLE),
1427      l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT),
1428      l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE),
1429      l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_TRY_BUTTON),
1430      &tutorial_learn_more_link_,
1431      &tutorial_enable_new_profile_management_button_);
1432}
1433
1434views::View* ProfileChooserView::CreateEndPreviewView() {
1435  views::View* view = new views::View();
1436  views::GridLayout* layout = CreateSingleColumnLayout(
1437      view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew);
1438  layout->SetInsets(0,
1439                    views::kButtonHEdgeMarginNew,
1440                    views::kButtonVEdgeMarginNew,
1441                    views::kButtonHEdgeMarginNew);
1442
1443  // Adds main text.
1444  views::Label* content_label = new views::Label(
1445      l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_TEXT));
1446  content_label->SetMultiLine(true);
1447  content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1448  ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1449  const gfx::FontList& small_font_list =
1450      rb->GetFontList(ui::ResourceBundle::SmallFont);
1451  content_label->SetFontList(small_font_list);
1452  layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1453  layout->AddView(content_label);
1454
1455  // Adds button.
1456  end_preview_and_relaunch_button_ = new views::BlueButton(
1457      this, l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_AND_RELAUNCH));
1458  end_preview_and_relaunch_button_->SetHorizontalAlignment(
1459      gfx::ALIGN_CENTER);
1460  layout->StartRowWithPadding(
1461      1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1462  layout->AddView(end_preview_and_relaunch_button_);
1463
1464  TitleCard* title_card = new TitleCard(
1465      IDS_PROFILES_END_PREVIEW, this, &end_preview_cancel_button_);
1466  return TitleCard::AddPaddedTitleCard(
1467      view, title_card, kFixedAccountRemovalViewWidth);
1468}
1469
1470