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;
38 // The column set constants that can be used in the InitContent() function
40 enum OneClickSigninBubbleColumnTypes
{
41 COLUMN_SET_FILL_ALIGN
,
48 OneClickSigninBubbleView
* OneClickSigninBubbleView::bubble_view_
= NULL
;
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
) {
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);
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);
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);
79 views::BubbleDelegateView::CreateBubble(bubble_view_
)->Show();
83 bool OneClickSigninBubbleView::IsShowing() {
84 return bubble_view_
!= NULL
;
88 void OneClickSigninBubbleView::Hide() {
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
,
100 : BubbleDelegateView(anchor_view
, views::BubbleBorder::TOP_RIGHT
),
101 delegate_(delegate
.Pass()),
102 error_message_(error_message
),
104 start_sync_callback_(start_sync_callback
),
105 is_sync_dialog_(is_sync_dialog
),
106 advanced_link_(NULL
),
107 learn_more_link_(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()) {
156 layout
->StartRow(0, COLUMN_SET_CONTROLS
);
157 layout
->AddView(advanced_link_
);
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_
) :
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
);
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
,
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
);
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_
);
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
);
342 return BubbleDelegateView::AcceleratorPressed(accelerator
);
345 void OneClickSigninBubbleView::LinkClicked(views::Link
* source
,
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
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
);
368 delegate_
->OnAdvancedLinkClicked();
375 void OneClickSigninBubbleView::ButtonPressed(views::Button
* sender
,
376 const ui::Event
& event
) {
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
408 DCHECK_EQ(bubble_view_
, this);
411 if (is_sync_dialog_
&& !start_sync_callback_
.is_null()) {
412 base::ResetAndReturn(&start_sync_callback_
).Run(
413 OneClickSigninSyncStarter::UNDO_SYNC
);