OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/password_manager/core/browser/password_form_manager.h" | 5 #include "components/password_manager/core/browser/password_form_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <set> | 8 #include <set> |
9 | 9 |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
377 } | 377 } |
378 | 378 |
379 // Do not autofill on sign-up or change password forms (until we have some | 379 // Do not autofill on sign-up or change password forms (until we have some |
380 // working change password functionality). | 380 // working change password functionality). |
381 if (!observed_form_.new_password_element.empty()) { | 381 if (!observed_form_.new_password_element.empty()) { |
382 if (logger) | 382 if (logger) |
383 logger->LogMessage(Logger::STRING_FORM_NOT_AUTOFILLED); | 383 logger->LogMessage(Logger::STRING_FORM_NOT_AUTOFILLED); |
384 client_->AutofillResultsComputed(); | 384 client_->AutofillResultsComputed(); |
385 // There is no point in looking for the credentials in the store when they | 385 // There is no point in looking for the credentials in the store when they |
386 // won't be autofilled, so pretend there were none. | 386 // won't be autofilled, so pretend there were none. |
387 std::vector<autofill::PasswordForm*> dummy_results; | 387 OnGetPasswordStoreResults(ScopedVector<autofill::PasswordForm>()); |
388 OnGetPasswordStoreResults(dummy_results); | |
389 return; | 388 return; |
390 } | 389 } |
391 | 390 |
392 PasswordStore* password_store = client_->GetPasswordStore(); | 391 PasswordStore* password_store = client_->GetPasswordStore(); |
393 if (!password_store) { | 392 if (!password_store) { |
394 if (logger) | 393 if (logger) |
395 logger->LogMessage(Logger::STRING_NO_STORE); | 394 logger->LogMessage(Logger::STRING_NO_STORE); |
396 NOTREACHED(); | 395 NOTREACHED(); |
397 return; | 396 return; |
398 } | 397 } |
399 password_store->GetLogins(observed_form_, prompt_policy, this); | 398 password_store->GetLogins(observed_form_, prompt_policy, this); |
400 } | 399 } |
401 | 400 |
402 bool PasswordFormManager::HasCompletedMatching() const { | 401 bool PasswordFormManager::HasCompletedMatching() const { |
403 return state_ == POST_MATCHING_PHASE; | 402 return state_ == POST_MATCHING_PHASE; |
404 } | 403 } |
405 | 404 |
406 bool PasswordFormManager::IsIgnorableChangePasswordForm() const { | 405 bool PasswordFormManager::IsIgnorableChangePasswordForm() const { |
407 bool is_change_password_form = !observed_form_.new_password_element.empty() && | 406 bool is_change_password_form = !observed_form_.new_password_element.empty() && |
408 !observed_form_.password_element.empty(); | 407 !observed_form_.password_element.empty(); |
409 bool is_username_certainly_correct = observed_form_.username_marked_by_site; | 408 bool is_username_certainly_correct = observed_form_.username_marked_by_site; |
410 return is_change_password_form && !is_username_certainly_correct; | 409 return is_change_password_form && !is_username_certainly_correct; |
411 } | 410 } |
412 | 411 |
413 void PasswordFormManager::OnRequestDone( | 412 void PasswordFormManager::OnRequestDone( |
414 const std::vector<PasswordForm*>& logins_result) { | 413 ScopedVector<PasswordForm> logins_result) { |
415 // Note that the result gets deleted after this call completes, but we own | 414 const size_t logins_result_size = logins_result.size(); |
416 // the PasswordForm objects pointed to by the result vector, thus we keep | |
417 // copies to a minimum here. | |
418 | 415 |
419 scoped_ptr<BrowserSavePasswordProgressLogger> logger; | 416 scoped_ptr<BrowserSavePasswordProgressLogger> logger; |
420 if (client_->IsLoggingActive()) { | 417 if (client_->IsLoggingActive()) { |
421 logger.reset(new BrowserSavePasswordProgressLogger(client_)); | 418 logger.reset(new BrowserSavePasswordProgressLogger(client_)); |
422 logger->LogMessage(Logger::STRING_ON_REQUEST_DONE_METHOD); | 419 logger->LogMessage(Logger::STRING_ON_REQUEST_DONE_METHOD); |
423 } | 420 } |
424 | 421 |
425 int best_score = 0; | 422 int best_score = 0; |
426 // These credentials will be in the final result regardless of score. | 423 // These credentials will be in the final result regardless of score. |
427 std::vector<PasswordForm> credentials_to_keep; | 424 ScopedVector<PasswordForm> credentials_to_keep; |
428 for (size_t i = 0; i < logins_result.size(); i++) { | 425 for (auto& login_ptr : logins_result) { |
429 if (ShouldIgnoreResult(*logins_result[i])) { | 426 scoped_ptr<PasswordForm> login(login_ptr); |
430 delete logins_result[i]; | 427 login_ptr = nullptr; |
428 // A stable handle to the form while the owners change. | |
429 const PasswordForm& const_login(*login); | |
430 | |
431 if (ShouldIgnoreResult(const_login)) | |
431 continue; | 432 continue; |
432 } | 433 |
433 // Score and update best matches. | 434 // Score and update best matches. |
434 int current_score = ScoreResult(*logins_result[i]); | 435 int current_score = ScoreResult(const_login); |
435 // This check is here so we can append empty path matches in the event | |
436 // they don't score as high as others and aren't added to best_matches_. | |
437 // This is most commonly imported firefox logins. We skip blacklisted | |
438 // ones because clearly we don't want to autofill them, and secondly | |
439 // because they only mean something when we have no other matches already | |
440 // saved in Chrome - in which case they'll make it through the regular | |
441 // scoring flow below by design. Note signon_realm == origin implies empty | |
442 // path logins_result, since signon_realm is a prefix of origin for HTML | |
443 // password forms. | |
444 // TODO(timsteele): Bug 1269400. We probably should do something more | |
445 // elegant for any shorter-path match instead of explicitly handling empty | |
446 // path matches. | |
447 if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && | 436 if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && |
448 (observed_form_.signon_realm == logins_result[i]->origin.spec()) && | 437 (observed_form_.signon_realm == const_login.origin.spec()) && |
449 (current_score > 0) && (!logins_result[i]->blacklisted_by_user)) { | 438 (current_score > 0) && (!const_login.blacklisted_by_user)) { |
450 credentials_to_keep.push_back(*logins_result[i]); | 439 // This check is here so we can append empty path matches in the event |
440 // they don't score as high as others and aren't added to best_matches_. | |
441 // This is most commonly imported firefox logins. We skip blacklisted | |
442 // ones because clearly we don't want to autofill them, and secondly | |
443 // because they only mean something when we have no other matches already | |
444 // saved in Chrome - in which case they'll make it through the regular | |
445 // scoring flow below by design. Note signon_realm == origin implies empty | |
446 // path logins_result, since signon_realm is a prefix of origin for HTML | |
447 // password forms. | |
448 // TODO(timsteele): Bug 1269400. We probably should do something more | |
449 // elegant for any shorter-path match instead of explicitly handling empty | |
450 // path matches. | |
451 credentials_to_keep.push_back(login.release()); | |
452 } else if (const_login.type == PasswordForm::TYPE_GENERATED) { | |
453 // Always keep generated passwords as part of the result set. If a user | |
454 // generates a password on a signup form, it should show on a login form | |
455 // even if they have a previous login saved. | |
456 // TODO(gcasto): We don't want to cut credentials that were saved on | |
457 // signup forms even if they weren't generated, but currently it's hard to | |
458 // distinguish between those forms and two different login forms on the | |
459 // same domain. Filed http://crbug.com/294468 to look into this. | |
460 credentials_to_keep.push_back(login.release()); | |
451 } | 461 } |
452 | 462 |
453 // Always keep generated passwords as part of the result set. If a user | 463 // End of exceptions, only interested in good-scoring candidates since now. |
454 // generates a password on a signup form, it should show on a login form | |
455 // even if they have a previous login saved. | |
456 // TODO(gcasto): We don't want to cut credentials that were saved on signup | |
457 // forms even if they weren't generated, but currently it's hard to | |
458 // distinguish between those forms and two different login forms on the | |
459 // same domain. Filed http://crbug.com/294468 to look into this. | |
460 if (logins_result[i]->type == PasswordForm::TYPE_GENERATED) | |
461 credentials_to_keep.push_back(*logins_result[i]); | |
462 | |
463 if (current_score < best_score) { | 464 if (current_score < best_score) { |
464 delete logins_result[i]; | |
465 continue; | 465 continue; |
466 } else if (current_score == best_score) { | |
467 auto& best_match = best_matches_[const_login.username_value]; | |
468 if (best_match == preferred_match_) | |
469 preferred_match_ = nullptr; | |
470 delete best_match; | |
471 best_match = login.release(); | |
472 } else { // current_score > best_score | |
473 best_score = current_score; | |
474 preferred_match_ = nullptr; // Don't delete, its owned by best_matches_. | |
475 STLDeleteValues(&best_matches_); // |login| tops the previous matches. | |
476 best_matches_[const_login.username_value] = login.release(); | |
466 } | 477 } |
467 if (current_score == best_score) { | 478 preferred_match_ = const_login.preferred ? &const_login : preferred_match_; |
vasilii
2015/02/09 10:08:35
What about DCHECK(!login) above because you save t
vabr (Chromium)
2015/02/09 11:36:06
Done.
| |
468 PasswordForm* old_form = best_matches_[logins_result[i]->username_value]; | |
469 if (old_form) { | |
470 if (preferred_match_ == old_form) | |
471 preferred_match_ = NULL; | |
472 delete old_form; | |
473 } | |
474 best_matches_[logins_result[i]->username_value] = logins_result[i]; | |
475 } else if (current_score > best_score) { | |
476 best_score = current_score; | |
477 // This new login has a better score than all those up to this point | |
478 // Note 'this' owns all the PasswordForms in best_matches_. | |
479 STLDeleteValues(&best_matches_); | |
480 preferred_match_ = NULL; // Don't delete, its owned by best_matches_. | |
481 best_matches_[logins_result[i]->username_value] = logins_result[i]; | |
482 } | |
483 preferred_match_ = | |
484 logins_result[i]->preferred ? logins_result[i] : preferred_match_; | |
485 } | 479 } |
480 logins_result.weak_clear(); // It contains just nulls, speed up destruction. | |
486 | 481 |
487 client_->AutofillResultsComputed(); | 482 client_->AutofillResultsComputed(); |
488 | 483 |
489 // TODO(gcasto): Change this to check that best_matches_ is empty. This should | 484 // TODO(gcasto): Change this to check that best_matches_ is empty. This should |
490 // be equivalent for the moment, but it's less clear and may not be | 485 // be equivalent for the moment, but it's less clear and may not be |
491 // equivalent in the future. | 486 // equivalent in the future. |
492 if (best_score <= 0) { | 487 if (best_score <= 0) { |
493 if (logger) | 488 if (logger) |
494 logger->LogNumber(Logger::STRING_BEST_SCORE, best_score); | 489 logger->LogNumber(Logger::STRING_BEST_SCORE, best_score); |
495 return; | 490 return; |
496 } | 491 } |
497 | 492 |
498 for (std::vector<PasswordForm>::const_iterator it = | 493 for (auto& form : credentials_to_keep) { |
499 credentials_to_keep.begin(); | |
500 it != credentials_to_keep.end(); ++it) { | |
501 // If we don't already have a result with the same username, add the | 494 // If we don't already have a result with the same username, add the |
502 // lower-scored match (if it had equal score it would already be in | 495 // lower-scored match (if it had equal score it would already be in |
503 // best_matches_). | 496 // best_matches_). |
504 if (best_matches_.find(it->username_value) == best_matches_.end()) | 497 auto& corresponding_best_match = best_matches_[form->username_value]; |
505 best_matches_[it->username_value] = new PasswordForm(*it); | 498 if (!corresponding_best_match) { |
499 corresponding_best_match = form; | |
500 form = nullptr; | |
501 } | |
506 } | 502 } |
507 | 503 |
508 UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsNotShown", | 504 UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsNotShown", |
509 logins_result.size() - best_matches_.size()); | 505 logins_result_size - best_matches_.size()); |
510 | 506 |
511 // It is possible we have at least one match but have no preferred_match_, | 507 // It is possible we have at least one match but have no preferred_match_, |
512 // because a user may have chosen to 'Forget' the preferred match. So we | 508 // because a user may have chosen to 'Forget' the preferred match. So we |
513 // just pick the first one and whichever the user selects for submit will | 509 // just pick the first one and whichever the user selects for submit will |
514 // be saved as preferred. | 510 // be saved as preferred. |
515 DCHECK(!best_matches_.empty()); | 511 DCHECK(!best_matches_.empty()); |
516 if (!preferred_match_) | 512 if (!preferred_match_) |
517 preferred_match_ = best_matches_.begin()->second; | 513 preferred_match_ = best_matches_.begin()->second; |
518 | 514 |
519 // Check to see if the user told us to ignore this site in the past. | 515 // Check to see if the user told us to ignore this site in the past. |
(...skipping 29 matching lines...) Expand all Loading... | |
549 preferred_match_->IsPublicSuffixMatch(); | 545 preferred_match_->IsPublicSuffixMatch(); |
550 if (wait_for_username) | 546 if (wait_for_username) |
551 manager_action_ = kManagerActionNone; | 547 manager_action_ = kManagerActionNone; |
552 else | 548 else |
553 manager_action_ = kManagerActionAutofilled; | 549 manager_action_ = kManagerActionAutofilled; |
554 password_manager_->Autofill(driver.get(), observed_form_, best_matches_, | 550 password_manager_->Autofill(driver.get(), observed_form_, best_matches_, |
555 *preferred_match_, wait_for_username); | 551 *preferred_match_, wait_for_username); |
556 } | 552 } |
557 | 553 |
558 void PasswordFormManager::OnGetPasswordStoreResults( | 554 void PasswordFormManager::OnGetPasswordStoreResults( |
559 const std::vector<autofill::PasswordForm*>& results) { | 555 ScopedVector<PasswordForm> results) { |
560 DCHECK_EQ(state_, MATCHING_PHASE); | 556 DCHECK_EQ(state_, MATCHING_PHASE); |
561 | 557 |
562 scoped_ptr<BrowserSavePasswordProgressLogger> logger; | 558 scoped_ptr<BrowserSavePasswordProgressLogger> logger; |
563 if (client_->IsLoggingActive()) { | 559 if (client_->IsLoggingActive()) { |
564 logger.reset(new BrowserSavePasswordProgressLogger(client_)); | 560 logger.reset(new BrowserSavePasswordProgressLogger(client_)); |
565 logger->LogMessage(Logger::STRING_ON_GET_STORE_RESULTS_METHOD); | 561 logger->LogMessage(Logger::STRING_ON_GET_STORE_RESULTS_METHOD); |
566 logger->LogNumber(Logger::STRING_NUMBER_RESULTS, results.size()); | 562 logger->LogNumber(Logger::STRING_NUMBER_RESULTS, results.size()); |
567 } | 563 } |
568 | 564 |
569 if (!results.empty()) | 565 if (!results.empty()) |
570 OnRequestDone(results); | 566 OnRequestDone(results.Pass()); |
571 state_ = POST_MATCHING_PHASE; | 567 state_ = POST_MATCHING_PHASE; |
572 | 568 |
573 if (manager_action_ != kManagerActionBlacklisted) { | 569 if (manager_action_ != kManagerActionBlacklisted) { |
574 for (auto const& driver : drivers_) | 570 for (auto const& driver : drivers_) |
575 ProcessFrame(driver); | 571 ProcessFrame(driver); |
576 } | 572 } |
577 drivers_.clear(); | 573 drivers_.clear(); |
578 } | 574 } |
579 | 575 |
580 bool PasswordFormManager::ShouldIgnoreResult(const PasswordForm& form) const { | 576 bool PasswordFormManager::ShouldIgnoreResult(const PasswordForm& form) const { |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
862 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED); | 858 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED); |
863 } | 859 } |
864 | 860 |
865 void PasswordFormManager::SubmitFailed() { | 861 void PasswordFormManager::SubmitFailed() { |
866 submit_result_ = kSubmitResultFailed; | 862 submit_result_ = kSubmitResultFailed; |
867 if (has_generated_password_) | 863 if (has_generated_password_) |
868 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED); | 864 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED); |
869 } | 865 } |
870 | 866 |
871 } // namespace password_manager | 867 } // namespace password_manager |
OLD | NEW |