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

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 <memory> 9 #include <memory>
10 #include <string> 10 #include <string>
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
260 FormInputElementMap unowned_elements_map; 260 FormInputElementMap unowned_elements_map;
261 if (FindFormInputElements(control_elements, data, ambiguous_or_empty_names, 261 if (FindFormInputElements(control_elements, data, ambiguous_or_empty_names,
262 &unowned_elements_map)) 262 &unowned_elements_map))
263 results->push_back(unowned_elements_map); 263 results->push_back(unowned_elements_map);
264 } 264 }
265 265
266 bool IsElementEditable(const blink::WebInputElement& element) { 266 bool IsElementEditable(const blink::WebInputElement& element) {
267 return element.IsEnabled() && !element.IsReadOnly(); 267 return element.IsEnabled() && !element.IsReadOnly();
268 } 268 }
269 269
270 bool DoUsernamesMatch(const base::string16& username1, 270 bool DoUsernamesMatch(const base::string16& potential_suggestion,
271 const base::string16& username2, 271 const base::string16& current_username,
272 bool exact_match) { 272 bool exact_match) {
273 if (exact_match) 273 bool match = (potential_suggestion == current_username);
274 return username1 == username2; 274 return exact_match
275 return FieldIsSuggestionSubstringStartingOnTokenBoundary(username1, username2, 275 ? match
276 true); 276 : (match || IsPrefixStartingOnTokenBoundary(
277 current_username, potential_suggestion, true));
dvadym 2017/06/01 12:15:56 As far as I understand the main use case is emails
melandory 2017/06/12 14:56:12 My motivation was that since currently gmail suffe
vabr (Chromium) 2017/06/12 18:56:58 Currently case sensitive equality is required. Let
277 } 278 }
278 279
279 // Returns |true| if the given element is editable. Otherwise, returns |false|. 280 // Returns |true| if the given element is editable. Otherwise, returns |false|.
280 bool IsElementAutocompletable(const blink::WebInputElement& element) { 281 bool IsElementAutocompletable(const blink::WebInputElement& element) {
281 return IsElementEditable(element); 282 return IsElementEditable(element);
282 } 283 }
283 284
284 // Return true if either password_value or new_password_value is not empty and 285 // Return true if either password_value or new_password_value is not empty and
285 // not default. 286 // not default.
286 bool FormContainsNonDefaultPasswordValue(const PasswordForm& password_form) { 287 bool FormContainsNonDefaultPasswordValue(const PasswordForm& password_form) {
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 if (value) 352 if (value)
352 it->second.first.reset(new base::string16(*value)); 353 it->second.first.reset(new base::string16(*value));
353 it->second.second |= added_flags; 354 it->second.second |= added_flags;
354 } else { 355 } else {
355 (*field_value_and_properties_map)[element] = std::make_pair( 356 (*field_value_and_properties_map)[element] = std::make_pair(
356 value ? base::MakeUnique<base::string16>(*value) : nullptr, 357 value ? base::MakeUnique<base::string16>(*value) : nullptr,
357 added_flags); 358 added_flags);
358 } 359 }
359 } 360 }
360 361
362 void FindMatchesByUsername(const PasswordFormFillData& fill_data,
363 const base::string16& current_username,
364 bool exact_username_match,
365 RendererSavePasswordProgressLogger* logger,
366 base::string16* username,
367 base::string16* password) {
368 // Look for any suitable matches to current field text.
369 if (DoUsernamesMatch(fill_data.username_field.value, current_username,
370 exact_username_match)) {
371 *username = fill_data.username_field.value;
372 *password = fill_data.password_field.value;
373 if (logger)
374 logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
375 } else {
376 // Scan additional logins for a match.
377 for (const auto& it : fill_data.additional_logins) {
378 if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
379 *username = it.first;
380 *password = it.second.password;
381 break;
382 }
383 }
384 if (logger) {
385 logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
386 !(username->empty() && password->empty()));
387 }
388
389 // Check possible usernames.
390 if (username->empty() && password->empty()) {
391 for (const auto& it : fill_data.other_possible_usernames) {
392 for (size_t i = 0; i < it.second.size(); ++i) {
393 if (DoUsernamesMatch(it.second[i], current_username,
394 exact_username_match)) {
395 *username = it.second[i];
396 *password = it.first.password;
397 break;
398 }
399 }
400 if (!username->empty() && !password->empty())
401 break;
402 }
403 }
404 }
405 }
406
361 // This function attempts to fill |username_element| and |password_element| 407 // This function attempts to fill |username_element| and |password_element|
362 // with values from |fill_data|. The |password_element| will only have the 408 // with values from |fill_data|. The |password_element| will only have the
363 // suggestedValue set, and will be registered for copying that to the real 409 // suggestedValue set, and will be registered for copying that to the real
364 // value through |registration_callback|. If a match is found, return true and 410 // value through |registration_callback|. If a match is found, return true and
365 // |field_value_and_properties_map| will be modified with the autofilled 411 // |field_value_and_properties_map| will be modified with the autofilled
366 // credentials and |FieldPropertiesFlags::AUTOFILLED| flag. 412 // credentials and |FieldPropertiesFlags::AUTOFILLED| flag.
367 bool FillUserNameAndPassword( 413 bool FillUserNameAndPassword(
368 blink::WebInputElement* username_element, 414 blink::WebInputElement* username_element,
369 blink::WebInputElement* password_element, 415 blink::WebInputElement* password_element,
370 const PasswordFormFillData& fill_data, 416 const PasswordFormFillData& fill_data,
(...skipping 11 matching lines...) Expand all
382 428
383 base::string16 current_username; 429 base::string16 current_username;
384 if (!username_element->IsNull()) { 430 if (!username_element->IsNull()) {
385 current_username = username_element->Value().Utf16(); 431 current_username = username_element->Value().Utf16();
386 } 432 }
387 433
388 // username and password will contain the match found if any. 434 // username and password will contain the match found if any.
389 base::string16 username; 435 base::string16 username;
390 base::string16 password; 436 base::string16 password;
391 437
392 // Look for any suitable matches to current field text. 438 FindMatchesByUsername(fill_data, current_username, exact_username_match,
393 if (DoUsernamesMatch(fill_data.username_field.value, current_username, 439 logger, &username, &password);
394 exact_username_match)) {
395 username = fill_data.username_field.value;
396 password = fill_data.password_field.value;
397 if (logger)
398 logger->LogMessage(Logger::STRING_USERNAMES_MATCH);
399 } else {
400 // Scan additional logins for a match.
401 for (const auto& it : fill_data.additional_logins) {
402 if (DoUsernamesMatch(it.first, current_username, exact_username_match)) {
403 username = it.first;
404 password = it.second.password;
405 break;
406 }
407 }
408 if (logger) {
409 logger->LogBoolean(Logger::STRING_MATCH_IN_ADDITIONAL,
410 !(username.empty() && password.empty()));
411 }
412 440
413 // Check possible usernames.
414 if (username.empty() && password.empty()) {
415 for (const auto& it : fill_data.other_possible_usernames) {
416 for (size_t i = 0; i < it.second.size(); ++i) {
417 if (DoUsernamesMatch(
418 it.second[i], current_username, exact_username_match)) {
419 username = it.second[i];
420 password = it.first.password;
421 break;
422 }
423 }
424 if (!username.empty() && !password.empty())
425 break;
426 }
427 }
428 }
429 if (password.empty()) 441 if (password.empty())
430 return false; 442 return false;
431 443
432 // TODO(tkent): Check maxlength and pattern for both username and password 444 // TODO(tkent): Check maxlength and pattern for both username and password
433 // fields. 445 // fields.
434 446
435 // Input matches the username, fill in required values. 447 // Input matches the username, fill in required values.
436 if (!username_element->IsNull() && 448 if (!username_element->IsNull() &&
437 IsElementAutocompletable(*username_element)) { 449 IsElementAutocompletable(*username_element)) {
438 // TODO(crbug.com/507714): Why not setSuggestedValue? 450 // TODO(crbug.com/507714): Why not setSuggestedValue?
439 username_element->SetAutofillValue(blink::WebString::FromUTF16(username)); 451 username_element->SetAutofillValue(blink::WebString::FromUTF16(username));
440 UpdateFieldValueAndPropertiesMaskMap(*username_element, &username, 452 UpdateFieldValueAndPropertiesMaskMap(*username_element, &username,
441 FieldPropertiesFlags::AUTOFILLED, 453 FieldPropertiesFlags::AUTOFILLED,
442 field_value_and_properties_map); 454 field_value_and_properties_map);
443 username_element->SetAutofilled(true); 455 username_element->SetAutofilled(true);
444 if (logger) 456 if (logger)
445 logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element); 457 logger->LogElementName(Logger::STRING_USERNAME_FILLED, *username_element);
446 if (set_selection) { 458 if (set_selection) {
447 form_util::PreviewSuggestion(username, current_username, 459 form_util::PreviewSuggestion(username, current_username,
448 username_element); 460 username_element);
449 } 461 }
450 } else if (current_username != username) { 462 } else if (exact_username_match && current_username != username) {
451 // If the username can't be filled and it doesn't match a saved password 463 // If the username can't be filled and it doesn't match a saved password
452 // as is, don't autofill a password. 464 // as is, autofill the password only in case the username element id read
465 // only.
453 return false; 466 return false;
454 } 467 }
455 468
456 // Wait to fill in the password until a user gesture occurs. This is to make 469 // Wait to fill in the password until a user gesture occurs. This is to make
457 // sure that we do not fill in the DOM with a password until we believe the 470 // sure that we do not fill in the DOM with a password until we believe the
458 // user is intentionally interacting with the page. 471 // user is intentionally interacting with the page.
459 password_element->SetSuggestedValue(blink::WebString::FromUTF16(password)); 472 password_element->SetSuggestedValue(blink::WebString::FromUTF16(password));
460 UpdateFieldValueAndPropertiesMaskMap(*password_element, &password, 473 UpdateFieldValueAndPropertiesMaskMap(*password_element, &password,
461 FieldPropertiesFlags::AUTOFILLED, 474 FieldPropertiesFlags::AUTOFILLED,
462 field_value_and_properties_map); 475 field_value_and_properties_map);
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 // In all other cases, do nothing. 541 // In all other cases, do nothing.
529 bool form_has_fillable_username = !username_field_name.empty() && 542 bool form_has_fillable_username = !username_field_name.empty() &&
530 IsElementAutocompletable(username_element); 543 IsElementAutocompletable(username_element);
531 544
532 if (form_has_fillable_username && username_element.Value().IsEmpty()) { 545 if (form_has_fillable_username && username_element.Value().IsEmpty()) {
533 // TODO(tkent): Check maxlength and pattern. 546 // TODO(tkent): Check maxlength and pattern.
534 username_element.SetAutofillValue( 547 username_element.SetAutofillValue(
535 blink::WebString::FromUTF16(fill_data.username_field.value)); 548 blink::WebString::FromUTF16(fill_data.username_field.value));
536 } 549 }
537 550
538 // Fill if we have an exact match for the username. Note that this sets 551 bool exact_username_match =
539 // username to autofilled. 552 username_element.IsNull() || IsElementEditable(username_element);
553 // User the exact match for the editable username fields and allow prefix
554 // match for read-only username fields.
555 // Note that this sets username to autofilled.
540 return FillUserNameAndPassword( 556 return FillUserNameAndPassword(
541 &username_element, &password_element, fill_data, 557 &username_element, &password_element, fill_data, exact_username_match,
542 true /* exact_username_match */, false /* set_selection */, 558 false /* set_selection */, field_value_and_properties_map,
543 field_value_and_properties_map, registration_callback, logger); 559 registration_callback, logger);
544 } 560 }
545 561
546 // Annotate |forms| with form and field signatures as HTML attributes. 562 // Annotate |forms| with form and field signatures as HTML attributes.
547 void AnnotateFormsWithSignatures( 563 void AnnotateFormsWithSignatures(
548 blink::WebVector<blink::WebFormElement> forms) { 564 blink::WebVector<blink::WebFormElement> forms) {
549 for (blink::WebFormElement form : forms) { 565 for (blink::WebFormElement form : forms) {
550 std::unique_ptr<PasswordForm> password_form( 566 std::unique_ptr<PasswordForm> password_form(
551 CreatePasswordFormFromWebForm(form, nullptr, nullptr)); 567 CreatePasswordFormFromWebForm(form, nullptr, nullptr));
552 if (password_form) { 568 if (password_form) {
553 form.SetAttribute( 569 form.SetAttribute(
(...skipping 1163 matching lines...) Expand 10 before | Expand all | Expand 10 after
1717 PasswordAutofillAgent::GetPasswordManagerDriver() { 1733 PasswordAutofillAgent::GetPasswordManagerDriver() {
1718 if (!password_manager_driver_) { 1734 if (!password_manager_driver_) {
1719 render_frame()->GetRemoteInterfaces()->GetInterface( 1735 render_frame()->GetRemoteInterfaces()->GetInterface(
1720 mojo::MakeRequest(&password_manager_driver_)); 1736 mojo::MakeRequest(&password_manager_driver_));
1721 } 1737 }
1722 1738
1723 return password_manager_driver_; 1739 return password_manager_driver_;
1724 } 1740 }
1725 1741
1726 } // namespace autofill 1742 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698