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