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