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