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

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

Issue 2906383003: Teach PasswordAutofillAgent sometimes match prefixes of usernames (Closed)
Patch Set: dvadym@ comments 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 bool match = (potential_suggestion == current_username);
vabr (Chromium) 2017/06/12 18:56:58 optional nit: I am a bit concerned that we have tw
melandory 2017/06/19 10:53:48 Done.
275 return username1 == username2; 275 return exact_match ? match
276 return FieldIsSuggestionSubstringStartingOnTokenBoundary(username1, username2, 276 : (match || IsPrefixEndingOnTokenBoundary(
277 true); 277 current_username, 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 void FindMatchesByUsername(const PasswordFormFillData& fill_data,
vabr (Chromium) 2017/06/12 18:56:58 nit: Please comment on what the function does and
melandory 2017/06/19 10:53:48 Done.
377 const base::string16& current_username,
378 bool exact_username_match,
379 RendererSavePasswordProgressLogger* logger,
380 base::string16* username,
381 base::string16* password) {
382 // Look for any suitable matches to current field text.
383 if (DoUsernamesMatch(fill_data.username_field.value, current_username,
384 exact_username_match)) {
385 *username = fill_data.username_field.value;
386 *password = fill_data.password_field.value;
387 if (logger)
388 logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
389 } else {
390 // Scan additional logins for a match.
391 for (const auto& it : fill_data.additional_logins) {
392 if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
393 *username = it.first;
394 *password = it.second.password;
395 break;
396 }
397 }
398 if (logger) {
399 logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
400 !(username->empty() && password->empty()));
401 }
402
403 // Check possible usernames.
404 if (username->empty() && password->empty()) {
405 for (const auto& it : fill_data.other_possible_usernames) {
406 for (size_t i = 0; i < it.second.size(); ++i) {
407 if (DoUsernamesMatch(it.second[i], current_username,
408 exact_username_match)) {
409 *username = it.second[i];
410 *password = it.first.password;
411 break;
412 }
413 }
414 if (!username->empty() && !password->empty())
vabr (Chromium) 2017/06/12 18:56:58 I know that this is just the old code moved around
melandory 2017/06/19 10:53:48 Done.
415 break;
416 }
417 }
418 }
419 }
420
376 // This function attempts to fill |username_element| and |password_element| 421 // This function attempts to fill |username_element| and |password_element|
377 // with values from |fill_data|. The |password_element| will only have the 422 // 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 423 // 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 424 // value through |registration_callback|. If a match is found, return true and
380 // |field_value_and_properties_map| will be modified with the autofilled 425 // |field_value_and_properties_map| will be modified with the autofilled
381 // credentials and |FieldPropertiesFlags::AUTOFILLED| flag. 426 // credentials and |FieldPropertiesFlags::AUTOFILLED| flag.
382 bool FillUserNameAndPassword( 427 bool FillUserNameAndPassword(
383 blink::WebInputElement* username_element, 428 blink::WebInputElement* username_element,
384 blink::WebInputElement* password_element, 429 blink::WebInputElement* password_element,
385 const PasswordFormFillData& fill_data, 430 const PasswordFormFillData& fill_data,
(...skipping 11 matching lines...) Expand all
397 442
398 base::string16 current_username; 443 base::string16 current_username;
399 if (!username_element->IsNull()) { 444 if (!username_element->IsNull()) {
400 current_username = username_element->Value().Utf16(); 445 current_username = username_element->Value().Utf16();
401 } 446 }
402 447
403 // username and password will contain the match found if any. 448 // username and password will contain the match found if any.
404 base::string16 username; 449 base::string16 username;
405 base::string16 password; 450 base::string16 password;
406 451
407 // Look for any suitable matches to current field text. 452 FindMatchesByUsername(fill_data, current_username, exact_username_match,
408 if (DoUsernamesMatch(fill_data.username_field.value, current_username, 453 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 454
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()) 455 if (password.empty())
445 return false; 456 return false;
446 457
447 // TODO(tkent): Check maxlength and pattern for both username and password 458 // TODO(tkent): Check maxlength and pattern for both username and password
448 // fields. 459 // fields.
449 460
450 // Input matches the username, fill in required values. 461 // Input matches the username, fill in required values.
451 if (!username_element->IsNull() && 462 if (!username_element->IsNull() &&
452 IsElementAutocompletable(*username_element)) { 463 IsElementAutocompletable(*username_element)) {
453 // TODO(crbug.com/507714): Why not setSuggestedValue? 464 // TODO(crbug.com/507714): Why not setSuggestedValue?
454 username_element->SetAutofillValue(blink::WebString::FromUTF16(username)); 465 username_element->SetAutofillValue(blink::WebString::FromUTF16(username));
455 UpdateFieldValueAndPropertiesMaskMap(*username_element, &username, 466 UpdateFieldValueAndPropertiesMaskMap(*username_element, &username,
456 FieldPropertiesFlags::AUTOFILLED, 467 FieldPropertiesFlags::AUTOFILLED,
457 field_value_and_properties_map); 468 field_value_and_properties_map);
458 username_element->SetAutofilled(true); 469 username_element->SetAutofilled(true);
459 if (logger) 470 if (logger)
460 logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element); 471 logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element);
461 if (set_selection) { 472 if (set_selection) {
462 form_util::PreviewSuggestion(username, current_username, 473 form_util::PreviewSuggestion(username, current_username,
463 username_element); 474 username_element);
464 } 475 }
465 } else if (current_username != username) { 476 } else if (exact_username_match && current_username != username) {
vabr (Chromium) 2017/06/12 18:56:58 There is not code path to satisfy this condition:
melandory 2017/06/19 10:53:48 Done.
466 // If the username can't be filled and it doesn't match a saved password 477 // If the username can't be filled and it doesn't match a saved password
467 // as is, don't autofill a password. 478 // as is, autofill the password only in case the username element id read
479 // only.
468 return false; 480 return false;
469 } 481 }
470 482
471 // Wait to fill in the password until a user gesture occurs. This is to make 483 // 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 484 // sure that we do not fill in the DOM with a password until we believe the
473 // user is intentionally interacting with the page. 485 // user is intentionally interacting with the page.
474 password_element->SetSuggestedValue(blink::WebString::FromUTF16(password)); 486 password_element->SetSuggestedValue(blink::WebString::FromUTF16(password));
475 UpdateFieldValueAndPropertiesMaskMap(*password_element, &password, 487 UpdateFieldValueAndPropertiesMaskMap(*password_element, &password,
476 FieldPropertiesFlags::AUTOFILLED, 488 FieldPropertiesFlags::AUTOFILLED,
477 field_value_and_properties_map); 489 field_value_and_properties_map);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
543 // In all other cases, do nothing. 555 // In all other cases, do nothing.
544 bool form_has_fillable_username = !username_field_name.empty() && 556 bool form_has_fillable_username = !username_field_name.empty() &&
545 IsElementAutocompletable(username_element); 557 IsElementAutocompletable(username_element);
546 558
547 if (form_has_fillable_username && username_element.Value().IsEmpty()) { 559 if (form_has_fillable_username && username_element.Value().IsEmpty()) {
548 // TODO(tkent): Check maxlength and pattern. 560 // TODO(tkent): Check maxlength and pattern.
549 username_element.SetAutofillValue( 561 username_element.SetAutofillValue(
550 blink::WebString::FromUTF16(fill_data.username_field.value)); 562 blink::WebString::FromUTF16(fill_data.username_field.value));
551 } 563 }
552 564
553 // Fill if we have an exact match for the username. Note that this sets 565 bool exact_username_match =
554 // username to autofilled. 566 username_element.IsNull() || IsElementEditable(username_element);
567 // User the exact match for the editable username fields and allow prefix
vabr (Chromium) 2017/06/12 18:56:58 nit: User -> Use
melandory 2017/06/19 10:53:48 Done.
568 // match for read-only username fields.
569 // Note that this sets username to autofilled.
vabr (Chromium) 2017/06/12 18:56:58 nit: The latter sentence no longer makes sense ("t
melandory 2017/06/19 10:53:48 Done.
555 return FillUserNameAndPassword( 570 return FillUserNameAndPassword(
556 &username_element, &password_element, fill_data, 571 &username_element, &password_element, fill_data, exact_username_match,
557 true /* exact_username_match */, false /* set_selection */, 572 false /* set_selection */, field_value_and_properties_map,
558 field_value_and_properties_map, registration_callback, logger); 573 registration_callback, logger);
559 } 574 }
560 575
561 // Annotate |forms| with form and field signatures as HTML attributes. 576 // Annotate |forms| with form and field signatures as HTML attributes.
562 void AnnotateFormsWithSignatures( 577 void AnnotateFormsWithSignatures(
563 blink::WebVector<blink::WebFormElement> forms) { 578 blink::WebVector<blink::WebFormElement> forms) {
564 for (blink::WebFormElement form : forms) { 579 for (blink::WebFormElement form : forms) {
565 std::unique_ptr<PasswordForm> password_form( 580 std::unique_ptr<PasswordForm> password_form(
566 CreatePasswordFormFromWebForm(form, nullptr, nullptr)); 581 CreatePasswordFormFromWebForm(form, nullptr, nullptr));
567 if (password_form) { 582 if (password_form) {
568 form.SetAttribute( 583 form.SetAttribute(
(...skipping 1203 matching lines...) Expand 10 before | Expand all | Expand 10 after
1772 PasswordAutofillAgent::GetPasswordManagerDriver() { 1787 PasswordAutofillAgent::GetPasswordManagerDriver() {
1773 if (!password_manager_driver_) { 1788 if (!password_manager_driver_) {
1774 render_frame()->GetRemoteInterfaces()->GetInterface( 1789 render_frame()->GetRemoteInterfaces()->GetInterface(
1775 mojo::MakeRequest(&password_manager_driver_)); 1790 mojo::MakeRequest(&password_manager_driver_));
1776 } 1791 }
1777 1792
1778 return password_manager_driver_; 1793 return password_manager_driver_;
1779 } 1794 }
1780 1795
1781 } // namespace autofill 1796 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698