Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Side by Side Diff: components/autofill/content/renderer/password_autofill_agent.cc

Issue 2906383003: Teach PasswordAutofillAgent sometimes match prefixes of usernames (Closed)
Patch Set: . Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/autofill/content/renderer/password_autofill_agent.h" 5 #include "components/autofill/content/renderer/password_autofill_agent.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <memory> 10 #include <memory>
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 FormInputElementMap unowned_elements_map; 261 FormInputElementMap unowned_elements_map;
262 if (FindFormInputElements(control_elements, data, ambiguous_or_empty_names, 262 if (FindFormInputElements(control_elements, data, ambiguous_or_empty_names,
263 &unowned_elements_map)) 263 &unowned_elements_map))
264 results->push_back(unowned_elements_map); 264 results->push_back(unowned_elements_map);
265 } 265 }
266 266
267 bool IsElementEditable(const blink::WebInputElement& element) { 267 bool IsElementEditable(const blink::WebInputElement& element) {
268 return element.IsEnabled() && !element.IsReadOnly(); 268 return element.IsEnabled() && !element.IsReadOnly();
269 } 269 }
270 270
271 bool DoUsernamesMatch(const base::string16& username1, 271 bool DoUsernamesMatch(const base::string16& potential_suggestion,
272 const base::string16& username2, 272 const base::string16& current_username,
273 bool exact_match) { 273 bool exact_match) {
274 if (exact_match) 274 if (potential_suggestion == current_username)
275 return username1 == username2; 275 return true;
276 return FieldIsSuggestionSubstringStartingOnTokenBoundary(username1, username2, 276 return !exact_match && IsPrefixOfEmailEndingWithAtSign(current_username,
277 true); 277 potential_suggestion);
278 } 278 }
279 279
280 // Returns whether the given |element| is editable. 280 // Returns whether the given |element| is editable.
281 bool IsElementAutocompletable(const blink::WebInputElement& element) { 281 bool IsElementAutocompletable(const blink::WebInputElement& element) {
282 return IsElementEditable(element); 282 return IsElementEditable(element);
283 } 283 }
284 284
285 // Returns whether the |username_element| is allowed to be autofilled. 285 // Returns whether the |username_element| is allowed to be autofilled.
286 // 286 //
287 // Note that if the user interacts with the |password_field| and the 287 // Note that if the user interacts with the |password_field| and the
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 if (value) 366 if (value)
367 it->second.first.reset(new base::string16(*value)); 367 it->second.first.reset(new base::string16(*value));
368 it->second.second |= added_flags; 368 it->second.second |= added_flags;
369 } else { 369 } else {
370 (*field_value_and_properties_map)[element] = std::make_pair( 370 (*field_value_and_properties_map)[element] = std::make_pair(
371 value ? base::MakeUnique<base::string16>(*value) : nullptr, 371 value ? base::MakeUnique<base::string16>(*value) : nullptr,
372 added_flags); 372 added_flags);
373 } 373 }
374 } 374 }
375 375
376 // This function attempts to find the matching credentials for the
377 // |current_username| by scanning |fill_data|. The result is written in
378 // |username| and |password| parameters.
379 void FindMatchesByUsername(const PasswordFormFillData& fill_data,
380 const base::string16& current_username,
381 bool exact_username_match,
382 RendererSavePasswordProgressLogger* logger,
383 base::string16* username,
384 base::string16* password) {
385 // Look for any suitable matches to current field text.
386 if (DoUsernamesMatch(fill_data.username_field.value, current_username,
387 exact_username_match)) {
388 *username = fill_data.username_field.value;
389 *password = fill_data.password_field.value;
390 if (logger)
391 logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
392 } else {
393 // Scan additional logins for a match.
394 for (const auto& it : fill_data.additional_logins) {
395 if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
396 *username = it.first;
397 *password = it.second.password;
398 break;
399 }
400 }
401 if (logger) {
402 logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
403 !(username->empty() && password->empty()));
404 }
405
406 // Check possible usernames.
407 if (username->empty() && password->empty()) {
408 for (const auto& it : fill_data.other_possible_usernames) {
409 for (size_t i = 0; i < it.second.size(); ++i) {
410 if (DoUsernamesMatch(it.second[i], current_username,
411 exact_username_match)) {
412 *username = it.second[i];
413 *password = it.first.password;
414 break;
415 }
416 }
417 if (!password->empty())
418 break;
419 }
420 }
421 }
422 }
423
376 // This function attempts to fill |username_element| and |password_element| 424 // This function attempts to fill |username_element| and |password_element|
377 // with values from |fill_data|. The |password_element| will only have the 425 // with values from |fill_data|. The |password_element| will only have the
378 // suggestedValue set, and will be registered for copying that to the real 426 // suggestedValue set, and will be registered for copying that to the real
379 // value through |registration_callback|. If a match is found, return true and 427 // value through |registration_callback|. If a match is found, return true and
380 // |field_value_and_properties_map| will be modified with the autofilled 428 // |field_value_and_properties_map| will be modified with the autofilled
381 // credentials and |FieldPropertiesFlags::AUTOFILLED| flag. 429 // credentials and |FieldPropertiesFlags::AUTOFILLED| flag.
382 bool FillUserNameAndPassword( 430 bool FillUserNameAndPassword(
383 blink::WebInputElement* username_element, 431 blink::WebInputElement* username_element,
384 blink::WebInputElement* password_element, 432 blink::WebInputElement* password_element,
385 const PasswordFormFillData& fill_data, 433 const PasswordFormFillData& fill_data,
(...skipping 11 matching lines...) Expand all
397 445
398 base::string16 current_username; 446 base::string16 current_username;
399 if (!username_element->IsNull()) { 447 if (!username_element->IsNull()) {
400 current_username = username_element->Value().Utf16(); 448 current_username = username_element->Value().Utf16();
401 } 449 }
402 450
403 // username and password will contain the match found if any. 451 // username and password will contain the match found if any.
404 base::string16 username; 452 base::string16 username;
405 base::string16 password; 453 base::string16 password;
406 454
407 // Look for any suitable matches to current field text. 455 FindMatchesByUsername(fill_data, current_username, exact_username_match,
408 if (DoUsernamesMatch(fill_data.username_field.value, current_username, 456 logger, &username, &password);
409 exact_username_match)) {
410 username = fill_data.username_field.value;
411 password = fill_data.password_field.value;
412 if (logger)
413 logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
414 } else {
415 // Scan additional logins for a match.
416 for (const auto& it : fill_data.additional_logins) {
417 if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
418 username = it.first;
419 password = it.second.password;
420 break;
421 }
422 }
423 if (logger) {
424 logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
425 !(username.empty() && password.empty()));
426 }
427 457
428 // Check possible usernames.
429 if (username.empty() && password.empty()) {
430 for (const auto& it : fill_data.other_possible_usernames) {
431 for (size_t i = 0; i < it.second.size(); ++i) {
432 if (DoUsernamesMatch(
433 it.second[i], current_username, exact_username_match)) {
434 username = it.second[i];
435 password = it.first.password;
436 break;
437 }
438 }
439 if (!username.empty() && !password.empty())
440 break;
441 }
442 }
443 }
444 if (password.empty()) 458 if (password.empty())
445 return false; 459 return false;
446 460
447 // TODO(tkent): Check maxlength and pattern for both username and password 461 // TODO(tkent): Check maxlength and pattern for both username and password
448 // fields. 462 // fields.
449 463
450 // Input matches the username, fill in required values. 464 // Input matches the username, fill in required values.
451 if (!username_element->IsNull() && 465 if (!username_element->IsNull() &&
452 IsElementAutocompletable(*username_element)) { 466 IsElementAutocompletable(*username_element)) {
453 // TODO(crbug.com/507714): Why not setSuggestedValue? 467 // TODO(crbug.com/507714): Why not setSuggestedValue?
454 username_element->SetAutofillValue(blink::WebString::FromUTF16(username)); 468 username_element->SetAutofillValue(blink::WebString::FromUTF16(username));
455 UpdateFieldValueAndPropertiesMaskMap(*username_element, &username, 469 UpdateFieldValueAndPropertiesMaskMap(*username_element, &username,
456 FieldPropertiesFlags::AUTOFILLED, 470 FieldPropertiesFlags::AUTOFILLED,
457 field_value_and_properties_map); 471 field_value_and_properties_map);
458 username_element->SetAutofilled(true); 472 username_element->SetAutofilled(true);
459 if (logger) 473 if (logger)
460 logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element); 474 logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element);
461 if (set_selection) { 475 if (set_selection) {
462 form_util::PreviewSuggestion(username, current_username, 476 form_util::PreviewSuggestion(username, current_username,
463 username_element); 477 username_element);
464 } 478 }
465 } else if (current_username != username) {
466 // If the username can't be filled and it doesn't match a saved password
467 // as is, don't autofill a password.
468 return false;
469 } 479 }
470 480
471 // Wait to fill in the password until a user gesture occurs. This is to make 481 // Wait to fill in the password until a user gesture occurs. This is to make
472 // sure that we do not fill in the DOM with a password until we believe the 482 // sure that we do not fill in the DOM with a password until we believe the
473 // user is intentionally interacting with the page. 483 // user is intentionally interacting with the page.
474 password_element->SetSuggestedValue(blink::WebString::FromUTF16(password)); 484 password_element->SetSuggestedValue(blink::WebString::FromUTF16(password));
475 UpdateFieldValueAndPropertiesMaskMap(*password_element, &password, 485 UpdateFieldValueAndPropertiesMaskMap(*password_element, &password,
476 FieldPropertiesFlags::AUTOFILLED, 486 FieldPropertiesFlags::AUTOFILLED,
477 field_value_and_properties_map); 487 field_value_and_properties_map);
478 registration_callback.Run(password_element); 488 registration_callback.Run(password_element);
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 // In all other cases, do nothing. 553 // In all other cases, do nothing.
544 bool form_has_fillable_username = !username_field_name.empty() && 554 bool form_has_fillable_username = !username_field_name.empty() &&
545 IsElementAutocompletable(username_element); 555 IsElementAutocompletable(username_element);
546 556
547 if (form_has_fillable_username && username_element.Value().IsEmpty()) { 557 if (form_has_fillable_username && username_element.Value().IsEmpty()) {
548 // TODO(tkent): Check maxlength and pattern. 558 // TODO(tkent): Check maxlength and pattern.
549 username_element.SetAutofillValue( 559 username_element.SetAutofillValue(
550 blink::WebString::FromUTF16(fill_data.username_field.value)); 560 blink::WebString::FromUTF16(fill_data.username_field.value));
551 } 561 }
552 562
553 // Fill if we have an exact match for the username. Note that this sets 563 bool exact_username_match =
554 // username to autofilled. 564 username_element.IsNull() || IsElementEditable(username_element);
565 // Use the exact match for the editable username fields and allow prefix
566 // match for read-only username fields.
555 return FillUserNameAndPassword( 567 return FillUserNameAndPassword(
556 &username_element, &password_element, fill_data, 568 &username_element, &password_element, fill_data, exact_username_match,
557 true /* exact_username_match */, false /* set_selection */, 569 false /* set_selection */, field_value_and_properties_map,
558 field_value_and_properties_map, registration_callback, logger); 570 registration_callback, logger);
559 } 571 }
560 572
561 // Annotate |forms| with form and field signatures as HTML attributes. 573 // Annotate |forms| with form and field signatures as HTML attributes.
562 void AnnotateFormsWithSignatures( 574 void AnnotateFormsWithSignatures(
563 blink::WebVector<blink::WebFormElement> forms) { 575 blink::WebVector<blink::WebFormElement> forms) {
564 for (blink::WebFormElement form : forms) { 576 for (blink::WebFormElement form : forms) {
565 std::unique_ptr<PasswordForm> password_form( 577 std::unique_ptr<PasswordForm> password_form(
566 CreatePasswordFormFromWebForm(form, nullptr, nullptr)); 578 CreatePasswordFormFromWebForm(form, nullptr, nullptr));
567 if (password_form) { 579 if (password_form) {
568 form.SetAttribute( 580 form.SetAttribute(
(...skipping 1203 matching lines...) Expand 10 before | Expand all | Expand 10 after
1772 PasswordAutofillAgent::GetPasswordManagerDriver() { 1784 PasswordAutofillAgent::GetPasswordManagerDriver() {
1773 if (!password_manager_driver_) { 1785 if (!password_manager_driver_) {
1774 render_frame()->GetRemoteInterfaces()->GetInterface( 1786 render_frame()->GetRemoteInterfaces()->GetInterface(
1775 mojo::MakeRequest(&password_manager_driver_)); 1787 mojo::MakeRequest(&password_manager_driver_));
1776 } 1788 }
1777 1789
1778 return password_manager_driver_; 1790 return password_manager_driver_;
1779 } 1791 }
1780 1792
1781 } // namespace autofill 1793 } // namespace autofill
OLDNEW
« no previous file with comments | « chrome/renderer/autofill/password_autofill_agent_browsertest.cc ('k') | components/autofill/core/common/autofill_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698