ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / components / autofill / content / renderer / password_generation_agent.cc
blob8c24048c876cc8473597b9c2a90caba8477dcd16
1 // Copyright 2013 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.
5 #include "components/autofill/content/renderer/password_generation_agent.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "components/autofill/content/common/autofill_messages.h"
11 #include "components/autofill/content/renderer/form_autofill_util.h"
12 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
13 #include "components/autofill/core/common/autofill_switches.h"
14 #include "components/autofill/core/common/form_data.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/autofill/core/common/password_generation_util.h"
17 #include "content/public/renderer/render_frame.h"
18 #include "content/public/renderer/render_view.h"
19 #include "google_apis/gaia/gaia_urls.h"
20 #include "third_party/WebKit/public/platform/WebVector.h"
21 #include "third_party/WebKit/public/web/WebDocument.h"
22 #include "third_party/WebKit/public/web/WebFormElement.h"
23 #include "third_party/WebKit/public/web/WebInputElement.h"
24 #include "third_party/WebKit/public/web/WebLocalFrame.h"
25 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
26 #include "third_party/WebKit/public/web/WebView.h"
27 #include "ui/gfx/geometry/rect.h"
29 namespace autofill {
31 namespace {
33 // Returns true if we think that this form is for account creation. |passwords|
34 // is filled with the password field(s) in the form.
35 bool GetAccountCreationPasswordFields(
36 const blink::WebFormElement& form,
37 std::vector<blink::WebInputElement>* passwords) {
38 // Grab all of the passwords for the form.
39 blink::WebVector<blink::WebFormControlElement> control_elements;
40 form.getFormControlElements(control_elements);
42 size_t num_input_elements = 0;
43 for (size_t i = 0; i < control_elements.size(); i++) {
44 blink::WebInputElement* input_element =
45 toWebInputElement(&control_elements[i]);
46 if (input_element && input_element->isTextField()) {
47 num_input_elements++;
48 if (input_element->isPasswordField())
49 passwords->push_back(*input_element);
53 // This may be too lenient, but we assume that any form with at least three
54 // input elements where at least one of them is a password is an account
55 // creation form.
56 if (!passwords->empty() && num_input_elements >= 3) {
57 // We trim |passwords| because occasionally there are forms where the
58 // security question answers are put in password fields and we don't want
59 // to fill those.
60 if (passwords->size() > 2)
61 passwords->resize(2);
63 return true;
66 return false;
69 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
70 return std::find(urls.begin(), urls.end(), url) != urls.end();
73 bool ContainsForm(const std::vector<autofill::FormData>& forms,
74 const PasswordForm& form) {
75 for (std::vector<autofill::FormData>::const_iterator it =
76 forms.begin(); it != forms.end(); ++it) {
77 if (it->SameFormAs(form.form_data))
78 return true;
80 return false;
83 void CopyValueToAllInputElements(
84 const blink::WebString value,
85 std::vector<blink::WebInputElement>* elements) {
86 for (std::vector<blink::WebInputElement>::iterator it = elements->begin();
87 it != elements->end(); ++it) {
88 it->setValue(value, true /* sendEvents */);
92 } // namespace
94 PasswordGenerationAgent::AccountCreationFormData::AccountCreationFormData(
95 linked_ptr<PasswordForm> password_form,
96 std::vector<blink::WebInputElement> passwords)
97 : form(password_form),
98 password_elements(passwords) {}
100 PasswordGenerationAgent::AccountCreationFormData::~AccountCreationFormData() {}
102 PasswordGenerationAgent::PasswordGenerationAgent(
103 content::RenderFrame* render_frame)
104 : content::RenderFrameObserver(render_frame),
105 password_is_generated_(false),
106 password_edited_(false),
107 generation_popup_shown_(false),
108 editing_popup_shown_(false),
109 enabled_(password_generation::IsPasswordGenerationEnabled()) {
110 VLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled");
112 PasswordGenerationAgent::~PasswordGenerationAgent() {}
114 void PasswordGenerationAgent::DidFinishDocumentLoad() {
115 if (render_frame()->GetWebFrame()->parent())
116 return;
118 // In every navigation, the IPC message sent by the password autofill manager
119 // to query whether the current form is blacklisted or not happens when the
120 // document load finishes, so we need to clear previous states here before we
121 // hear back from the browser. We only clear this state on main frame load
122 // as we don't want subframe loads to clear state that we have received from
123 // the main frame. Note that we assume there is only one account creation
124 // form, but there could be multiple password forms in each frame.
125 not_blacklisted_password_form_origins_.clear();
126 generation_enabled_forms_.clear();
127 generation_element_.reset();
128 possible_account_creation_forms_.clear();
130 // Log statistics after navigation so that we only log once per page.
131 if (generation_form_data_ &&
132 generation_form_data_->password_elements.empty()) {
133 password_generation::LogPasswordGenerationEvent(
134 password_generation::NO_SIGN_UP_DETECTED);
135 } else {
136 password_generation::LogPasswordGenerationEvent(
137 password_generation::SIGN_UP_DETECTED);
139 generation_form_data_.reset();
140 password_is_generated_ = false;
141 if (password_edited_) {
142 password_generation::LogPasswordGenerationEvent(
143 password_generation::PASSWORD_EDITED);
145 password_edited_ = false;
147 if (generation_popup_shown_) {
148 password_generation::LogPasswordGenerationEvent(
149 password_generation::GENERATION_POPUP_SHOWN);
151 generation_popup_shown_ = false;
153 if (editing_popup_shown_) {
154 password_generation::LogPasswordGenerationEvent(
155 password_generation::EDITING_POPUP_SHOWN);
157 editing_popup_shown_ = false;
159 FindPossibleGenerationForm();
162 void PasswordGenerationAgent::OnDynamicFormsSeen() {
163 FindPossibleGenerationForm();
166 void PasswordGenerationAgent::FindPossibleGenerationForm() {
167 if (!enabled_)
168 return;
170 // We don't want to generate passwords if the browser won't store or sync
171 // them.
172 if (!ShouldAnalyzeDocument())
173 return;
175 // If we have already found a signup form for this page, no need to continue.
176 if (generation_form_data_)
177 return;
179 blink::WebVector<blink::WebFormElement> forms;
180 render_frame()->GetWebFrame()->document().forms(forms);
181 for (size_t i = 0; i < forms.size(); ++i) {
182 if (forms[i].isNull())
183 continue;
185 // If we can't get a valid PasswordForm, we skip this form because the
186 // the password won't get saved even if we generate it.
187 scoped_ptr<PasswordForm> password_form(
188 CreatePasswordForm(forms[i], nullptr));
189 if (!password_form.get()) {
190 VLOG(2) << "Skipping form as it would not be saved";
191 continue;
194 // Do not generate password for GAIA since it is used to retrieve the
195 // generated paswords.
196 GURL realm(password_form->signon_realm);
197 if (realm == GaiaUrls::GetInstance()->gaia_login_form_realm())
198 continue;
200 std::vector<blink::WebInputElement> passwords;
201 if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
202 AccountCreationFormData ac_form_data(
203 make_linked_ptr(password_form.release()), passwords);
204 possible_account_creation_forms_.push_back(ac_form_data);
208 if (!possible_account_creation_forms_.empty()) {
209 VLOG(2) << possible_account_creation_forms_.size()
210 << " possible account creation forms deteceted";
211 DetermineGenerationElement();
215 bool PasswordGenerationAgent::ShouldAnalyzeDocument() const {
216 // Make sure that this security origin is allowed to use password manager.
217 // Generating a password that can't be saved is a bad idea.
218 blink::WebSecurityOrigin origin =
219 render_frame()->GetWebFrame()->document().securityOrigin();
220 if (!origin.canAccessPasswordManager()) {
221 VLOG(1) << "No PasswordManager access";
222 return false;
225 return true;
228 bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) {
229 bool handled = true;
230 IPC_BEGIN_MESSAGE_MAP(PasswordGenerationAgent, message)
231 IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted,
232 OnFormNotBlacklisted)
233 IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted,
234 OnPasswordAccepted)
235 IPC_MESSAGE_HANDLER(AutofillMsg_AccountCreationFormsDetected,
236 OnAccountCreationFormsDetected)
237 IPC_MESSAGE_UNHANDLED(handled = false)
238 IPC_END_MESSAGE_MAP()
239 return handled;
242 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
243 not_blacklisted_password_form_origins_.push_back(form.origin);
244 DetermineGenerationElement();
247 void PasswordGenerationAgent::OnPasswordAccepted(
248 const base::string16& password) {
249 password_is_generated_ = true;
250 password_generation::LogPasswordGenerationEvent(
251 password_generation::PASSWORD_ACCEPTED);
252 for (auto& password_element : generation_form_data_->password_elements) {
253 password_element.setValue(password, true /* sendEvents */);
254 password_element.setAutofilled(true);
255 // Advance focus to the next input field. We assume password fields in
256 // an account creation form are always adjacent.
257 render_frame()->GetRenderView()->GetWebView()->advanceFocus(false);
261 void PasswordGenerationAgent::OnAccountCreationFormsDetected(
262 const std::vector<autofill::FormData>& forms) {
263 generation_enabled_forms_.insert(
264 generation_enabled_forms_.end(), forms.begin(), forms.end());
265 DetermineGenerationElement();
268 void PasswordGenerationAgent::DetermineGenerationElement() {
269 if (generation_form_data_) {
270 VLOG(2) << "Account creation form already found";
271 return;
274 // Make sure local heuristics have identified a possible account creation
275 // form.
276 if (possible_account_creation_forms_.empty()) {
277 VLOG(2) << "Local hueristics have not detected a possible account "
278 << "creation form";
279 return;
282 for (auto& possible_form_data : possible_account_creation_forms_) {
283 PasswordForm* possible_password_form = possible_form_data.form.get();
284 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
285 switches::kLocalHeuristicsOnlyForPasswordGeneration)) {
286 VLOG(2) << "Bypassing additional checks.";
287 } else if (!ContainsURL(not_blacklisted_password_form_origins_,
288 possible_password_form->origin)) {
289 VLOG(2) << "Have not received confirmation that password form isn't "
290 << "blacklisted";
291 continue;
292 } else if (!ContainsForm(generation_enabled_forms_,
293 *possible_password_form)) {
294 // Note that this message will never be sent if this feature is disabled
295 // (e.g. Password saving is disabled).
296 VLOG(2) << "Have not received confirmation from Autofill that form is "
297 << "used for account creation";
298 continue;
301 VLOG(2) << "Password generation eligible form found";
302 generation_form_data_.reset(
303 new AccountCreationFormData(possible_form_data.form,
304 possible_form_data.password_elements));
305 generation_element_ = generation_form_data_->password_elements[0];
306 generation_element_.setAttribute("aria-autocomplete", "list");
307 password_generation::LogPasswordGenerationEvent(
308 password_generation::GENERATION_AVAILABLE);
309 possible_account_creation_forms_.clear();
310 return;
314 bool PasswordGenerationAgent::FocusedNodeHasChanged(
315 const blink::WebNode& node) {
316 if (!generation_element_.isNull())
317 generation_element_.setShouldRevealPassword(false);
319 if (node.isNull() || !node.isElementNode())
320 return false;
322 const blink::WebElement web_element = node.toConst<blink::WebElement>();
323 if (!web_element.document().frame())
324 return false;
326 const blink::WebInputElement* element = toWebInputElement(&web_element);
327 if (!element || *element != generation_element_)
328 return false;
330 if (password_is_generated_) {
331 generation_element_.setShouldRevealPassword(true);
332 ShowEditingPopup();
333 return true;
336 // Assume that if the password field has less than kMaximumOfferSize
337 // characters then the user is not finished typing their password and display
338 // the password suggestion.
339 if (!element->isReadOnly() &&
340 element->isEnabled() &&
341 element->value().length() <= kMaximumOfferSize) {
342 ShowGenerationPopup();
343 return true;
346 return false;
349 bool PasswordGenerationAgent::TextDidChangeInTextField(
350 const blink::WebInputElement& element) {
351 if (element != generation_element_)
352 return false;
354 if (element.value().isEmpty()) {
355 if (password_is_generated_) {
356 // User generated a password and then deleted it.
357 password_generation::LogPasswordGenerationEvent(
358 password_generation::PASSWORD_DELETED);
359 CopyValueToAllInputElements(element.value(),
360 &generation_form_data_->password_elements);
363 // Do not treat the password as generated.
364 // TODO(gcasto): Set PasswordForm::type in the browser to TYPE_NORMAL.
365 password_is_generated_ = false;
366 generation_element_.setShouldRevealPassword(false);
368 // Offer generation again.
369 ShowGenerationPopup();
370 } else if (password_is_generated_) {
371 password_edited_ = true;
372 // Mirror edits to any confirmation password fields.
373 CopyValueToAllInputElements(element.value(),
374 &generation_form_data_->password_elements);
375 } else if (element.value().length() > kMaximumOfferSize) {
376 // User has rejected the feature and has started typing a password.
377 HidePopup();
378 } else {
379 // Password isn't generated and there are fewer than kMaximumOfferSize
380 // characters typed, so keep offering the password. Note this function
381 // will just keep the previous popup if one is already showing.
382 ShowGenerationPopup();
385 return true;
388 void PasswordGenerationAgent::ShowGenerationPopup() {
389 gfx::RectF bounding_box_scaled = GetScaledBoundingBox(
390 render_frame()->GetRenderView()->GetWebView()->pageScaleFactor(),
391 &generation_element_);
393 Send(new AutofillHostMsg_ShowPasswordGenerationPopup(
394 routing_id(),
395 bounding_box_scaled,
396 generation_element_.maxLength(),
397 *generation_form_data_->form));
399 generation_popup_shown_ = true;
402 void PasswordGenerationAgent::ShowEditingPopup() {
403 gfx::RectF bounding_box_scaled = GetScaledBoundingBox(
404 render_frame()->GetRenderView()->GetWebView()->pageScaleFactor(),
405 &generation_element_);
407 Send(new AutofillHostMsg_ShowPasswordEditingPopup(
408 routing_id(),
409 bounding_box_scaled,
410 *generation_form_data_->form));
412 editing_popup_shown_ = true;
415 void PasswordGenerationAgent::HidePopup() {
416 Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
419 } // namespace autofill