Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / sync / one_click_signin_bubble_view.cc
blobd2e631ffd025fec3ca18d464bbef39b88ce33b98
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/ui/views/sync/one_click_signin_bubble_view.h"
7 #include "base/callback_helpers.h"
8 #include "base/logging.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/common/url_constants.h"
11 #include "chrome/grit/chromium_strings.h"
12 #include "chrome/grit/generated_resources.h"
13 #include "components/google/core/browser/google_util.h"
14 #include "grit/components_strings.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/events/keycodes/keyboard_codes.h"
18 #include "ui/resources/grit/ui_resources.h"
19 #include "ui/views/controls/button/image_button.h"
20 #include "ui/views/controls/button/label_button.h"
21 #include "ui/views/controls/image_view.h"
22 #include "ui/views/controls/label.h"
23 #include "ui/views/controls/link.h"
24 #include "ui/views/layout/grid_layout.h"
25 #include "ui/views/layout/layout_constants.h"
26 #include "ui/views/widget/widget.h"
28 // Minimum width of the the bubble.
29 const int kMinBubbleWidth = 310;
31 // Minimum width for the multi-line label.
32 const int kMinimumDialogLabelWidth = 400;
33 const int kMinimumLabelWidth = 240;
34 const int kDialogMargin = 16;
36 namespace {
38 // The column set constants that can be used in the InitContent() function
39 // to layout views.
40 enum OneClickSigninBubbleColumnTypes {
41 COLUMN_SET_FILL_ALIGN,
42 COLUMN_SET_CONTROLS,
43 COLUMN_SET_TITLE_BAR
45 } // namespace
47 // static
48 OneClickSigninBubbleView* OneClickSigninBubbleView::bubble_view_ = NULL;
50 // static
51 void OneClickSigninBubbleView::ShowBubble(
52 BrowserWindow::OneClickSigninBubbleType type,
53 const base::string16& email,
54 const base::string16& error_message,
55 scoped_ptr<OneClickSigninBubbleDelegate> delegate,
56 views::View* anchor_view,
57 const BrowserWindow::StartSyncCallback& start_sync) {
58 if (IsShowing())
59 return;
61 switch (type) {
62 case BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE:
63 bubble_view_ = new OneClickSigninBubbleView(
64 error_message, base::string16(), delegate.Pass(),
65 anchor_view, start_sync, false);
66 break;
67 case BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_MODAL_DIALOG:
68 bubble_view_ = new OneClickSigninBubbleView(
69 base::string16(), base::string16(), delegate.Pass(),
70 anchor_view, start_sync, true);
71 break;
72 case BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG:
73 bubble_view_ = new OneClickSigninBubbleView(
74 base::string16(), email, delegate.Pass(),
75 anchor_view, start_sync, true);
76 break;
79 views::BubbleDelegateView::CreateBubble(bubble_view_)->Show();
82 // static
83 bool OneClickSigninBubbleView::IsShowing() {
84 return bubble_view_ != NULL;
87 // static
88 void OneClickSigninBubbleView::Hide() {
89 if (IsShowing())
90 bubble_view_->GetWidget()->Close();
93 OneClickSigninBubbleView::OneClickSigninBubbleView(
94 const base::string16& error_message,
95 const base::string16& email,
96 scoped_ptr<OneClickSigninBubbleDelegate> delegate,
97 views::View* anchor_view,
98 const BrowserWindow::StartSyncCallback& start_sync_callback,
99 bool is_sync_dialog)
100 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
101 delegate_(delegate.Pass()),
102 error_message_(error_message),
103 email_(email),
104 start_sync_callback_(start_sync_callback),
105 is_sync_dialog_(is_sync_dialog),
106 advanced_link_(NULL),
107 learn_more_link_(NULL),
108 ok_button_(NULL),
109 undo_button_(NULL),
110 close_button_(NULL),
111 clicked_learn_more_(false) {
112 if (is_sync_dialog_) {
113 DCHECK(!start_sync_callback_.is_null());
114 set_arrow(views::BubbleBorder::NONE);
115 set_anchor_view_insets(gfx::Insets(0, 0, anchor_view->height() / 2, 0));
116 set_close_on_deactivate(false);
118 int margin = is_sync_dialog_ ? kDialogMargin : views::kButtonVEdgeMarginNew;
119 set_margins(gfx::Insets(margin, margin, margin, margin));
122 OneClickSigninBubbleView::~OneClickSigninBubbleView() {
125 ui::ModalType OneClickSigninBubbleView::GetModalType() const {
126 return is_sync_dialog_? ui::MODAL_TYPE_CHILD : ui::MODAL_TYPE_NONE;
129 void OneClickSigninBubbleView::Init() {
130 views::GridLayout* layout = new views::GridLayout(this);
131 SetLayoutManager(layout);
133 SetBorder(views::Border::CreateEmptyBorder(8, 8, 8, 8));
135 // Column set for descriptive text and link.
136 views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_FILL_ALIGN);
137 cs->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1,
138 views::GridLayout::USE_PREF, 0, 0);
140 // Column set for buttons at bottom of bubble.
141 cs = layout->AddColumnSet(COLUMN_SET_CONTROLS);
142 cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
143 views::GridLayout::USE_PREF, 0, 0);
144 cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
145 cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
146 views::GridLayout::USE_PREF, 0, 0);
147 cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
148 views::GridLayout::USE_PREF, 0, 0);
150 is_sync_dialog_ ? InitDialogContent(layout) : InitBubbleContent(layout);
152 // Add controls at the bottom.
153 // Don't display the advanced link for the error bubble.
154 if (is_sync_dialog_ || error_message_.empty()) {
155 InitAdvancedLink();
156 layout->StartRow(0, COLUMN_SET_CONTROLS);
157 layout->AddView(advanced_link_);
160 InitButtons(layout);
161 ok_button_->SetIsDefault(true);
163 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, 0));
166 void OneClickSigninBubbleView::InitBubbleContent(views::GridLayout* layout) {
167 layout->set_minimum_size(gfx::Size(kMinBubbleWidth, 0));
169 // If no error occurred, add title message.
170 if (error_message_.empty()) {
171 views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_TITLE_BAR);
172 cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0,
173 views::GridLayout::USE_PREF, 0, 0);
175 layout->StartRow(0, COLUMN_SET_TITLE_BAR);
177 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
178 views::Label* label = new views::Label(
179 l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW),
180 rb.GetFontList(ui::ResourceBundle::MediumFont));
181 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
182 layout->AddView(label);
185 layout->AddPaddingRow(0, views::kUnrelatedControlLargeVerticalSpacing);
188 // Add main text description.
189 layout->StartRow(0, COLUMN_SET_FILL_ALIGN);
191 views::Label* label = !error_message_.empty() ?
192 new views::Label(error_message_) :
193 new views::Label(
194 l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_BUBBLE_MESSAGE));
196 label->SetMultiLine(true);
197 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
198 label->SizeToFit(kMinimumLabelWidth);
199 layout->AddView(label);
201 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
203 layout->StartRow(0, COLUMN_SET_CONTROLS);
205 InitLearnMoreLink();
206 layout->AddView(learn_more_link_);
209 void OneClickSigninBubbleView::InitDialogContent(views::GridLayout* layout) {
210 signin_metrics::LogSigninConfirmHistogramValue(
211 signin_metrics::HISTOGRAM_CONFIRM_SHOWN);
213 // Column set for title bar.
214 views::ColumnSet* cs = layout->AddColumnSet(COLUMN_SET_TITLE_BAR);
215 cs->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
216 views::GridLayout::USE_PREF, 0, 0);
217 cs->AddPaddingColumn(1, views::kUnrelatedControlHorizontalSpacing);
218 cs->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER, 0,
219 views::GridLayout::USE_PREF, 0, 0);
221 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
224 layout->StartRow(0, COLUMN_SET_TITLE_BAR);
226 views::Label* label = new views::Label(
227 l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW),
228 rb.GetFontList(ui::ResourceBundle::MediumBoldFont));
229 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
230 layout->AddView(label);
232 close_button_ = new views::ImageButton(this);
233 close_button_->SetImage(views::ImageButton::STATE_NORMAL,
234 rb.GetImageNamed(IDR_CLOSE_2).ToImageSkia());
235 close_button_->SetImage(views::ImageButton::STATE_HOVERED,
236 rb.GetImageNamed(IDR_CLOSE_2_H).ToImageSkia());
237 close_button_->SetImage(views::ImageButton::STATE_PRESSED,
238 rb.GetImageNamed(IDR_CLOSE_2_P).ToImageSkia());
240 layout->AddView(close_button_);
243 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
246 layout->StartRow(0, COLUMN_SET_FILL_ALIGN);
248 views::Label* label = new views::Label(
249 l10n_util::GetStringFUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW,
250 email_));
251 label->SetMultiLine(true);
252 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
253 label->SizeToFit(kMinimumDialogLabelWidth);
254 layout->AddView(label);
256 layout->StartRow(0, COLUMN_SET_FILL_ALIGN);
258 InitLearnMoreLink();
259 layout->AddView(learn_more_link_, 1, 1, views::GridLayout::TRAILING,
260 views::GridLayout::CENTER);
263 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
266 void OneClickSigninBubbleView::InitButtons(views::GridLayout* layout) {
267 GetButtons(&ok_button_, &undo_button_);
268 layout->AddView(ok_button_);
270 if (is_sync_dialog_)
271 layout->AddView(undo_button_);
274 void OneClickSigninBubbleView::GetButtons(views::LabelButton** ok_button,
275 views::LabelButton** undo_button) {
276 base::string16 ok_label = !error_message_.empty() ?
277 l10n_util::GetStringUTF16(IDS_OK) :
278 l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_OK_BUTTON);
280 *ok_button = new views::LabelButton(this, ok_label);
281 (*ok_button)->SetStyle(views::Button::STYLE_BUTTON);
283 // The default size of the buttons is too large. To allow them to be smaller
284 // ignore the minimum default size.,
285 (*ok_button)->SetMinSize(gfx::Size());
287 if (is_sync_dialog_) {
288 *undo_button = new views::LabelButton(this, base::string16());
289 (*undo_button)->SetStyle(views::Button::STYLE_BUTTON);
290 (*undo_button)->SetMinSize(gfx::Size());
292 base::string16 undo_label =
293 l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_UNDO_BUTTON);
294 (*undo_button)->SetText(undo_label);
298 void OneClickSigninBubbleView::InitAdvancedLink() {
299 advanced_link_ = new views::Link(
300 l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_ADVANCED));
302 advanced_link_->set_listener(this);
303 advanced_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
306 void OneClickSigninBubbleView::InitLearnMoreLink() {
307 learn_more_link_ = new views::Link(
308 l10n_util::GetStringUTF16(IDS_LEARN_MORE));
309 learn_more_link_->set_listener(this);
310 learn_more_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
313 bool OneClickSigninBubbleView::AcceleratorPressed(
314 const ui::Accelerator& accelerator) {
315 if (accelerator.key_code() == ui::VKEY_RETURN ||
316 accelerator.key_code() == ui::VKEY_ESCAPE) {
317 OneClickSigninBubbleView::Hide();
319 if (is_sync_dialog_) {
320 if (accelerator.key_code() == ui::VKEY_RETURN) {
321 signin_metrics::LogSigninConfirmHistogramValue(
322 clicked_learn_more_ ?
323 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_RETURN :
324 signin_metrics::HISTOGRAM_CONFIRM_RETURN);
326 base::ResetAndReturn(&start_sync_callback_).Run(
327 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS);
328 } else if (accelerator.key_code() == ui::VKEY_ESCAPE) {
329 signin_metrics::LogSigninConfirmHistogramValue(
330 clicked_learn_more_ ?
331 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_ESCAPE :
332 signin_metrics::HISTOGRAM_CONFIRM_ESCAPE);
334 base::ResetAndReturn(&start_sync_callback_).Run(
335 OneClickSigninSyncStarter::UNDO_SYNC);
339 return true;
342 return BubbleDelegateView::AcceleratorPressed(accelerator);
345 void OneClickSigninBubbleView::LinkClicked(views::Link* source,
346 int event_flags) {
347 if (source == learn_more_link_) {
348 if (is_sync_dialog_ && !clicked_learn_more_) {
349 signin_metrics::LogSigninConfirmHistogramValue(
350 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE);
351 clicked_learn_more_ = true;
353 delegate_->OnLearnMoreLinkClicked(is_sync_dialog_);
355 // don't hide the modal dialog, as this is an informational link
356 if (is_sync_dialog_)
357 return;
358 } else if (advanced_link_ && source == advanced_link_) {
359 if (is_sync_dialog_) {
360 signin_metrics::LogSigninConfirmHistogramValue(
361 clicked_learn_more_ ?
362 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED :
363 signin_metrics::HISTOGRAM_CONFIRM_ADVANCED);
365 base::ResetAndReturn(&start_sync_callback_).Run(
366 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST);
367 } else {
368 delegate_->OnAdvancedLinkClicked();
372 Hide();
375 void OneClickSigninBubbleView::ButtonPressed(views::Button* sender,
376 const ui::Event& event) {
377 Hide();
379 if (is_sync_dialog_) {
380 if (sender == ok_button_)
381 signin_metrics::LogSigninConfirmHistogramValue(
382 clicked_learn_more_ ?
383 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_OK :
384 signin_metrics::HISTOGRAM_CONFIRM_OK);
386 if (sender == undo_button_)
387 signin_metrics::LogSigninConfirmHistogramValue(
388 clicked_learn_more_ ?
389 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_UNDO :
390 signin_metrics::HISTOGRAM_CONFIRM_UNDO);
392 if (sender == close_button_)
393 signin_metrics::LogSigninConfirmHistogramValue(
394 clicked_learn_more_ ?
395 signin_metrics::HISTOGRAM_CONFIRM_LEARN_MORE_CLOSE :
396 signin_metrics::HISTOGRAM_CONFIRM_CLOSE);
398 base::ResetAndReturn(&start_sync_callback_).Run((sender == ok_button_) ?
399 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS :
400 OneClickSigninSyncStarter::UNDO_SYNC);
404 void OneClickSigninBubbleView::WindowClosing() {
405 // We have to reset |bubble_view_| here, not in our destructor, because
406 // we'll be destroyed asynchronously and the shown state will be checked
407 // before then.
408 DCHECK_EQ(bubble_view_, this);
409 bubble_view_ = NULL;
411 if (is_sync_dialog_ && !start_sync_callback_.is_null()) {
412 base::ResetAndReturn(&start_sync_callback_).Run(
413 OneClickSigninSyncStarter::UNDO_SYNC);