| Index: components/password_manager/core/browser/password_form_manager.cc
|
| diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
|
| index b642296ddba651cc8b1c420f5699f3d818ff9310..52b3e0bf793b32271c8f421ffe56bc7156470370 100644
|
| --- a/components/password_manager/core/browser/password_form_manager.cc
|
| +++ b/components/password_manager/core/browser/password_form_manager.cc
|
| @@ -411,6 +411,8 @@ bool PasswordFormManager::IsIgnorableChangePasswordForm() const {
|
|
|
| void PasswordFormManager::OnRequestDone(
|
| ScopedVector<PasswordForm> logins_result) {
|
| + preferred_match_ = nullptr;
|
| + STLDeleteValues(&best_matches_);
|
| const size_t logins_result_size = logins_result.size();
|
|
|
| scoped_ptr<BrowserSavePasswordProgressLogger> logger;
|
| @@ -419,89 +421,86 @@ void PasswordFormManager::OnRequestDone(
|
| logger->LogMessage(Logger::STRING_ON_REQUEST_DONE_METHOD);
|
| }
|
|
|
| + // First, compute scores for credentials in |login_result|.
|
| + std::vector<int> credential_scores;
|
| + credential_scores.reserve(logins_result.size());
|
| int best_score = 0;
|
| - // These credentials will be in the final result regardless of score.
|
| - std::vector<PasswordForm> credentials_to_keep;
|
| - for (auto& login_ptr : logins_result) {
|
| - scoped_ptr<PasswordForm> login(login_ptr);
|
| - login_ptr = nullptr;
|
| -
|
| - if (ShouldIgnoreResult(*login))
|
| + for (const PasswordForm* login : logins_result) {
|
| + if (ShouldIgnoreResult(*login)) {
|
| + credential_scores.push_back(-1);
|
| continue;
|
| -
|
| - // Score and update best matches.
|
| - int current_score = ScoreResult(*login);
|
| - if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) &&
|
| - (observed_form_.signon_realm == login->origin.spec()) &&
|
| - (current_score > 0) && (!login->blacklisted_by_user)) {
|
| - // This check is here so we can append empty path matches in the event
|
| - // they don't score as high as others and aren't added to best_matches_.
|
| - // This is most commonly imported firefox logins. We skip blacklisted
|
| - // ones because clearly we don't want to autofill them, and secondly
|
| - // because they only mean something when we have no other matches already
|
| - // saved in Chrome - in which case they'll make it through the regular
|
| - // scoring flow below by design. Note signon_realm == origin implies empty
|
| - // path logins_result, since signon_realm is a prefix of origin for HTML
|
| - // password forms.
|
| - // TODO(timsteele): Bug 1269400. We probably should do something more
|
| - // elegant for any shorter-path match instead of explicitly handling empty
|
| - // path matches.
|
| - credentials_to_keep.push_back(*login);
|
| - } else if (login->type == PasswordForm::TYPE_GENERATED) {
|
| - // Always keep generated passwords as part of the result set. If a user
|
| - // generates a password on a signup form, it should show on a login form
|
| - // even if they have a previous login saved.
|
| - // TODO(gcasto): We don't want to cut credentials that were saved on
|
| - // signup forms even if they weren't generated, but currently it's hard to
|
| - // distinguish between those forms and two different login forms on the
|
| - // same domain. Filed http://crbug.com/294468 to look into this.
|
| - credentials_to_keep.push_back(*login);
|
| }
|
|
|
| - // End of exceptions, only interested in good-scoring candidates since now.
|
| - if (current_score < best_score) {
|
| - continue;
|
| - } else if (current_score == best_score) {
|
| - auto& best_match = best_matches_[login->username_value];
|
| - if (best_match == preferred_match_)
|
| - preferred_match_ = nullptr;
|
| - delete best_match;
|
| - preferred_match_ = login->preferred ? login.get() : preferred_match_;
|
| - best_match = login.release();
|
| - } else { // current_score > best_score
|
| + int current_score = ScoreResult(*login);
|
| + if (current_score > best_score)
|
| best_score = current_score;
|
| - STLDeleteValues(&best_matches_); // |login| tops the previous matches.
|
| - preferred_match_ = login->preferred ? login.get() : nullptr;
|
| - base::string16 username_value = login->username_value;
|
| - best_matches_[username_value] = login.release();
|
| - }
|
| - // |login| is just temporary, should no longer hold the credentials at this
|
| - // point.
|
| - DCHECK(!login);
|
| + credential_scores.push_back(current_score);
|
| }
|
| - logins_result.weak_clear(); // It contains just nulls, speed up destruction.
|
| -
|
| - client_->AutofillResultsComputed();
|
|
|
| - // TODO(gcasto): Change this to check that best_matches_ is empty. This should
|
| - // be equivalent for the moment, but it's less clear and may not be
|
| - // equivalent in the future.
|
| if (best_score <= 0) {
|
| if (logger)
|
| logger->LogNumber(Logger::STRING_BEST_SCORE, best_score);
|
| return;
|
| }
|
|
|
| - for (std::vector<PasswordForm>::const_iterator it =
|
| - credentials_to_keep.begin();
|
| - it != credentials_to_keep.end(); ++it) {
|
| - // If we don't already have a result with the same username, add the
|
| - // lower-scored match (if it had equal score it would already be in
|
| - // best_matches_).
|
| - if (best_matches_.find(it->username_value) == best_matches_.end())
|
| - best_matches_[it->username_value] = new PasswordForm(*it);
|
| + // Start the |best_matches_| with the best-scoring normal credentials and save
|
| + // the worse-scoring "protected" ones for later.
|
| + ScopedVector<PasswordForm> protected_credentials;
|
| + for (size_t i = 0; i < logins_result.size(); ++i) {
|
| + if (credential_scores[i] < 0)
|
| + continue;
|
| + if (credential_scores[i] < best_score) {
|
| + // Empty path matches are most commonly imports from Firefox, and
|
| + // generally useful to autofill. Blacklisted entries are only meaningful
|
| + // in the absence of non-blacklisted entries, in which case they need no
|
| + // protection to become |best_matches_|. TODO(timsteele): Bug 1269400. We
|
| + // probably should do something more elegant for any shorter-path match
|
| + // instead of explicitly handling empty path matches.
|
| + bool is_credential_protected =
|
| + observed_form_.scheme == PasswordForm::SCHEME_HTML &&
|
| + StartsWithASCII("/", logins_result[i]->origin.path(), true) &&
|
| + credential_scores[i] > 0 && !logins_result[i]->blacklisted_by_user;
|
| + // Passwords generated on a signup form must show on a login form even if
|
| + // there are better-matching saved credentials. TODO(gcasto): We don't
|
| + // want to cut credentials that were saved on signup forms even if they
|
| + // weren't generated, but currently it's hard to distinguish between those
|
| + // forms and two different login forms on the same domain. Filed
|
| + // http://crbug.com/294468 to look into this.
|
| + is_credential_protected |=
|
| + logins_result[i]->type == PasswordForm::TYPE_GENERATED;
|
| +
|
| + if (is_credential_protected) {
|
| + protected_credentials.push_back(logins_result[i]);
|
| + logins_result[i] = nullptr;
|
| + }
|
| + continue;
|
| + }
|
| +
|
| + // If there is another best-score match for the same username, replace it.
|
| + // TODO(vabr): Spare the replacing and keep the first instead of the last
|
| + // candidate.
|
| + auto& best_match = best_matches_[logins_result[i]->username_value];
|
| + if (best_match == preferred_match_)
|
| + preferred_match_ = nullptr;
|
| + delete best_match;
|
| +
|
| + best_match = logins_result[i];
|
| + logins_result[i] = nullptr;
|
| + preferred_match_ = best_match->preferred ? best_match : preferred_match_;
|
| + }
|
| +
|
| + // Add the protected results if we don't already have a result with the same
|
| + // username.
|
| + for (auto& protege : protected_credentials) {
|
| + auto& corresponding_best_match = best_matches_[protege->username_value];
|
| + if (!corresponding_best_match) {
|
| + corresponding_best_match = protege;
|
| + protege = nullptr;
|
| + }
|
| }
|
|
|
| + client_->AutofillResultsComputed();
|
| +
|
| UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsNotShown",
|
| logins_result_size - best_matches_.size());
|
|
|
|
|