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