| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/autofill/password_generation_popup_controller_impl.h" |
| |
| #include <math.h> |
| |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversion_utils.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/ui/autofill/password_generation_popup_observer.h" |
| #include "chrome/browser/ui/autofill/password_generation_popup_view.h" |
| #include "chrome/browser/ui/autofill/popup_constants.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/autofill/content/common/autofill_messages.h" |
| #include "components/autofill/core/browser/password_generator.h" |
| #include "components/password_manager/core/browser/password_manager.h" |
| #include "content/public/browser/native_web_keyboard_event.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "grit/chromium_strings.h" |
| #include "grit/generated_resources.h" |
| #include "grit/google_chrome_strings.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/events/keycodes/keyboard_codes.h" |
| #include "ui/gfx/rect_conversions.h" |
| #include "ui/gfx/text_utils.h" |
| |
| namespace autofill { |
| |
| base::WeakPtr<PasswordGenerationPopupControllerImpl> |
| PasswordGenerationPopupControllerImpl::GetOrCreate( |
| base::WeakPtr<PasswordGenerationPopupControllerImpl> previous, |
| const gfx::RectF& bounds, |
| const PasswordForm& form, |
| int max_length, |
| password_manager::PasswordManager* password_manager, |
| PasswordGenerationPopupObserver* observer, |
| content::WebContents* web_contents, |
| gfx::NativeView container_view) { |
| if (previous.get() && |
| previous->element_bounds() == bounds && |
| previous->web_contents() == web_contents && |
| previous->container_view() == container_view) { |
| return previous; |
| } |
| |
| if (previous.get()) |
| previous->Hide(); |
| |
| PasswordGenerationPopupControllerImpl* controller = |
| new PasswordGenerationPopupControllerImpl( |
| bounds, |
| form, |
| max_length, |
| password_manager, |
| observer, |
| web_contents, |
| container_view); |
| return controller->GetWeakPtr(); |
| } |
| |
| PasswordGenerationPopupControllerImpl::PasswordGenerationPopupControllerImpl( |
| const gfx::RectF& bounds, |
| const PasswordForm& form, |
| int max_length, |
| password_manager::PasswordManager* password_manager, |
| PasswordGenerationPopupObserver* observer, |
| content::WebContents* web_contents, |
| gfx::NativeView container_view) |
| : form_(form), |
| password_manager_(password_manager), |
| observer_(observer), |
| generator_(new PasswordGenerator(max_length)), |
| controller_common_(bounds, container_view, web_contents), |
| view_(NULL), |
| font_list_(ResourceBundle::GetSharedInstance().GetFontList( |
| ResourceBundle::SmallFont)), |
| password_selected_(false), |
| display_password_(false), |
| weak_ptr_factory_(this) { |
| controller_common_.SetKeyPressCallback( |
| base::Bind(&PasswordGenerationPopupControllerImpl::HandleKeyPressEvent, |
| base::Unretained(this))); |
| |
| std::vector<base::string16> pieces; |
| base::SplitStringDontTrim( |
| l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_PROMPT), |
| '|', // separator |
| &pieces); |
| DCHECK_EQ(3u, pieces.size()); |
| link_range_ = gfx::Range(pieces[0].size(), |
| pieces[0].size() + pieces[1].size()); |
| help_text_ = JoinString(pieces, base::string16()); |
| } |
| |
| PasswordGenerationPopupControllerImpl::~PasswordGenerationPopupControllerImpl() |
| {} |
| |
| base::WeakPtr<PasswordGenerationPopupControllerImpl> |
| PasswordGenerationPopupControllerImpl::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| bool PasswordGenerationPopupControllerImpl::HandleKeyPressEvent( |
| const content::NativeWebKeyboardEvent& event) { |
| switch (event.windowsKeyCode) { |
| case ui::VKEY_UP: |
| case ui::VKEY_DOWN: |
| PasswordSelected(true); |
| return true; |
| case ui::VKEY_ESCAPE: |
| Hide(); |
| return true; |
| case ui::VKEY_RETURN: |
| case ui::VKEY_TAB: |
| // We suppress tab if the password is selected because we will |
| // automatically advance focus anyway. |
| return PossiblyAcceptPassword(); |
| default: |
| return false; |
| } |
| } |
| |
| bool PasswordGenerationPopupControllerImpl::PossiblyAcceptPassword() { |
| if (password_selected_) { |
| PasswordAccepted(); // This will delete |this|. |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void PasswordGenerationPopupControllerImpl::PasswordSelected(bool selected) { |
| if (!display_password_ || selected == password_selected_) |
| return; |
| |
| password_selected_ = selected; |
| view_->PasswordSelectionUpdated(); |
| view_->UpdateBoundsAndRedrawPopup(); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::PasswordAccepted() { |
| if (!display_password_) |
| return; |
| |
| web_contents()->GetRenderViewHost()->Send( |
| new AutofillMsg_GeneratedPasswordAccepted( |
| web_contents()->GetRenderViewHost()->GetRoutingID(), |
| current_password_)); |
| password_manager_->SetFormHasGeneratedPassword(form_); |
| Hide(); |
| } |
| |
| int PasswordGenerationPopupControllerImpl::GetDesiredWidth() { |
| // Minimum width in pixels. |
| const int minimum_required_width = 300; |
| |
| // If the width of the field is longer than the minimum, use that instead. |
| int width = std::max(minimum_required_width, |
| controller_common_.RoundedElementBounds().width()); |
| |
| if (display_password_) { |
| // Make sure that the width will always be large enough to display the |
| // password and suggestion on one line. |
| width = std::max(width, |
| gfx::GetStringWidth(current_password_ + SuggestedText(), |
| font_list_) + 2 * kHorizontalPadding); |
| } |
| |
| return width; |
| } |
| |
| int PasswordGenerationPopupControllerImpl::GetDesiredHeight(int width) { |
| // Note that this wrapping isn't exactly what the popup will do. It shouldn't |
| // line break in the middle of the link, but as long as the link isn't longer |
| // than given width this shouldn't affect the height calculated here. The |
| // default width should be wide enough to prevent this from being an issue. |
| int total_length = gfx::GetStringWidth(HelpText(), font_list_); |
| int usable_width = width - 2 * kHorizontalPadding; |
| int text_height = |
| static_cast<int>(ceil(static_cast<double>(total_length)/usable_width)) * |
| font_list_.GetFontSize(); |
| int help_section_height = text_height + 2 * kHelpVerticalPadding; |
| |
| int password_section_height = 0; |
| if (display_password_) { |
| password_section_height = |
| font_list_.GetFontSize() + 2 * kPasswordVerticalPadding; |
| } |
| |
| return (2 * kPopupBorderThickness + |
| help_section_height + |
| password_section_height); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::CalculateBounds() { |
| int popup_width = GetDesiredWidth(); |
| int popup_height = GetDesiredHeight(popup_width); |
| |
| popup_bounds_ = controller_common_.GetPopupBounds(popup_height, popup_width); |
| int sub_view_width = popup_bounds_.width() - 2 * kPopupBorderThickness; |
| |
| // Calculate the bounds for the rest of the elements given the bounds of |
| // the popup. |
| if (display_password_) { |
| password_bounds_ = gfx::Rect( |
| kPopupBorderThickness, |
| kPopupBorderThickness, |
| sub_view_width, |
| font_list_.GetFontSize() + 2 * kPasswordVerticalPadding); |
| |
| divider_bounds_ = gfx::Rect(kPopupBorderThickness, |
| password_bounds_.bottom(), |
| sub_view_width, |
| 1 /* divider heigth*/); |
| } else { |
| password_bounds_ = gfx::Rect(); |
| divider_bounds_ = gfx::Rect(); |
| } |
| |
| int help_y = std::max(kPopupBorderThickness, divider_bounds_.bottom()); |
| int help_height = |
| popup_bounds_.height() - help_y - kPopupBorderThickness; |
| help_bounds_ = gfx::Rect( |
| kPopupBorderThickness, |
| help_y, |
| sub_view_width, |
| help_height); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::Show(bool display_password) { |
| display_password_ = display_password; |
| if (display_password_) |
| current_password_ = base::ASCIIToUTF16(generator_->Generate()); |
| |
| CalculateBounds(); |
| |
| if (!view_) { |
| view_ = PasswordGenerationPopupView::Create(this); |
| view_->Show(); |
| } else { |
| view_->UpdateBoundsAndRedrawPopup(); |
| } |
| |
| controller_common_.RegisterKeyPressCallback(); |
| |
| if (observer_) |
| observer_->OnPopupShown(display_password_); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::HideAndDestroy() { |
| Hide(); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::Hide() { |
| controller_common_.RemoveKeyPressCallback(); |
| |
| if (view_) |
| view_->Hide(); |
| |
| if (observer_) |
| observer_->OnPopupHidden(); |
| |
| delete this; |
| } |
| |
| void PasswordGenerationPopupControllerImpl::ViewDestroyed() { |
| view_ = NULL; |
| |
| Hide(); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::OnSavedPasswordsLinkClicked() { |
| // TODO(gcasto): Change this to navigate to account central once passwords |
| // are visible there. |
| Browser* browser = |
| chrome::FindBrowserWithWebContents(controller_common_.web_contents()); |
| content::OpenURLParams params( |
| GURL(chrome::kAutoPasswordGenerationLearnMoreURL), content::Referrer(), |
| NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false); |
| browser->OpenURL(params); |
| } |
| |
| void PasswordGenerationPopupControllerImpl::SetSelectionAtPoint( |
| const gfx::Point& point) { |
| PasswordSelected(password_bounds_.Contains(point)); |
| } |
| |
| bool PasswordGenerationPopupControllerImpl::AcceptSelectedLine() { |
| if (!password_selected_) |
| return false; |
| |
| PasswordAccepted(); |
| return true; |
| } |
| |
| void PasswordGenerationPopupControllerImpl::SelectionCleared() { |
| PasswordSelected(false); |
| } |
| |
| gfx::NativeView PasswordGenerationPopupControllerImpl::container_view() { |
| return controller_common_.container_view(); |
| } |
| |
| const gfx::FontList& PasswordGenerationPopupControllerImpl::font_list() const { |
| return font_list_; |
| } |
| |
| const gfx::Rect& PasswordGenerationPopupControllerImpl::popup_bounds() const { |
| return popup_bounds_; |
| } |
| |
| const gfx::Rect& PasswordGenerationPopupControllerImpl::password_bounds() |
| const { |
| return password_bounds_; |
| } |
| |
| const gfx::Rect& PasswordGenerationPopupControllerImpl::divider_bounds() |
| const { |
| return divider_bounds_; |
| } |
| |
| const gfx::Rect& PasswordGenerationPopupControllerImpl::help_bounds() const { |
| return help_bounds_; |
| } |
| |
| bool PasswordGenerationPopupControllerImpl::display_password() const { |
| return display_password_; |
| } |
| |
| bool PasswordGenerationPopupControllerImpl::password_selected() const { |
| return password_selected_; |
| } |
| |
| base::string16 PasswordGenerationPopupControllerImpl::password() const { |
| return current_password_; |
| } |
| |
| base::string16 PasswordGenerationPopupControllerImpl::SuggestedText() { |
| return l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SUGGESTION); |
| } |
| |
| const base::string16& PasswordGenerationPopupControllerImpl::HelpText() { |
| return help_text_; |
| } |
| |
| const gfx::Range& PasswordGenerationPopupControllerImpl::HelpTextLinkRange() { |
| return link_range_; |
| } |
| |
| } // namespace autofill |