Home | History | Annotate | Download | only in autofill
      1 // Copyright 2014 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/autofill/password_generation_popup_view_views.h"
      6 
      7 #include "base/strings/string16.h"
      8 #include "chrome/browser/ui/autofill/password_generation_popup_controller.h"
      9 #include "chrome/browser/ui/autofill/popup_constants.h"
     10 #include "grit/theme_resources.h"
     11 #include "ui/base/resource/resource_bundle.h"
     12 #include "ui/gfx/canvas.h"
     13 #include "ui/views/background.h"
     14 #include "ui/views/border.h"
     15 #include "ui/views/controls/image_view.h"
     16 #include "ui/views/controls/label.h"
     17 #include "ui/views/controls/styled_label.h"
     18 #include "ui/views/layout/box_layout.h"
     19 #include "ui/views/widget/widget.h"
     20 
     21 namespace autofill {
     22 
     23 namespace {
     24 
     25 // The amount of whitespace that is present when there is no padding. Used
     26 // to get the proper spacing in the help section.
     27 const int kHelpVerticalOffset = 5;
     28 
     29 // Wrapper around just the text portions of the generation UI (password and
     30 // prompting text).
     31 class PasswordTextBox : public views::View {
     32  public:
     33   PasswordTextBox() {}
     34   virtual ~PasswordTextBox() {}
     35 
     36   // |suggestion_text| prompts the user to select the password,
     37   // |generated_password| is the generated password, and |font_list| is the font
     38   // used for all text in this class.
     39   void Init(const base::string16& suggestion_text,
     40             const base::string16& generated_password,
     41             const gfx::FontList& font_list) {
     42     views::BoxLayout* box_layout = new views::BoxLayout(
     43         views::BoxLayout::kVertical, 0, 12, 5);
     44     box_layout->set_main_axis_alignment(
     45         views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
     46     SetLayoutManager(box_layout);
     47 
     48     views::Label* suggestion_label = new views::Label(
     49         suggestion_text, font_list.DeriveWithStyle(gfx::Font::BOLD));
     50     suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     51     suggestion_label->SetEnabledColor(
     52         PasswordGenerationPopupView::kPasswordTextColor);
     53     AddChildView(suggestion_label);
     54 
     55     views::Label* password_label =
     56         new views::Label(generated_password, font_list);
     57     password_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     58     password_label->SetEnabledColor(
     59         PasswordGenerationPopupView::kPasswordTextColor);
     60     AddChildView(password_label);
     61   }
     62 
     63   // views::View:
     64   virtual bool CanProcessEventsWithinSubtree() const OVERRIDE {
     65     // Send events to the parent view for handling.
     66     return false;
     67   }
     68 
     69  private:
     70   DISALLOW_COPY_AND_ASSIGN(PasswordTextBox);
     71 };
     72 
     73 }  // namespace
     74 
     75 // Class that shows the generated password and associated UI (currently a key
     76 // image and some explanatory text).
     77 class PasswordGenerationPopupViewViews::PasswordBox : public views::View {
     78  public:
     79   PasswordBox() {}
     80   virtual ~PasswordBox() {}
     81 
     82   // |password| is the generated password, |suggestion| is the text prompting
     83   // the user to select the password, and |font_list| is the font used for all
     84   // the text.
     85   void Init(const base::string16& password,
     86             const base::string16& suggestion,
     87             const gfx::FontList& font_list) {
     88     views::BoxLayout* box_layout = new views::BoxLayout(
     89         views::BoxLayout::kHorizontal,
     90         PasswordGenerationPopupController::kHorizontalPadding,
     91         0,
     92         15);
     93     box_layout->set_main_axis_alignment(
     94         views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
     95     SetLayoutManager(box_layout);
     96 
     97     views::ImageView* key_image = new views::ImageView();
     98     key_image->SetImage(
     99         ui::ResourceBundle::GetSharedInstance().GetImageNamed(
    100             IDR_GENERATE_PASSWORD_KEY).ToImageSkia());
    101     AddChildView(key_image);
    102 
    103     PasswordTextBox* password_text_box = new PasswordTextBox();
    104     password_text_box->Init(suggestion, password, font_list);
    105     AddChildView(password_text_box);
    106   }
    107 
    108   // views::View:
    109   virtual bool CanProcessEventsWithinSubtree() const OVERRIDE {
    110     // Send events to the parent view for handling.
    111     return false;
    112   }
    113 
    114  private:
    115   DISALLOW_COPY_AND_ASSIGN(PasswordBox);
    116 };
    117 
    118 PasswordGenerationPopupViewViews::PasswordGenerationPopupViewViews(
    119     PasswordGenerationPopupController* controller,
    120     views::Widget* observing_widget)
    121     : AutofillPopupBaseView(controller, observing_widget),
    122       password_view_(NULL),
    123       font_list_(ResourceBundle::GetSharedInstance().GetFontList(
    124           ResourceBundle::SmallFont)),
    125       controller_(controller) {
    126   if (controller_->display_password())
    127     CreatePasswordView();
    128 
    129   help_label_ = new views::StyledLabel(controller_->HelpText(), this);
    130   help_label_->SetBaseFontList(font_list_);
    131   help_label_->SetLineHeight(20);
    132   views::StyledLabel::RangeStyleInfo default_style;
    133   default_style.color = kExplanatoryTextColor;
    134   help_label_->SetDefaultStyle(default_style);
    135 
    136   views::StyledLabel::RangeStyleInfo link_style =
    137       views::StyledLabel::RangeStyleInfo::CreateForLink();
    138   link_style.disable_line_wrapping = false;
    139   help_label_->AddStyleRange(controller_->HelpTextLinkRange(), link_style);
    140 
    141   help_label_->set_background(
    142       views::Background::CreateSolidBackground(
    143           kExplanatoryTextBackgroundColor));
    144   help_label_->SetBorder(views::Border::CreateEmptyBorder(
    145       PasswordGenerationPopupController::kHelpVerticalPadding -
    146       kHelpVerticalOffset,
    147       PasswordGenerationPopupController::kHorizontalPadding,
    148       PasswordGenerationPopupController::kHelpVerticalPadding -
    149       kHelpVerticalOffset,
    150       PasswordGenerationPopupController::kHorizontalPadding));
    151   AddChildView(help_label_);
    152 
    153   set_background(views::Background::CreateSolidBackground(kPopupBackground));
    154 }
    155 
    156 PasswordGenerationPopupViewViews::~PasswordGenerationPopupViewViews() {}
    157 
    158 void PasswordGenerationPopupViewViews::CreatePasswordView() {
    159   if (password_view_)
    160     return;
    161 
    162   password_view_ = new PasswordBox();
    163   password_view_->Init(controller_->password(),
    164                        controller_->SuggestedText(),
    165                        font_list_);
    166   password_view_->SetPosition(gfx::Point(kPopupBorderThickness,
    167                                          kPopupBorderThickness));
    168   password_view_->SizeToPreferredSize();
    169   AddChildView(password_view_);
    170 }
    171 
    172 gfx::Size PasswordGenerationPopupViewViews::GetPreferredSizeOfPasswordView() {
    173   int height = kPopupBorderThickness;
    174   if (controller_->display_password()) {
    175     // Add divider height as well.
    176     height +=
    177         PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1;
    178   }
    179   int width = controller_->GetMinimumWidth();
    180   int popup_width = width - 2 * kPopupBorderThickness;
    181   height += help_label_->GetHeightForWidth(popup_width);
    182   return gfx::Size(width, height + kPopupBorderThickness);
    183 }
    184 
    185 void PasswordGenerationPopupViewViews::Show() {
    186   DoShow();
    187 }
    188 
    189 void PasswordGenerationPopupViewViews::Hide() {
    190   // The controller is no longer valid after it hides us.
    191   controller_ = NULL;
    192 
    193   DoHide();
    194 }
    195 
    196 void PasswordGenerationPopupViewViews::UpdateBoundsAndRedrawPopup() {
    197   DoUpdateBoundsAndRedrawPopup();
    198 }
    199 
    200 void PasswordGenerationPopupViewViews::PasswordSelectionUpdated() {
    201   if (!password_view_)
    202     return;
    203 
    204   password_view_->set_background(
    205       views::Background::CreateSolidBackground(
    206           controller_->password_selected() ?
    207           kHoveredBackgroundColor :
    208           kPopupBackground));
    209 }
    210 
    211 void PasswordGenerationPopupViewViews::Layout() {
    212   // Need to leave room for the border.
    213   int y = kPopupBorderThickness;
    214   int popup_width = bounds().width() - 2 * kPopupBorderThickness;
    215   if (controller_->display_password()) {
    216     // Currently the UI can change from not offering a password to offering
    217     // a password (e.g. the user is editing a generated password and deletes
    218     // it), but it can't change the other way around.
    219     CreatePasswordView();
    220     password_view_->SetBounds(
    221         kPopupBorderThickness,
    222         y,
    223         popup_width,
    224         PasswordGenerationPopupController::kPopupPasswordSectionHeight);
    225     divider_bounds_ =
    226         gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(),
    227                   popup_width, 1);
    228     y = divider_bounds_.bottom();
    229   }
    230 
    231   help_label_->SetBounds(kPopupBorderThickness, y, popup_width,
    232                          help_label_->GetHeightForWidth(popup_width));
    233 }
    234 
    235 void PasswordGenerationPopupViewViews::OnPaint(gfx::Canvas* canvas) {
    236   if (!controller_)
    237     return;
    238 
    239   // Draw border and background.
    240   views::View::OnPaint(canvas);
    241 
    242   // Divider line needs to be drawn after OnPaint() otherwise the background
    243   // will overwrite the divider.
    244   if (password_view_)
    245     canvas->FillRect(divider_bounds_, kDividerColor);
    246 }
    247 
    248 void PasswordGenerationPopupViewViews::StyledLabelLinkClicked(
    249     const gfx::Range& range, int event_flags) {
    250   controller_->OnSavedPasswordsLinkClicked();
    251 }
    252 
    253 bool PasswordGenerationPopupViewViews::IsPointInPasswordBounds(
    254     const gfx::Point& point) {
    255   if (password_view_)
    256     return password_view_->bounds().Contains(point);
    257   return false;
    258 }
    259 
    260 PasswordGenerationPopupView* PasswordGenerationPopupView::Create(
    261     PasswordGenerationPopupController* controller) {
    262   views::Widget* observing_widget =
    263       views::Widget::GetTopLevelWidgetForNativeView(
    264           controller->container_view());
    265 
    266   // If the top level widget can't be found, cancel the popup since we can't
    267   // fully set it up.
    268   if (!observing_widget)
    269     return NULL;
    270 
    271   return new PasswordGenerationPopupViewViews(controller, observing_widget);
    272 }
    273 
    274 }  // namespace autofill
    275