Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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, |
|
vabr (Chromium)
2017/06/21 12:28:07
There is still one issue:
In the rare case the us
| |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |