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