manage_passwords_bubble_view.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2013 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/passwords/manage_passwords_bubble_view.h" 6 7#include "chrome/browser/chrome_notification_types.h" 8#include "chrome/browser/ui/browser.h" 9#include "chrome/browser/ui/browser_finder.h" 10#include "chrome/browser/ui/browser_window.h" 11#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h" 12#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" 13#include "chrome/browser/ui/views/frame/browser_view.h" 14#include "chrome/browser/ui/views/location_bar/location_bar_view.h" 15#include "chrome/browser/ui/views/passwords/manage_password_item_view.h" 16#include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h" 17#include "components/password_manager/core/common/password_manager_ui.h" 18#include "content/public/browser/notification_source.h" 19#include "grit/generated_resources.h" 20#include "ui/base/l10n/l10n_util.h" 21#include "ui/base/models/combobox_model.h" 22#include "ui/base/resource/resource_bundle.h" 23#include "ui/gfx/text_utils.h" 24#include "ui/views/controls/button/blue_button.h" 25#include "ui/views/controls/button/label_button.h" 26#include "ui/views/controls/combobox/combobox.h" 27#include "ui/views/controls/styled_label.h" 28#include "ui/views/layout/fill_layout.h" 29#include "ui/views/layout/grid_layout.h" 30#include "ui/views/layout/layout_constants.h" 31 32 33// Helpers -------------------------------------------------------------------- 34 35namespace { 36 37// The number of seconds the inactive bubble should stay alive. 38const int kBubbleCloseDelay = 15; 39 40const int kDesiredBubbleWidth = 370; 41 42enum ColumnSetType { 43 // | | (FILL, FILL) | | 44 // Used for the bubble's header, the credentials list, and for simple 45 // messages like "No passwords". 46 SINGLE_VIEW_COLUMN_SET = 0, 47 48 // | | (TRAILING, CENTER) | | (TRAILING, CENTER) | | 49 // Used for buttons at the bottom of the bubble which should nest at the 50 // bottom-right corner. 51 DOUBLE_BUTTON_COLUMN_SET = 1, 52 53 // | | (LEADING, CENTER) | | (TRAILING, CENTER) | | 54 // Used for buttons at the bottom of the bubble which should occupy 55 // the corners. 56 LINK_BUTTON_COLUMN_SET = 2, 57 58 // | | (TRAILING, CENTER) | | 59 // Used when there is only one button which should next at the bottom-right 60 // corner. 61 SINGLE_BUTTON_COLUMN_SET = 3, 62}; 63 64// Construct an appropriate ColumnSet for the given |type|, and add it 65// to |layout|. 66void BuildColumnSet(views::GridLayout* layout, ColumnSetType type) { 67 views::ColumnSet* column_set = layout->AddColumnSet(type); 68 column_set->AddPaddingColumn(0, views::kPanelHorizMargin); 69 int full_width = kDesiredBubbleWidth - (2 * views::kPanelHorizMargin); 70 switch (type) { 71 case SINGLE_VIEW_COLUMN_SET: 72 column_set->AddColumn(views::GridLayout::FILL, 73 views::GridLayout::FILL, 74 0, 75 views::GridLayout::FIXED, 76 full_width, 77 0); 78 break; 79 80 case DOUBLE_BUTTON_COLUMN_SET: 81 column_set->AddColumn(views::GridLayout::TRAILING, 82 views::GridLayout::CENTER, 83 1, 84 views::GridLayout::USE_PREF, 85 0, 86 0); 87 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing); 88 column_set->AddColumn(views::GridLayout::TRAILING, 89 views::GridLayout::CENTER, 90 0, 91 views::GridLayout::USE_PREF, 92 0, 93 0); 94 break; 95 case LINK_BUTTON_COLUMN_SET: 96 column_set->AddColumn(views::GridLayout::LEADING, 97 views::GridLayout::CENTER, 98 1, 99 views::GridLayout::USE_PREF, 100 0, 101 0); 102 column_set->AddPaddingColumn(0, views::kRelatedButtonHSpacing); 103 column_set->AddColumn(views::GridLayout::TRAILING, 104 views::GridLayout::CENTER, 105 0, 106 views::GridLayout::USE_PREF, 107 0, 108 0); 109 break; 110 case SINGLE_BUTTON_COLUMN_SET: 111 column_set->AddColumn(views::GridLayout::TRAILING, 112 views::GridLayout::CENTER, 113 1, 114 views::GridLayout::USE_PREF, 115 0, 116 0); 117 } 118 column_set->AddPaddingColumn(0, views::kPanelHorizMargin); 119} 120 121// Given a layout and a model, add an appropriate title using a 122// SINGLE_VIEW_COLUMN_SET, followed by a spacer row. 123void AddTitleRow(views::GridLayout* layout, ManagePasswordsBubbleModel* model) { 124 views::Label* title_label = new views::Label(model->title()); 125 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 126 title_label->SetMultiLine(true); 127 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 128 ui::ResourceBundle::MediumFont)); 129 130 // Add the title to the layout with appropriate padding. 131 layout->StartRowWithPadding( 132 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing); 133 layout->AddView(title_label); 134 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); 135} 136 137} // namespace 138 139 140// Globals -------------------------------------------------------------------- 141 142namespace chrome { 143 144void ShowManagePasswordsBubble(content::WebContents* web_contents) { 145 if (ManagePasswordsBubbleView::IsShowing()) { 146 // The bubble is currently shown for some other tab. We should close it now 147 // and open for |web_contents|. 148 ManagePasswordsBubbleView::CloseBubble(); 149 } 150 ManagePasswordsUIController* controller = 151 ManagePasswordsUIController::FromWebContents(web_contents); 152 ManagePasswordsBubbleView::ShowBubble( 153 web_contents, 154 password_manager::ui::IsAutomaticDisplayState(controller->state()) 155 ? ManagePasswordsBubbleView::AUTOMATIC 156 : ManagePasswordsBubbleView::USER_ACTION); 157} 158 159} // namespace chrome 160 161 162// ManagePasswordsBubbleView::PendingView ------------------------------------- 163 164ManagePasswordsBubbleView::PendingView::PendingView( 165 ManagePasswordsBubbleView* parent) 166 : parent_(parent) { 167 views::GridLayout* layout = new views::GridLayout(this); 168 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); 169 SetLayoutManager(layout); 170 171 // Create the pending credential item, save button and refusal combobox. 172 ManagePasswordItemView* item = 173 new ManagePasswordItemView(parent_->model(), 174 parent_->model()->pending_credentials(), 175 ManagePasswordItemView::FIRST_ITEM); 176 save_button_ = new views::BlueButton( 177 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON)); 178 save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 179 ui::ResourceBundle::SmallFont)); 180 181 combobox_model_.reset(new SavePasswordRefusalComboboxModel()); 182 refuse_combobox_.reset(new views::Combobox(combobox_model_.get())); 183 refuse_combobox_->set_listener(this); 184 refuse_combobox_->SetStyle(views::Combobox::STYLE_ACTION); 185 // TODO(mkwst): Need a mechanism to pipe a font list down into a combobox. 186 187 // Title row. 188 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET); 189 AddTitleRow(layout, parent_->model()); 190 191 // Credential row. 192 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); 193 layout->AddView(item); 194 195 // Button row. 196 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET); 197 layout->StartRowWithPadding( 198 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing); 199 layout->AddView(save_button_); 200 layout->AddView(refuse_combobox_.get()); 201 202 // Extra padding for visual awesomeness. 203 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 204 205 parent_->set_initially_focused_view(save_button_); 206} 207 208ManagePasswordsBubbleView::PendingView::~PendingView() { 209} 210 211void ManagePasswordsBubbleView::PendingView::ButtonPressed( 212 views::Button* sender, 213 const ui::Event& event) { 214 DCHECK(sender == save_button_); 215 parent_->model()->OnSaveClicked(); 216 parent_->Close(); 217} 218 219void ManagePasswordsBubbleView::PendingView::OnPerformAction( 220 views::Combobox* source) { 221 DCHECK_EQ(source, refuse_combobox_); 222 switch (refuse_combobox_->selected_index()) { 223 case SavePasswordRefusalComboboxModel::INDEX_NOPE: 224 parent_->model()->OnNopeClicked(); 225 parent_->Close(); 226 break; 227 case SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE: 228 parent_->NotifyNeverForThisSiteClicked(); 229 break; 230 } 231} 232 233// ManagePasswordsBubbleView::ConfirmNeverView --------------------------------- 234 235ManagePasswordsBubbleView::ConfirmNeverView::ConfirmNeverView( 236 ManagePasswordsBubbleView* parent) 237 : parent_(parent) { 238 views::GridLayout* layout = new views::GridLayout(this); 239 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); 240 SetLayoutManager(layout); 241 242 // Title row. 243 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET); 244 views::Label* title_label = new views::Label(l10n_util::GetStringUTF16( 245 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TITLE)); 246 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 247 title_label->SetMultiLine(true); 248 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 249 ui::ResourceBundle::MediumFont)); 250 layout->StartRowWithPadding( 251 0, SINGLE_VIEW_COLUMN_SET, 0, views::kRelatedControlSmallVerticalSpacing); 252 layout->AddView(title_label); 253 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); 254 255 // Confirmation text. 256 views::Label* confirmation = new views::Label(l10n_util::GetStringUTF16( 257 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_TEXT)); 258 confirmation->SetHorizontalAlignment(gfx::ALIGN_LEFT); 259 confirmation->SetMultiLine(true); 260 confirmation->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 261 ui::ResourceBundle::SmallFont)); 262 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); 263 layout->AddView(confirmation); 264 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); 265 266 // Confirm and undo buttons. 267 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET); 268 layout->StartRowWithPadding( 269 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing); 270 271 confirm_button_ = new views::LabelButton( 272 this, 273 l10n_util::GetStringUTF16( 274 IDS_MANAGE_PASSWORDS_BLACKLIST_CONFIRMATION_BUTTON)); 275 confirm_button_->SetStyle(views::Button::STYLE_BUTTON); 276 confirm_button_->SetFontList( 277 ui::ResourceBundle::GetSharedInstance().GetFontList( 278 ui::ResourceBundle::SmallFont)); 279 layout->AddView(confirm_button_); 280 281 undo_button_ = 282 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_CANCEL)); 283 undo_button_->SetStyle(views::Button::STYLE_BUTTON); 284 undo_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 285 ui::ResourceBundle::SmallFont)); 286 layout->AddView(undo_button_); 287 288 // Extra padding for visual awesomeness. 289 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 290 291 parent_->set_initially_focused_view(confirm_button_); 292} 293 294ManagePasswordsBubbleView::ConfirmNeverView::~ConfirmNeverView() { 295} 296 297void ManagePasswordsBubbleView::ConfirmNeverView::ButtonPressed( 298 views::Button* sender, 299 const ui::Event& event) { 300 DCHECK(sender == confirm_button_ || sender == undo_button_); 301 if (sender == confirm_button_) 302 parent_->NotifyConfirmedNeverForThisSite(); 303 else 304 parent_->NotifyUndoNeverForThisSite(); 305} 306 307// ManagePasswordsBubbleView::ManageView -------------------------------------- 308 309ManagePasswordsBubbleView::ManageView::ManageView( 310 ManagePasswordsBubbleView* parent) 311 : parent_(parent) { 312 views::GridLayout* layout = new views::GridLayout(this); 313 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); 314 SetLayoutManager(layout); 315 316 // Add the title. 317 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET); 318 AddTitleRow(layout, parent_->model()); 319 320 // If we have a list of passwords to store for the current site, display 321 // them to the user for management. Otherwise, render a "No passwords for 322 // this site" message. 323 if (!parent_->model()->best_matches().empty()) { 324 for (autofill::ConstPasswordFormMap::const_iterator i( 325 parent_->model()->best_matches().begin()); 326 i != parent_->model()->best_matches().end(); 327 ++i) { 328 ManagePasswordItemView* item = new ManagePasswordItemView( 329 parent_->model(), 330 *i->second, 331 i == parent_->model()->best_matches().begin() 332 ? ManagePasswordItemView::FIRST_ITEM 333 : ManagePasswordItemView::SUBSEQUENT_ITEM); 334 335 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); 336 layout->AddView(item); 337 } 338 } else { 339 views::Label* empty_label = new views::Label( 340 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_NO_PASSWORDS)); 341 empty_label->SetMultiLine(true); 342 empty_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 343 empty_label->SetFontList( 344 ui::ResourceBundle::GetSharedInstance().GetFontList( 345 ui::ResourceBundle::SmallFont)); 346 347 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); 348 layout->AddView(empty_label); 349 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); 350 } 351 352 // Then add the "manage passwords" link and "Done" button. 353 manage_link_ = new views::Link(parent_->model()->manage_link()); 354 manage_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 355 manage_link_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 356 ui::ResourceBundle::SmallFont)); 357 manage_link_->SetUnderline(false); 358 manage_link_->set_listener(this); 359 360 done_button_ = 361 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE)); 362 done_button_->SetStyle(views::Button::STYLE_BUTTON); 363 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 364 ui::ResourceBundle::SmallFont)); 365 366 BuildColumnSet(layout, LINK_BUTTON_COLUMN_SET); 367 layout->StartRowWithPadding( 368 0, LINK_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing); 369 layout->AddView(manage_link_); 370 layout->AddView(done_button_); 371 372 // Extra padding for visual awesomeness. 373 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 374 375 parent_->set_initially_focused_view(done_button_); 376} 377 378ManagePasswordsBubbleView::ManageView::~ManageView() { 379} 380 381void ManagePasswordsBubbleView::ManageView::ButtonPressed( 382 views::Button* sender, 383 const ui::Event& event) { 384 DCHECK(sender == done_button_); 385 parent_->model()->OnDoneClicked(); 386 parent_->Close(); 387} 388 389void ManagePasswordsBubbleView::ManageView::LinkClicked(views::Link* source, 390 int event_flags) { 391 DCHECK_EQ(source, manage_link_); 392 parent_->model()->OnManageLinkClicked(); 393 parent_->Close(); 394} 395 396// ManagePasswordsBubbleView::BlacklistedView --------------------------------- 397 398ManagePasswordsBubbleView::BlacklistedView::BlacklistedView( 399 ManagePasswordsBubbleView* parent) 400 : parent_(parent) { 401 views::GridLayout* layout = new views::GridLayout(this); 402 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); 403 SetLayoutManager(layout); 404 405 // Add the title. 406 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET); 407 AddTitleRow(layout, parent_->model()); 408 409 // Add the "Hey! You blacklisted this site!" text. 410 views::Label* blacklisted = new views::Label( 411 l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_BLACKLISTED)); 412 blacklisted->SetMultiLine(true); 413 blacklisted->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 414 ui::ResourceBundle::SmallFont)); 415 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); 416 layout->AddView(blacklisted); 417 layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); 418 419 // Then add the "enable password manager" and "Done" buttons. 420 unblacklist_button_ = new views::BlueButton( 421 this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UNBLACKLIST_BUTTON)); 422 unblacklist_button_->SetFontList( 423 ui::ResourceBundle::GetSharedInstance().GetFontList( 424 ui::ResourceBundle::SmallFont)); 425 done_button_ = 426 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_DONE)); 427 done_button_->SetStyle(views::Button::STYLE_BUTTON); 428 done_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 429 ui::ResourceBundle::SmallFont)); 430 431 BuildColumnSet(layout, DOUBLE_BUTTON_COLUMN_SET); 432 layout->StartRowWithPadding( 433 0, DOUBLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing); 434 layout->AddView(unblacklist_button_); 435 layout->AddView(done_button_); 436 437 // Extra padding for visual awesomeness. 438 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 439 440 parent_->set_initially_focused_view(unblacklist_button_); 441} 442 443ManagePasswordsBubbleView::BlacklistedView::~BlacklistedView() { 444} 445 446void ManagePasswordsBubbleView::BlacklistedView::ButtonPressed( 447 views::Button* sender, 448 const ui::Event& event) { 449 if (sender == done_button_) 450 parent_->model()->OnDoneClicked(); 451 else if (sender == unblacklist_button_) 452 parent_->model()->OnUnblacklistClicked(); 453 else 454 NOTREACHED(); 455 parent_->Close(); 456} 457 458// ManagePasswordsBubbleView::SaveConfirmationView ---------------------------- 459 460ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView( 461 ManagePasswordsBubbleView* parent) 462 : parent_(parent) { 463 views::GridLayout* layout = new views::GridLayout(this); 464 layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); 465 SetLayoutManager(layout); 466 467 BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET); 468 AddTitleRow(layout, parent_->model()); 469 470 views::StyledLabel* confirmation = 471 new views::StyledLabel(parent_->model()->save_confirmation_text(), this); 472 confirmation->SetBaseFontList( 473 ui::ResourceBundle::GetSharedInstance().GetFontList( 474 ui::ResourceBundle::SmallFont)); 475 confirmation->AddStyleRange( 476 parent_->model()->save_confirmation_link_range(), 477 views::StyledLabel::RangeStyleInfo::CreateForLink()); 478 479 layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); 480 layout->AddView(confirmation); 481 482 ok_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK)); 483 ok_button_->SetStyle(views::Button::STYLE_BUTTON); 484 ok_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 485 ui::ResourceBundle::SmallFont)); 486 487 BuildColumnSet(layout, SINGLE_BUTTON_COLUMN_SET); 488 layout->StartRowWithPadding( 489 0, SINGLE_BUTTON_COLUMN_SET, 0, views::kRelatedControlVerticalSpacing); 490 layout->AddView(ok_button_); 491 492 // Extra padding for visual awesomeness. 493 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 494 495 parent_->set_initially_focused_view(ok_button_); 496} 497 498ManagePasswordsBubbleView::SaveConfirmationView::~SaveConfirmationView() { 499} 500 501void ManagePasswordsBubbleView::SaveConfirmationView::StyledLabelLinkClicked( 502 const gfx::Range& range, int event_flags) { 503 DCHECK_EQ(range, parent_->model()->save_confirmation_link_range()); 504 parent_->model()->OnRemoteManageLinkClicked(); 505 parent_->Close(); 506} 507 508void ManagePasswordsBubbleView::SaveConfirmationView::ButtonPressed( 509 views::Button* sender, const ui::Event& event) { 510 DCHECK_EQ(sender, ok_button_); 511 parent_->model()->OnOKClicked(); 512 parent_->Close(); 513} 514 515// ManagePasswordsBubbleView -------------------------------------------------- 516 517// static 518ManagePasswordsBubbleView* ManagePasswordsBubbleView::manage_passwords_bubble_ = 519 NULL; 520 521// static 522void ManagePasswordsBubbleView::ShowBubble(content::WebContents* web_contents, 523 DisplayReason reason) { 524 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); 525 DCHECK(browser); 526 DCHECK(browser->window()); 527 DCHECK(browser->fullscreen_controller()); 528 529 if (IsShowing()) 530 return; 531 532 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); 533 bool is_fullscreen = browser_view->IsFullscreen(); 534 ManagePasswordsIconView* anchor_view = 535 is_fullscreen 536 ? NULL 537 : browser_view->GetLocationBarView()->manage_passwords_icon_view(); 538 manage_passwords_bubble_ = new ManagePasswordsBubbleView( 539 web_contents, anchor_view, reason); 540 541 if (is_fullscreen) { 542 manage_passwords_bubble_->set_parent_window( 543 web_contents->GetTopLevelNativeWindow()); 544 } 545 546 views::BubbleDelegateView::CreateBubble(manage_passwords_bubble_); 547 548 // Adjust for fullscreen after creation as it relies on the content size. 549 if (is_fullscreen) { 550 manage_passwords_bubble_->AdjustForFullscreen( 551 browser_view->GetBoundsInScreen()); 552 } 553 if (reason == AUTOMATIC) 554 manage_passwords_bubble_->GetWidget()->ShowInactive(); 555 else 556 manage_passwords_bubble_->GetWidget()->Show(); 557 manage_passwords_bubble_->StartTimerIfNecessary(); 558} 559 560// static 561void ManagePasswordsBubbleView::CloseBubble() { 562 if (manage_passwords_bubble_) 563 manage_passwords_bubble_->Close(); 564} 565 566// static 567void ManagePasswordsBubbleView::ActivateBubble() { 568 if (!IsShowing()) 569 return; 570 manage_passwords_bubble_->GetWidget()->Activate(); 571} 572 573// static 574bool ManagePasswordsBubbleView::IsShowing() { 575 // The bubble may be in the process of closing. 576 return (manage_passwords_bubble_ != NULL) && 577 manage_passwords_bubble_->GetWidget()->IsVisible(); 578} 579 580ManagePasswordsBubbleView::ManagePasswordsBubbleView( 581 content::WebContents* web_contents, 582 ManagePasswordsIconView* anchor_view, 583 DisplayReason reason) 584 : ManagePasswordsBubble(web_contents, reason), 585 BubbleDelegateView(anchor_view, 586 anchor_view ? views::BubbleBorder::TOP_RIGHT 587 : views::BubbleBorder::NONE), 588 anchor_view_(anchor_view), 589 never_save_passwords_(false), 590 initially_focused_view_(NULL) { 591 // Compensate for built-in vertical padding in the anchor view's image. 592 set_anchor_view_insets(gfx::Insets(5, 0, 5, 0)); 593 set_notify_enter_exit_on_child(true); 594 if (anchor_view) 595 anchor_view->SetActive(true); 596} 597 598ManagePasswordsBubbleView::~ManagePasswordsBubbleView() { 599 if (anchor_view_) 600 anchor_view_->SetActive(false); 601} 602 603void ManagePasswordsBubbleView::AdjustForFullscreen( 604 const gfx::Rect& screen_bounds) { 605 if (GetAnchorView()) 606 return; 607 608 // The bubble's padding from the screen edge, used in fullscreen. 609 const int kFullscreenPaddingEnd = 20; 610 const size_t bubble_half_width = width() / 2; 611 const int x_pos = base::i18n::IsRTL() ? 612 screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd : 613 screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd; 614 SetAnchorRect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0)); 615} 616 617void ManagePasswordsBubbleView::Close() { 618 GetWidget()->Close(); 619} 620 621void ManagePasswordsBubbleView::Init() { 622 views::FillLayout* layout = new views::FillLayout(); 623 SetLayoutManager(layout); 624 625 Refresh(); 626} 627 628void ManagePasswordsBubbleView::WindowClosing() { 629 // Close() closes the window asynchronously, so by the time we reach here, 630 // |manage_passwords_bubble_| may have already been reset. 631 if (manage_passwords_bubble_ == this) 632 manage_passwords_bubble_ = NULL; 633} 634 635void ManagePasswordsBubbleView::OnWidgetActivationChanged(views::Widget* widget, 636 bool active) { 637 if (active && widget == GetWidget()) 638 timer_.Stop(); 639 BubbleDelegateView::OnWidgetActivationChanged(widget, active); 640} 641 642views::View* ManagePasswordsBubbleView::GetInitiallyFocusedView() { 643 return initially_focused_view_; 644} 645 646void ManagePasswordsBubbleView::OnMouseEntered(const ui::MouseEvent& event) { 647 timer_.Stop(); 648} 649 650void ManagePasswordsBubbleView::OnMouseExited(const ui::MouseEvent& event) { 651 StartTimerIfNecessary(); 652} 653 654void ManagePasswordsBubbleView::Refresh() { 655 RemoveAllChildViews(true); 656 initially_focused_view_ = NULL; 657 if (password_manager::ui::IsPendingState(model()->state())) { 658 if (never_save_passwords_) 659 AddChildView(new ConfirmNeverView(this)); 660 else 661 AddChildView(new PendingView(this)); 662 } else if (model()->state() == password_manager::ui::BLACKLIST_STATE) { 663 AddChildView(new BlacklistedView(this)); 664 } else if (model()->state() == password_manager::ui::CONFIRMATION_STATE) { 665 AddChildView(new SaveConfirmationView(this)); 666 } else { 667 AddChildView(new ManageView(this)); 668 } 669 GetLayoutManager()->Layout(this); 670 // If we refresh the existing bubble we may want to restart the timer. 671 if (GetWidget()) 672 StartTimerIfNecessary(); 673} 674 675void ManagePasswordsBubbleView::NotifyNeverForThisSiteClicked() { 676 if (model()->best_matches().empty()) { 677 // Skip confirmation if there are no existing passwords for this site. 678 NotifyConfirmedNeverForThisSite(); 679 } else { 680 never_save_passwords_ = true; 681 Refresh(); 682 } 683} 684 685void ManagePasswordsBubbleView::NotifyConfirmedNeverForThisSite() { 686 model()->OnNeverForThisSiteClicked(); 687 Close(); 688} 689 690void ManagePasswordsBubbleView::NotifyUndoNeverForThisSite() { 691 never_save_passwords_ = false; 692 Refresh(); 693} 694 695void ManagePasswordsBubbleView::StartTimerIfNecessary() { 696 // Active bubble will stay visible until it loses focus. 697 if (GetWidget()->IsActive()) 698 return; 699 timer_.Start(FROM_HERE, 700 base::TimeDelta::FromSeconds(kBubbleCloseDelay), 701 this, 702 &ManagePasswordsBubbleView::Close); 703} 704