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