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