password_generation_popup_controller_impl.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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/autofill/password_generation_popup_controller_impl.h"
6
7#include <math.h>
8
9#include "base/strings/string_split.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversion_utils.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/ui/autofill/password_generation_popup_observer.h"
14#include "chrome/browser/ui/autofill/password_generation_popup_view.h"
15#include "chrome/browser/ui/autofill/popup_constants.h"
16#include "chrome/browser/ui/browser.h"
17#include "chrome/browser/ui/browser_finder.h"
18#include "chrome/common/url_constants.h"
19#include "components/autofill/content/common/autofill_messages.h"
20#include "components/autofill/core/browser/password_generator.h"
21#include "components/password_manager/core/browser/password_manager.h"
22#include "content/public/browser/native_web_keyboard_event.h"
23#include "content/public/browser/render_view_host.h"
24#include "content/public/browser/web_contents.h"
25#include "grit/chromium_strings.h"
26#include "grit/generated_resources.h"
27#include "grit/google_chrome_strings.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/resource/resource_bundle.h"
30#include "ui/events/keycodes/keyboard_codes.h"
31#include "ui/gfx/rect_conversions.h"
32#include "ui/gfx/text_utils.h"
33
34namespace autofill {
35
36base::WeakPtr<PasswordGenerationPopupControllerImpl>
37PasswordGenerationPopupControllerImpl::GetOrCreate(
38    base::WeakPtr<PasswordGenerationPopupControllerImpl> previous,
39    const gfx::RectF& bounds,
40    const PasswordForm& form,
41    int max_length,
42    password_manager::PasswordManager* password_manager,
43    PasswordGenerationPopupObserver* observer,
44    content::WebContents* web_contents,
45    gfx::NativeView container_view) {
46  if (previous.get() &&
47      previous->element_bounds() == bounds &&
48      previous->web_contents() == web_contents &&
49      previous->container_view() == container_view) {
50    return previous;
51  }
52
53  if (previous.get())
54    previous->Hide();
55
56  PasswordGenerationPopupControllerImpl* controller =
57      new PasswordGenerationPopupControllerImpl(
58          bounds,
59          form,
60          max_length,
61          password_manager,
62          observer,
63          web_contents,
64          container_view);
65  return controller->GetWeakPtr();
66}
67
68PasswordGenerationPopupControllerImpl::PasswordGenerationPopupControllerImpl(
69    const gfx::RectF& bounds,
70    const PasswordForm& form,
71    int max_length,
72    password_manager::PasswordManager* password_manager,
73    PasswordGenerationPopupObserver* observer,
74    content::WebContents* web_contents,
75    gfx::NativeView container_view)
76    : form_(form),
77      password_manager_(password_manager),
78      observer_(observer),
79      generator_(new PasswordGenerator(max_length)),
80      controller_common_(bounds, container_view, web_contents),
81      view_(NULL),
82      password_selected_(false),
83      display_password_(false),
84      weak_ptr_factory_(this) {
85  controller_common_.SetKeyPressCallback(
86      base::Bind(&PasswordGenerationPopupControllerImpl::HandleKeyPressEvent,
87                 base::Unretained(this)));
88
89  std::vector<base::string16> pieces;
90  base::SplitStringDontTrim(
91      l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_PROMPT),
92      '|',  // separator
93      &pieces);
94  DCHECK_EQ(3u, pieces.size());
95  link_range_ = gfx::Range(pieces[0].size(),
96                           pieces[0].size() + pieces[1].size());
97  help_text_ = JoinString(pieces, base::string16());
98}
99
100PasswordGenerationPopupControllerImpl::~PasswordGenerationPopupControllerImpl()
101  {}
102
103base::WeakPtr<PasswordGenerationPopupControllerImpl>
104PasswordGenerationPopupControllerImpl::GetWeakPtr() {
105  return weak_ptr_factory_.GetWeakPtr();
106}
107
108bool PasswordGenerationPopupControllerImpl::HandleKeyPressEvent(
109    const content::NativeWebKeyboardEvent& event) {
110  switch (event.windowsKeyCode) {
111    case ui::VKEY_UP:
112    case ui::VKEY_DOWN:
113      PasswordSelected(true);
114      return true;
115    case ui::VKEY_ESCAPE:
116      Hide();
117      return true;
118    case ui::VKEY_RETURN:
119    case ui::VKEY_TAB:
120      // We suppress tab if the password is selected because we will
121      // automatically advance focus anyway.
122      return PossiblyAcceptPassword();
123    default:
124      return false;
125  }
126}
127
128bool PasswordGenerationPopupControllerImpl::PossiblyAcceptPassword() {
129  if (password_selected_) {
130    PasswordAccepted();  // This will delete |this|.
131    return true;
132  }
133
134  return false;
135}
136
137void PasswordGenerationPopupControllerImpl::PasswordSelected(bool selected) {
138  if (!display_password_ || selected == password_selected_)
139    return;
140
141  password_selected_ = selected;
142  view_->PasswordSelectionUpdated();
143  view_->UpdateBoundsAndRedrawPopup();
144}
145
146void PasswordGenerationPopupControllerImpl::PasswordAccepted() {
147  if (!display_password_)
148    return;
149
150  web_contents()->GetRenderViewHost()->Send(
151      new AutofillMsg_GeneratedPasswordAccepted(
152          web_contents()->GetRenderViewHost()->GetRoutingID(),
153          current_password_));
154  password_manager_->SetFormHasGeneratedPassword(form_);
155  Hide();
156}
157
158int PasswordGenerationPopupControllerImpl::GetMinimumWidth() {
159  // Minimum width in pixels.
160  const int minimum_width = 360;
161
162  // If the width of the field is longer than the minimum, use that instead.
163  return std::max(minimum_width,
164                  controller_common_.RoundedElementBounds().width());
165}
166
167void PasswordGenerationPopupControllerImpl::CalculateBounds() {
168  gfx::Size bounds = view_->GetPreferredSizeOfPasswordView();
169
170  popup_bounds_ = controller_common_.GetPopupBounds(bounds.width(),
171                                                    bounds.height());
172}
173
174void PasswordGenerationPopupControllerImpl::Show(bool display_password) {
175  display_password_ = display_password;
176  if (display_password_)
177    current_password_ = base::ASCIIToUTF16(generator_->Generate());
178
179  if (!view_) {
180    view_ = PasswordGenerationPopupView::Create(this);
181    CalculateBounds();
182    view_->Show();
183  } else {
184    CalculateBounds();
185    view_->UpdateBoundsAndRedrawPopup();
186  }
187
188  controller_common_.RegisterKeyPressCallback();
189
190  if (observer_)
191    observer_->OnPopupShown(display_password_);
192}
193
194void PasswordGenerationPopupControllerImpl::HideAndDestroy() {
195  Hide();
196}
197
198void PasswordGenerationPopupControllerImpl::Hide() {
199  controller_common_.RemoveKeyPressCallback();
200
201  if (view_)
202    view_->Hide();
203
204  if (observer_)
205    observer_->OnPopupHidden();
206
207  delete this;
208}
209
210void PasswordGenerationPopupControllerImpl::ViewDestroyed() {
211  view_ = NULL;
212
213  Hide();
214}
215
216void PasswordGenerationPopupControllerImpl::OnSavedPasswordsLinkClicked() {
217  // TODO(gcasto): Change this to navigate to account central once passwords
218  // are visible there.
219  Browser* browser =
220      chrome::FindBrowserWithWebContents(controller_common_.web_contents());
221  content::OpenURLParams params(
222      GURL(chrome::kAutoPasswordGenerationLearnMoreURL), content::Referrer(),
223      NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false);
224  browser->OpenURL(params);
225}
226
227void PasswordGenerationPopupControllerImpl::SetSelectionAtPoint(
228    const gfx::Point& point) {
229  PasswordSelected(view_->IsPointInPasswordBounds(point));
230}
231
232bool PasswordGenerationPopupControllerImpl::AcceptSelectedLine() {
233  if (!password_selected_)
234    return false;
235
236  PasswordAccepted();
237  return true;
238}
239
240void PasswordGenerationPopupControllerImpl::SelectionCleared() {
241  PasswordSelected(false);
242}
243
244gfx::NativeView PasswordGenerationPopupControllerImpl::container_view() {
245  return controller_common_.container_view();
246}
247
248const gfx::Rect& PasswordGenerationPopupControllerImpl::popup_bounds() const {
249  return popup_bounds_;
250}
251
252bool PasswordGenerationPopupControllerImpl::display_password() const {
253  return display_password_;
254}
255
256bool PasswordGenerationPopupControllerImpl::password_selected() const {
257  return password_selected_;
258}
259
260base::string16 PasswordGenerationPopupControllerImpl::password() const {
261  return current_password_;
262}
263
264base::string16 PasswordGenerationPopupControllerImpl::SuggestedText() {
265  return l10n_util::GetStringUTF16(IDS_PASSWORD_GENERATION_SUGGESTION);
266}
267
268const base::string16& PasswordGenerationPopupControllerImpl::HelpText() {
269  return help_text_;
270}
271
272const gfx::Range& PasswordGenerationPopupControllerImpl::HelpTextLinkRange() {
273  return link_range_;
274}
275
276}  // namespace autofill
277