Home | History | Annotate | Download | only in views
      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/password_generation_bubble_view.h"
      6 
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "chrome/browser/ui/browser.h"
      9 #include "chrome/browser/ui/browser_list.h"
     10 #include "chrome/common/url_constants.h"
     11 #include "components/autofill/content/common/autofill_messages.h"
     12 #include "components/autofill/core/browser/password_generator.h"
     13 #include "components/autofill/core/common/password_generation_util.h"
     14 #include "components/password_manager/core/browser/password_manager.h"
     15 #include "content/public/browser/render_view_host.h"
     16 #include "grit/generated_resources.h"
     17 #include "grit/theme_resources.h"
     18 #include "third_party/skia/include/core/SkPaint.h"
     19 #include "ui/base/l10n/l10n_util.h"
     20 #include "ui/base/resource/resource_bundle.h"
     21 #include "ui/base/theme_provider.h"
     22 #include "ui/gfx/canvas.h"
     23 #include "ui/views/border.h"
     24 #include "ui/views/controls/button/image_button.h"
     25 #include "ui/views/controls/button/label_button.h"
     26 #include "ui/views/controls/label.h"
     27 #include "ui/views/controls/link.h"
     28 #include "ui/views/controls/textfield/textfield.h"
     29 #include "ui/views/layout/layout_constants.h"
     30 #include "url/gurl.h"
     31 
     32 namespace {
     33 // Constants for PasswordGenerationBubbleView.
     34 const int kBubbleMargin = 9;
     35 const int kButtonHorizontalSpacing = 4;
     36 const int kButtonWidth = 65;
     37 const int kDefaultTextFieldChars = 18;
     38 const int kTitleLabelVerticalOffset = -1;
     39 const int kVerticalPadding = 8;
     40 
     41 // Constants for Text fieldWrapper.
     42 const int kTextfieldHorizontalPadding = 2;
     43 const int kTextfieldVerticalPadding = 3;
     44 const int kWrapperBorderSize = 1;
     45 
     46 // This class handles layout so that it looks like a Textfield and ImageButton
     47 // are part of one logical textfield with the button on the right side of the
     48 // field. It also assumes that the textfield is already sized appropriately
     49 // and will alter the image size to fit.
     50 class TextfieldWrapper : public views::View {
     51  public:
     52   TextfieldWrapper(views::Textfield* textfield,
     53                    views::ImageButton* image_button);
     54   virtual ~TextfieldWrapper();
     55 
     56   virtual void Layout() OVERRIDE;
     57   virtual gfx::Size GetPreferredSize() const OVERRIDE;
     58 
     59  private:
     60   gfx::Size GetImageSize() const;
     61 
     62   views::Textfield* textfield_;
     63   views::ImageButton* image_button_;
     64 };
     65 
     66 TextfieldWrapper::TextfieldWrapper(views::Textfield* textfield,
     67                                    views::ImageButton* image_button)
     68     : textfield_(textfield),
     69       image_button_(image_button) {
     70   SetBorder(views::Border::CreateSolidBorder(kWrapperBorderSize, SK_ColorGRAY));
     71 
     72   AddChildView(textfield_);
     73   AddChildView(image_button);
     74 }
     75 
     76 TextfieldWrapper::~TextfieldWrapper() {}
     77 
     78 void TextfieldWrapper::Layout() {
     79   // Add some spacing between the textfield and the border.
     80   textfield_->SetPosition(gfx::Point(kTextfieldHorizontalPadding,
     81                                      kTextfieldVerticalPadding));
     82   textfield_->SizeToPreferredSize();
     83 
     84   // Button should be offset one pixel from the end of the textfield so that
     85   // there is no overlap. It is also displaced down by the size of the border
     86   // so it doesn't overlap with it either.
     87   int button_x = (textfield_->GetPreferredSize().width() +
     88                   kTextfieldHorizontalPadding + 1);
     89   image_button_->SetPosition(gfx::Point(button_x,
     90                                         kWrapperBorderSize));
     91 
     92   // Make sure that the image is centered after cropping.
     93   image_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
     94                                    views::ImageButton::ALIGN_MIDDLE);
     95 
     96   image_button_->SetSize(GetImageSize());
     97 }
     98 
     99 gfx::Size TextfieldWrapper::GetPreferredSize() const {
    100   int width = (textfield_->GetPreferredSize().width() +
    101                GetImageSize().width() +
    102                kTextfieldHorizontalPadding * 3);
    103   int height = (textfield_->GetPreferredSize().height() +
    104                 kTextfieldVerticalPadding * 2);
    105 
    106   return gfx::Size(width, height);
    107 }
    108 
    109 gfx::Size TextfieldWrapper::GetImageSize() const {
    110   // The image is sized so that it fills the space between the borders
    111   // completely.
    112   int size = (textfield_->GetPreferredSize().height() +
    113               (kTextfieldVerticalPadding - kWrapperBorderSize) * 2);
    114   return gfx::Size(size, size);
    115 }
    116 }  // namespace
    117 
    118 PasswordGenerationBubbleView::PasswordGenerationBubbleView(
    119     const autofill::PasswordForm& form,
    120     const gfx::Rect& anchor_rect,
    121     views::View* anchor_view,
    122     content::RenderViewHost* render_view_host,
    123     password_manager::PasswordManager* password_manager,
    124     autofill::PasswordGenerator* password_generator,
    125     ui::ThemeProvider* theme_provider)
    126     : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT),
    127       title_label_(NULL),
    128       accept_button_(NULL),
    129       textfield_(NULL),
    130       regenerate_button_(NULL),
    131       textfield_wrapper_(NULL),
    132       form_(form),
    133       anchor_rect_(anchor_rect),
    134       render_view_host_(render_view_host),
    135       password_manager_(password_manager),
    136       password_generator_(password_generator),
    137       theme_provider_(theme_provider) {}
    138 
    139 PasswordGenerationBubbleView::~PasswordGenerationBubbleView() {}
    140 
    141 void PasswordGenerationBubbleView::Init() {
    142   set_margins(gfx::Insets(kBubbleMargin, kBubbleMargin,
    143                           kBubbleMargin, kBubbleMargin));
    144 
    145   // TODO(gcasto): Localize text after we have finalized the UI.
    146   // crbug.com/118062.
    147   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    148   title_label_ = new views::Label(
    149       l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_BUBBLE_TITLE),
    150       rb.GetFontList(ui::ResourceBundle::MediumFont));
    151   AddChildView(title_label_);
    152 
    153   regenerate_button_ = new views::ImageButton(this);
    154   regenerate_button_->SetImage(
    155       views::CustomButton::STATE_NORMAL,
    156       theme_provider_->GetImageSkiaNamed(IDR_RELOAD_DIMMED));
    157   regenerate_button_->SetImage(
    158       views::CustomButton::STATE_HOVERED,
    159       theme_provider_->GetImageSkiaNamed(IDR_RELOAD));
    160   regenerate_button_->SetImage(
    161       views::CustomButton::STATE_PRESSED,
    162       theme_provider_->GetImageSkiaNamed(IDR_RELOAD));
    163 
    164   textfield_ = new views::Textfield();
    165   textfield_->set_default_width_in_chars(kDefaultTextFieldChars);
    166   textfield_->SetText(base::ASCIIToUTF16(password_generator_->Generate()));
    167   textfield_->set_controller(this);
    168 
    169   textfield_wrapper_ = new TextfieldWrapper(textfield_,
    170                                             regenerate_button_);
    171   AddChildView(textfield_wrapper_);
    172 
    173   accept_button_ = new views::LabelButton(
    174       this,
    175       l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_BUTTON_TEXT));
    176   accept_button_->SetStyle(views::Button::STYLE_BUTTON);
    177   AddChildView(accept_button_);
    178 }
    179 
    180 void PasswordGenerationBubbleView::Layout() {
    181   // We have the title label shifted up to make the borders look more uniform.
    182   title_label_->SetPosition(gfx::Point(0, kTitleLabelVerticalOffset));
    183   title_label_->SizeToPreferredSize();
    184 
    185   int y = title_label_->GetPreferredSize().height() + kVerticalPadding;
    186 
    187   textfield_wrapper_->SetPosition(gfx::Point(0, y));
    188   textfield_wrapper_->SizeToPreferredSize();
    189 
    190   int button_x = (textfield_wrapper_->GetPreferredSize().width() +
    191                   kButtonHorizontalSpacing);
    192   accept_button_->SetBounds(
    193       button_x,
    194       y - kWrapperBorderSize,
    195       kButtonWidth,
    196       textfield_wrapper_->GetPreferredSize().height() + kWrapperBorderSize * 2);
    197 }
    198 
    199 gfx::Size PasswordGenerationBubbleView::GetPreferredSize() const {
    200   int width = (textfield_wrapper_->GetPreferredSize().width() +
    201                kButtonHorizontalSpacing +
    202                kButtonWidth - 1);
    203   int height = (title_label_->GetPreferredSize().height() +
    204                 textfield_wrapper_->GetPreferredSize().height() +
    205                 kVerticalPadding);
    206   return gfx::Size(width, height);
    207 }
    208 
    209 gfx::Rect PasswordGenerationBubbleView::GetAnchorRect() const {
    210   return anchor_rect_;
    211 }
    212 
    213 void PasswordGenerationBubbleView::ButtonPressed(views::Button* sender,
    214                                                  const ui::Event& event) {
    215   if (sender == accept_button_) {
    216     render_view_host_->Send(new AutofillMsg_GeneratedPasswordAccepted(
    217         render_view_host_->GetRoutingID(), textfield_->text()));
    218     password_manager_->SetFormHasGeneratedPassword(form_);
    219     actions_.password_accepted = true;
    220     GetWidget()->Close();
    221   }
    222   if (sender == regenerate_button_) {
    223     textfield_->SetText(
    224         base::ASCIIToUTF16(password_generator_->Generate()));
    225     actions_.password_regenerated = true;
    226   }
    227 }
    228 
    229 void PasswordGenerationBubbleView::ContentsChanged(
    230     views::Textfield* sender,
    231     const base::string16& contents) {
    232   actions_.password_edited = true;
    233 }
    234 
    235 bool PasswordGenerationBubbleView::HandleKeyEvent(
    236     views::Textfield* sender,
    237     const ui::KeyEvent& key_event) {
    238   return false;
    239 }
    240 
    241 views::View* PasswordGenerationBubbleView::GetInitiallyFocusedView() {
    242   return textfield_;
    243 }
    244 
    245 void PasswordGenerationBubbleView::WindowClosing() {
    246   autofill::password_generation::LogUserActions(actions_);
    247 }
    248