// 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/views/autofill/password_generation_popup_view_views.h" #include "base/macros.h" #include "base/strings/string16.h" #include "chrome/browser/ui/autofill/password_generation_popup_controller.h" #include "chrome/browser/ui/autofill/popup_constants.h" #include "ui/accessibility/ax_view_state.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icons_public.h" #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/styled_label.h" #include "ui/views/layout/box_layout.h" #include "ui/views/widget/widget.h" namespace autofill { namespace { // The amount of whitespace that is present when there is no padding. Used // to get the proper spacing in the help section. const int kHelpVerticalOffset = 5; // Wrapper around just the text portions of the generation UI (password and // prompting text). class PasswordTextBox : public views::View { public: PasswordTextBox() {} ~PasswordTextBox() override {} // |suggestion_text| prompts the user to select the password, // |generated_password| is the generated password, and |font_list| is the font // used for all text in this class. void Init(const base::string16& suggestion_text, const base::string16& generated_password, const gfx::FontList& font_list) { views::BoxLayout* box_layout = new views::BoxLayout( views::BoxLayout::kVertical, 0, 12, 5); box_layout->set_main_axis_alignment( views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); SetLayoutManager(box_layout); views::Label* suggestion_label = new views::Label( suggestion_text, font_list.DeriveWithStyle(gfx::Font::BOLD)); suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); suggestion_label->SetEnabledColor( PasswordGenerationPopupView::kPasswordTextColor); AddChildView(suggestion_label); views::Label* password_label = new views::Label(generated_password, font_list); password_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); password_label->SetEnabledColor( PasswordGenerationPopupView::kPasswordTextColor); AddChildView(password_label); } // views::View: bool CanProcessEventsWithinSubtree() const override { // Send events to the parent view for handling. return false; } private: DISALLOW_COPY_AND_ASSIGN(PasswordTextBox); }; } // namespace // Class that shows the generated password and associated UI (currently a key // image and some explanatory text). class PasswordGenerationPopupViewViews::PasswordBox : public views::View { public: PasswordBox() {} ~PasswordBox() override {} // |password| is the generated password, |suggestion| is the text prompting // the user to select the password, and |font_list| is the font used for all // the text. void Init(const base::string16& password, const base::string16& suggestion, const gfx::FontList& font_list) { views::BoxLayout* box_layout = new views::BoxLayout( views::BoxLayout::kHorizontal, PasswordGenerationPopupController::kHorizontalPadding, 0, PasswordGenerationPopupController::kHorizontalPadding); box_layout->set_main_axis_alignment( views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); SetLayoutManager(box_layout); views::ImageView* key_image = new views::ImageView(); key_image->SetImage(gfx::CreateVectorIcon(gfx::VectorIconId::AUTOLOGIN, 32, gfx::kChromeIconGrey)); AddChildView(key_image); PasswordTextBox* password_text_box = new PasswordTextBox(); password_text_box->Init(suggestion, password, font_list); AddChildView(password_text_box); } // views::View: bool CanProcessEventsWithinSubtree() const override { // Send events to the parent view for handling. return false; } private: DISALLOW_COPY_AND_ASSIGN(PasswordBox); }; PasswordGenerationPopupViewViews::PasswordGenerationPopupViewViews( PasswordGenerationPopupController* controller, views::Widget* parent_widget) : AutofillPopupBaseView(controller, parent_widget), password_view_(NULL), font_list_(ResourceBundle::GetSharedInstance().GetFontList( ResourceBundle::SmallFont)), controller_(controller) { if (controller_->display_password()) CreatePasswordView(); help_label_ = new views::StyledLabel(controller_->HelpText(), this); help_label_->SetBaseFontList(font_list_); help_label_->SetLineHeight(20); views::StyledLabel::RangeStyleInfo default_style; default_style.color = kExplanatoryTextColor; help_label_->SetDefaultStyle(default_style); views::StyledLabel::RangeStyleInfo link_style = views::StyledLabel::RangeStyleInfo::CreateForLink(); link_style.disable_line_wrapping = false; help_label_->AddStyleRange(controller_->HelpTextLinkRange(), link_style); help_label_->set_background( views::Background::CreateSolidBackground( kExplanatoryTextBackgroundColor)); help_label_->SetBorder(views::Border::CreateEmptyBorder( PasswordGenerationPopupController::kHelpVerticalPadding - kHelpVerticalOffset, PasswordGenerationPopupController::kHorizontalPadding, PasswordGenerationPopupController::kHelpVerticalPadding - kHelpVerticalOffset, PasswordGenerationPopupController::kHorizontalPadding)); AddChildView(help_label_); set_background(views::Background::CreateSolidBackground(kPopupBackground)); } PasswordGenerationPopupViewViews::~PasswordGenerationPopupViewViews() {} void PasswordGenerationPopupViewViews::CreatePasswordView() { if (password_view_) return; password_view_ = new PasswordBox(); password_view_->Init(controller_->password(), controller_->SuggestedText(), font_list_); password_view_->SetPosition(gfx::Point(kPopupBorderThickness, kPopupBorderThickness)); password_view_->SizeToPreferredSize(); AddChildView(password_view_); } gfx::Size PasswordGenerationPopupViewViews::GetPreferredSizeOfPasswordView() { int height = kPopupBorderThickness; if (controller_->display_password()) { // Add divider height as well. height += PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1; } int width = controller_->GetMinimumWidth(); int popup_width = width - 2 * kPopupBorderThickness; height += help_label_->GetHeightForWidth(popup_width); return gfx::Size(width, height + kPopupBorderThickness); } void PasswordGenerationPopupViewViews::Show() { DoShow(); } void PasswordGenerationPopupViewViews::Hide() { // The controller is no longer valid after it hides us. controller_ = NULL; DoHide(); } void PasswordGenerationPopupViewViews::UpdateBoundsAndRedrawPopup() { DoUpdateBoundsAndRedrawPopup(); } void PasswordGenerationPopupViewViews::PasswordSelectionUpdated() { if (!password_view_) return; if (controller_->password_selected()) NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); password_view_->set_background( views::Background::CreateSolidBackground( controller_->password_selected() ? kHoveredBackgroundColor : kPopupBackground)); } void PasswordGenerationPopupViewViews::Layout() { // Need to leave room for the border. int y = kPopupBorderThickness; int popup_width = bounds().width() - 2 * kPopupBorderThickness; if (controller_->display_password()) { // Currently the UI can change from not offering a password to offering // a password (e.g. the user is editing a generated password and deletes // it), but it can't change the other way around. CreatePasswordView(); password_view_->SetBounds( kPopupBorderThickness, y, popup_width, PasswordGenerationPopupController::kPopupPasswordSectionHeight); divider_bounds_ = gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(), popup_width, 1); y = divider_bounds_.bottom(); } help_label_->SetBounds(kPopupBorderThickness, y, popup_width, help_label_->GetHeightForWidth(popup_width)); } void PasswordGenerationPopupViewViews::OnPaint(gfx::Canvas* canvas) { if (!controller_) return; // Draw border and background. views::View::OnPaint(canvas); // Divider line needs to be drawn after OnPaint() otherwise the background // will overwrite the divider. if (password_view_) canvas->FillRect(divider_bounds_, kDividerColor); } void PasswordGenerationPopupViewViews::StyledLabelLinkClicked( views::StyledLabel* label, const gfx::Range& range, int event_flags) { controller_->OnSavedPasswordsLinkClicked(); } bool PasswordGenerationPopupViewViews::IsPointInPasswordBounds( const gfx::Point& point) { if (password_view_) return password_view_->bounds().Contains(point); return false; } PasswordGenerationPopupView* PasswordGenerationPopupView::Create( PasswordGenerationPopupController* controller) { views::Widget* observing_widget = views::Widget::GetTopLevelWidgetForNativeView( controller->container_view()); // If the top level widget can't be found, cancel the popup since we can't // fully set it up. if (!observing_widget) return NULL; return new PasswordGenerationPopupViewViews(controller, observing_widget); } void PasswordGenerationPopupViewViews::GetAccessibleState( ui::AXViewState* state) { state->name = controller_->SuggestedText(); state->role = ui::AX_ROLE_MENU_ITEM; } } // namespace autofill