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 3 // found in the LICENSE file. 4 5 #include "components/password_manager/core/browser/password_form_manager.h" 6 7 #include <algorithm> 8 9 #include "base/metrics/histogram.h" 10 #include "base/strings/string_split.h" 11 #include "base/strings/string_util.h" 12 #include "components/autofill/core/browser/autofill_manager.h" 13 #include "components/autofill/core/browser/form_structure.h" 14 #include "components/autofill/core/browser/validation.h" 15 #include "components/autofill/core/common/password_form.h" 16 #include "components/password_manager/core/browser/password_manager.h" 17 #include "components/password_manager/core/browser/password_manager_client.h" 18 #include "components/password_manager/core/browser/password_manager_driver.h" 19 #include "components/password_manager/core/browser/password_store.h" 20 21 using autofill::FormStructure; 22 using autofill::PasswordForm; 23 using autofill::PasswordFormMap; 24 using base::Time; 25 26 namespace password_manager { 27 28 namespace { 29 30 enum PasswordGenerationSubmissionEvent { 31 // Generated password was submitted and saved. 32 PASSWORD_SUBMITTED, 33 34 // Generated password submission failed. These passwords aren't saved. 35 PASSWORD_SUBMISSION_FAILED, 36 37 // Generated password was not submitted before navigation. Currently these 38 // passwords are not saved. 39 PASSWORD_NOT_SUBMITTED, 40 41 // Generated password was overridden by a non-generated one. This generally 42 // signals that the user was unhappy with the generated password for some 43 // reason. 44 PASSWORD_OVERRIDDEN, 45 46 // Number of enum entries, used for UMA histogram reporting macros. 47 SUBMISSION_EVENT_ENUM_COUNT 48 }; 49 50 void LogPasswordGenerationSubmissionEvent( 51 PasswordGenerationSubmissionEvent event) { 52 UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.SubmissionEvent", 53 event, SUBMISSION_EVENT_ENUM_COUNT); 54 } 55 56 } // namespace 57 58 PasswordFormManager::PasswordFormManager(PasswordManager* password_manager, 59 PasswordManagerClient* client, 60 PasswordManagerDriver* driver, 61 const PasswordForm& observed_form, 62 bool ssl_valid) 63 : best_matches_deleter_(&best_matches_), 64 observed_form_(observed_form), 65 is_new_login_(true), 66 has_generated_password_(false), 67 password_manager_(password_manager), 68 preferred_match_(NULL), 69 state_(PRE_MATCHING_PHASE), 70 client_(client), 71 driver_(driver), 72 manager_action_(kManagerActionNone), 73 user_action_(kUserActionNone), 74 submit_result_(kSubmitResultNotSubmitted) { 75 if (observed_form_.origin.is_valid()) 76 base::SplitString(observed_form_.origin.path(), '/', &form_path_tokens_); 77 observed_form_.ssl_valid = ssl_valid; 78 } 79 80 PasswordFormManager::~PasswordFormManager() { 81 UMA_HISTOGRAM_ENUMERATION( 82 "PasswordManager.ActionsTakenV3", GetActionsTaken(), kMaxNumActionsTaken); 83 if (has_generated_password_ && submit_result_ == kSubmitResultNotSubmitted) 84 LogPasswordGenerationSubmissionEvent(PASSWORD_NOT_SUBMITTED); 85 } 86 87 int PasswordFormManager::GetActionsTaken() { 88 return user_action_ + kUserActionMax * (manager_action_ + 89 kManagerActionMax * submit_result_); 90 }; 91 92 // TODO(timsteele): use a hash of some sort in the future? 93 bool PasswordFormManager::DoesManage(const PasswordForm& form, 94 ActionMatch action_match) const { 95 if (form.scheme != PasswordForm::SCHEME_HTML) 96 return observed_form_.signon_realm == form.signon_realm; 97 98 // HTML form case. 99 // At a minimum, username and password element must match. 100 if (!((form.username_element == observed_form_.username_element) && 101 (form.password_element == observed_form_.password_element))) { 102 return false; 103 } 104 105 // When action match is required, the action URL must match, but 106 // the form is allowed to have an empty action URL (See bug 1107719). 107 // Otherwise ignore action URL, this is to allow saving password form with 108 // dynamically changed action URL (See bug 27246). 109 if (form.action.is_valid() && (form.action != observed_form_.action)) { 110 if (action_match == ACTION_MATCH_REQUIRED) 111 return false; 112 } 113 114 // If this is a replay of the same form in the case a user entered an invalid 115 // password, the origin of the new form may equal the action of the "first" 116 // form. 117 if (!((form.origin == observed_form_.origin) || 118 (form.origin == observed_form_.action))) { 119 if (form.origin.SchemeIsSecure() && 120 !observed_form_.origin.SchemeIsSecure()) { 121 // Compare origins, ignoring scheme. There is no easy way to do this 122 // with GURL because clearing the scheme would result in an invalid url. 123 // This is for some sites (such as Hotmail) that begin on an http page and 124 // head to https for the retry when password was invalid. 125 std::string::const_iterator after_scheme1 = form.origin.spec().begin() + 126 form.origin.scheme().length(); 127 std::string::const_iterator after_scheme2 = 128 observed_form_.origin.spec().begin() + 129 observed_form_.origin.scheme().length(); 130 return std::search(after_scheme1, 131 form.origin.spec().end(), 132 after_scheme2, 133 observed_form_.origin.spec().end()) 134 != form.origin.spec().end(); 135 } 136 return false; 137 } 138 return true; 139 } 140 141 bool PasswordFormManager::IsBlacklisted() { 142 DCHECK_EQ(state_, POST_MATCHING_PHASE); 143 if (preferred_match_ && preferred_match_->blacklisted_by_user) 144 return true; 145 return false; 146 } 147 148 void PasswordFormManager::PermanentlyBlacklist() { 149 DCHECK_EQ(state_, POST_MATCHING_PHASE); 150 151 // Configure the form about to be saved for blacklist status. 152 pending_credentials_.preferred = true; 153 pending_credentials_.blacklisted_by_user = true; 154 pending_credentials_.username_value.clear(); 155 pending_credentials_.password_value.clear(); 156 157 // Retroactively forget existing matches for this form, so we NEVER prompt or 158 // autofill it again. 159 int num_passwords_deleted = 0; 160 if (!best_matches_.empty()) { 161 PasswordFormMap::const_iterator iter; 162 PasswordStore* password_store = client_->GetPasswordStore(); 163 if (!password_store) { 164 NOTREACHED(); 165 return; 166 } 167 for (iter = best_matches_.begin(); iter != best_matches_.end(); ++iter) { 168 // We want to remove existing matches for this form so that the exact 169 // origin match with |blackisted_by_user == true| is the only result that 170 // shows up in the future for this origin URL. However, we don't want to 171 // delete logins that were actually saved on a different page (hence with 172 // different origin URL) and just happened to match this form because of 173 // the scoring algorithm. See bug 1204493. 174 if (iter->second->origin == observed_form_.origin) { 175 password_store->RemoveLogin(*iter->second); 176 ++num_passwords_deleted; 177 } 178 } 179 } 180 181 UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsDeletedWhenBlacklisting", 182 num_passwords_deleted); 183 184 // Save the pending_credentials_ entry marked as blacklisted. 185 SaveAsNewLogin(false); 186 } 187 188 void PasswordFormManager::SetUseAdditionalPasswordAuthentication( 189 bool use_additional_authentication) { 190 pending_credentials_.use_additional_authentication = 191 use_additional_authentication; 192 } 193 194 bool PasswordFormManager::IsNewLogin() { 195 DCHECK_EQ(state_, POST_MATCHING_PHASE); 196 return is_new_login_; 197 } 198 199 bool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() { 200 return pending_credentials_.IsPublicSuffixMatch(); 201 } 202 203 void PasswordFormManager::SetHasGeneratedPassword() { 204 has_generated_password_ = true; 205 } 206 207 bool PasswordFormManager::HasGeneratedPassword() { 208 // This check is permissive, as the user may have generated a password and 209 // then edited it in the form itself. However, even in this case the user 210 // has already given consent, so we treat these cases the same. 211 return has_generated_password_; 212 } 213 214 bool PasswordFormManager::HasValidPasswordForm() { 215 DCHECK_EQ(state_, POST_MATCHING_PHASE); 216 // Non-HTML password forms (primarily HTTP and FTP autentication) 217 // do not contain username_element and password_element values. 218 if (observed_form_.scheme != PasswordForm::SCHEME_HTML) 219 return true; 220 return !observed_form_.username_element.empty() && 221 !observed_form_.password_element.empty(); 222 } 223 224 void PasswordFormManager::ProvisionallySave( 225 const PasswordForm& credentials, 226 OtherPossibleUsernamesAction action) { 227 DCHECK_EQ(state_, POST_MATCHING_PHASE); 228 DCHECK(DoesManage(credentials, ACTION_MATCH_NOT_REQUIRED)); 229 230 // Make sure the important fields stay the same as the initially observed or 231 // autofilled ones, as they may have changed if the user experienced a login 232 // failure. 233 // Look for these credentials in the list containing auto-fill entries. 234 PasswordFormMap::const_iterator it = 235 best_matches_.find(credentials.username_value); 236 if (it != best_matches_.end()) { 237 // The user signed in with a login we autofilled. 238 pending_credentials_ = *it->second; 239 240 // Public suffix matches should always be new logins, since we want to store 241 // them so they can automatically be filled in later. 242 is_new_login_ = IsPendingCredentialsPublicSuffixMatch(); 243 if (is_new_login_) 244 user_action_ = kUserActionChoosePslMatch; 245 246 // Check to see if we're using a known username but a new password. 247 if (pending_credentials_.password_value != credentials.password_value) 248 user_action_ = kUserActionOverridePassword; 249 } else if (action == ALLOW_OTHER_POSSIBLE_USERNAMES && 250 UpdatePendingCredentialsIfOtherPossibleUsername( 251 credentials.username_value)) { 252 // |pending_credentials_| is now set. Note we don't update 253 // |pending_credentials_.username_value| to |credentials.username_value| 254 // yet because we need to keep the original username to modify the stored 255 // credential. 256 selected_username_ = credentials.username_value; 257 is_new_login_ = false; 258 } else { 259 // User typed in a new, unknown username. 260 user_action_ = kUserActionOverrideUsernameAndPassword; 261 pending_credentials_ = observed_form_; 262 pending_credentials_.username_value = credentials.username_value; 263 pending_credentials_.other_possible_usernames = 264 credentials.other_possible_usernames; 265 } 266 267 pending_credentials_.action = credentials.action; 268 // If the user selected credentials we autofilled from a PasswordForm 269 // that contained no action URL (IE6/7 imported passwords, for example), 270 // bless it with the action URL from the observed form. See bug 1107719. 271 if (pending_credentials_.action.is_empty()) 272 pending_credentials_.action = observed_form_.action; 273 274 pending_credentials_.password_value = credentials.password_value; 275 pending_credentials_.preferred = credentials.preferred; 276 277 if (user_action_ == kUserActionOverridePassword && 278 pending_credentials_.type == PasswordForm::TYPE_GENERATED && 279 !has_generated_password_) { 280 LogPasswordGenerationSubmissionEvent(PASSWORD_OVERRIDDEN); 281 } 282 283 if (has_generated_password_) 284 pending_credentials_.type = PasswordForm::TYPE_GENERATED; 285 } 286 287 void PasswordFormManager::Save() { 288 DCHECK_EQ(state_, POST_MATCHING_PHASE); 289 DCHECK(!driver_->IsOffTheRecord()); 290 291 if (IsNewLogin()) 292 SaveAsNewLogin(true); 293 else 294 UpdateLogin(); 295 } 296 297 void PasswordFormManager::FetchMatchingLoginsFromPasswordStore( 298 PasswordStore::AuthorizationPromptPolicy prompt_policy) { 299 DCHECK_EQ(state_, PRE_MATCHING_PHASE); 300 state_ = MATCHING_PHASE; 301 PasswordStore* password_store = client_->GetPasswordStore(); 302 if (!password_store) { 303 NOTREACHED(); 304 return; 305 } 306 password_store->GetLogins(observed_form_, prompt_policy, this); 307 } 308 309 bool PasswordFormManager::HasCompletedMatching() { 310 return state_ == POST_MATCHING_PHASE; 311 } 312 313 void PasswordFormManager::OnRequestDone( 314 const std::vector<PasswordForm*>& logins_result) { 315 // Note that the result gets deleted after this call completes, but we own 316 // the PasswordForm objects pointed to by the result vector, thus we keep 317 // copies to a minimum here. 318 319 int best_score = 0; 320 // These credentials will be in the final result regardless of score. 321 std::vector<PasswordForm> credentials_to_keep; 322 for (size_t i = 0; i < logins_result.size(); i++) { 323 if (IgnoreResult(*logins_result[i])) { 324 delete logins_result[i]; 325 continue; 326 } 327 // Score and update best matches. 328 int current_score = ScoreResult(*logins_result[i]); 329 // This check is here so we can append empty path matches in the event 330 // they don't score as high as others and aren't added to best_matches_. 331 // This is most commonly imported firefox logins. We skip blacklisted 332 // ones because clearly we don't want to autofill them, and secondly 333 // because they only mean something when we have no other matches already 334 // saved in Chrome - in which case they'll make it through the regular 335 // scoring flow below by design. Note signon_realm == origin implies empty 336 // path logins_result, since signon_realm is a prefix of origin for HTML 337 // password forms. 338 // TODO(timsteele): Bug 1269400. We probably should do something more 339 // elegant for any shorter-path match instead of explicitly handling empty 340 // path matches. 341 if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && 342 (observed_form_.signon_realm == logins_result[i]->origin.spec()) && 343 (current_score > 0) && (!logins_result[i]->blacklisted_by_user)) { 344 credentials_to_keep.push_back(*logins_result[i]); 345 } 346 347 // Always keep generated passwords as part of the result set. If a user 348 // generates a password on a signup form, it should show on a login form 349 // even if they have a previous login saved. 350 // TODO(gcasto): We don't want to cut credentials that were saved on signup 351 // forms even if they weren't generated, but currently it's hard to 352 // distinguish between those forms and two different login forms on the 353 // same domain. Filed http://crbug.com/294468 to look into this. 354 if (logins_result[i]->type == PasswordForm::TYPE_GENERATED) 355 credentials_to_keep.push_back(*logins_result[i]); 356 357 if (current_score < best_score) { 358 delete logins_result[i]; 359 continue; 360 } 361 if (current_score == best_score) { 362 best_matches_[logins_result[i]->username_value] = logins_result[i]; 363 } else if (current_score > best_score) { 364 best_score = current_score; 365 // This new login has a better score than all those up to this point 366 // Note 'this' owns all the PasswordForms in best_matches_. 367 STLDeleteValues(&best_matches_); 368 best_matches_.clear(); 369 preferred_match_ = NULL; // Don't delete, its owned by best_matches_. 370 best_matches_[logins_result[i]->username_value] = logins_result[i]; 371 } 372 preferred_match_ = logins_result[i]->preferred ? logins_result[i] 373 : preferred_match_; 374 } 375 // We're done matching now. 376 state_ = POST_MATCHING_PHASE; 377 378 if (best_score <= 0) { 379 return; 380 } 381 382 for (std::vector<PasswordForm>::const_iterator it = 383 credentials_to_keep.begin(); 384 it != credentials_to_keep.end(); ++it) { 385 // If we don't already have a result with the same username, add the 386 // lower-scored match (if it had equal score it would already be in 387 // best_matches_). 388 if (best_matches_.find(it->username_value) == best_matches_.end()) 389 best_matches_[it->username_value] = new PasswordForm(*it); 390 } 391 392 UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsNotShown", 393 logins_result.size() - best_matches_.size()); 394 395 // It is possible we have at least one match but have no preferred_match_, 396 // because a user may have chosen to 'Forget' the preferred match. So we 397 // just pick the first one and whichever the user selects for submit will 398 // be saved as preferred. 399 DCHECK(!best_matches_.empty()); 400 if (!preferred_match_) 401 preferred_match_ = best_matches_.begin()->second; 402 403 // Check to see if the user told us to ignore this site in the past. 404 if (preferred_match_->blacklisted_by_user) { 405 client_->PasswordAutofillWasBlocked(best_matches_); 406 manager_action_ = kManagerActionBlacklisted; 407 return; 408 } 409 410 // If not blacklisted, inform the driver that password generation is allowed 411 // for |observed_form_|. 412 driver_->AllowPasswordGenerationForForm(&observed_form_); 413 414 // Proceed to autofill. 415 // Note that we provide the choices but don't actually prefill a value if: 416 // (1) we are in Incognito mode, (2) the ACTION paths don't match, 417 // or (3) if it matched using public suffix domain matching. 418 bool wait_for_username = 419 driver_->IsOffTheRecord() || 420 observed_form_.action.GetWithEmptyPath() != 421 preferred_match_->action.GetWithEmptyPath() || 422 preferred_match_->IsPublicSuffixMatch(); 423 if (wait_for_username) 424 manager_action_ = kManagerActionNone; 425 else 426 manager_action_ = kManagerActionAutofilled; 427 password_manager_->Autofill(observed_form_, best_matches_, 428 *preferred_match_, wait_for_username); 429 } 430 431 void PasswordFormManager::OnGetPasswordStoreResults( 432 const std::vector<autofill::PasswordForm*>& results) { 433 DCHECK_EQ(state_, MATCHING_PHASE); 434 435 if (results.empty()) { 436 state_ = POST_MATCHING_PHASE; 437 // No result means that we visit this site the first time so we don't need 438 // to check whether this site is blacklisted or not. Just send a message 439 // to allow password generation. 440 driver_->AllowPasswordGenerationForForm(&observed_form_); 441 return; 442 } 443 OnRequestDone(results); 444 } 445 446 bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { 447 // Ignore change password forms until we have some change password 448 // functionality 449 if (observed_form_.old_password_element.length() != 0) { 450 return true; 451 } 452 // Don't match an invalid SSL form with one saved under secure 453 // circumstances. 454 if (form.ssl_valid && !observed_form_.ssl_valid) { 455 return true; 456 } 457 return false; 458 } 459 460 void PasswordFormManager::SaveAsNewLogin(bool reset_preferred_login) { 461 DCHECK_EQ(state_, POST_MATCHING_PHASE); 462 DCHECK(IsNewLogin()); 463 // The new_form is being used to sign in, so it is preferred. 464 DCHECK(pending_credentials_.preferred); 465 // new_form contains the same basic data as observed_form_ (because its the 466 // same form), but with the newly added credentials. 467 468 DCHECK(!driver_->IsOffTheRecord()); 469 470 PasswordStore* password_store = client_->GetPasswordStore(); 471 if (!password_store) { 472 NOTREACHED(); 473 return; 474 } 475 476 pending_credentials_.date_created = Time::Now(); 477 SanitizePossibleUsernames(&pending_credentials_); 478 password_store->AddLogin(pending_credentials_); 479 480 if (reset_preferred_login) { 481 UpdatePreferredLoginState(password_store); 482 } 483 } 484 485 void PasswordFormManager::SanitizePossibleUsernames(PasswordForm* form) { 486 // Remove any possible usernames that could be credit cards or SSN for privacy 487 // reasons. Also remove duplicates, both in other_possible_usernames and 488 // between other_possible_usernames and username_value. 489 std::set<base::string16> set; 490 for (std::vector<base::string16>::iterator it = 491 form->other_possible_usernames.begin(); 492 it != form->other_possible_usernames.end(); ++it) { 493 if (!autofill::IsValidCreditCardNumber(*it) && !autofill::IsSSN(*it)) 494 set.insert(*it); 495 } 496 set.erase(form->username_value); 497 std::vector<base::string16> temp(set.begin(), set.end()); 498 form->other_possible_usernames.swap(temp); 499 } 500 501 void PasswordFormManager::UpdatePreferredLoginState( 502 PasswordStore* password_store) { 503 DCHECK(password_store); 504 PasswordFormMap::iterator iter; 505 for (iter = best_matches_.begin(); iter != best_matches_.end(); iter++) { 506 if (iter->second->username_value != pending_credentials_.username_value && 507 iter->second->preferred) { 508 // This wasn't the selected login but it used to be preferred. 509 iter->second->preferred = false; 510 if (user_action_ == kUserActionNone) 511 user_action_ = kUserActionChoose; 512 password_store->UpdateLogin(*iter->second); 513 } 514 } 515 } 516 517 void PasswordFormManager::UpdateLogin() { 518 DCHECK_EQ(state_, POST_MATCHING_PHASE); 519 DCHECK(preferred_match_); 520 // If we're doing an Update, we either autofilled correctly and need to 521 // update the stats, or the user typed in a new password for autofilled 522 // username, or the user selected one of the non-preferred matches, 523 // thus requiring a swap of preferred bits. 524 DCHECK(!IsNewLogin() && pending_credentials_.preferred); 525 DCHECK(!driver_->IsOffTheRecord()); 526 527 PasswordStore* password_store = client_->GetPasswordStore(); 528 if (!password_store) { 529 NOTREACHED(); 530 return; 531 } 532 533 // Update metadata. 534 ++pending_credentials_.times_used; 535 536 // Check to see if this form is a candidate for password generation. 537 CheckForAccountCreationForm(pending_credentials_, observed_form_); 538 539 UpdatePreferredLoginState(password_store); 540 541 // Remove alternate usernames. At this point we assume that we have found 542 // the right username. 543 pending_credentials_.other_possible_usernames.clear(); 544 545 // Update the new preferred login. 546 if (!selected_username_.empty()) { 547 // An other possible username is selected. We set this selected username 548 // as the real username. The PasswordStore API isn't designed to update 549 // username, so we delete the old credentials and add a new one instead. 550 password_store->RemoveLogin(pending_credentials_); 551 pending_credentials_.username_value = selected_username_; 552 password_store->AddLogin(pending_credentials_); 553 } else if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && 554 (observed_form_.origin.spec().length() > 555 observed_form_.signon_realm.length()) && 556 (observed_form_.signon_realm == 557 pending_credentials_.origin.spec())) { 558 // Note origin.spec().length > signon_realm.length implies the origin has a 559 // path, since signon_realm is a prefix of origin for HTML password forms. 560 // 561 // The user logged in successfully with one of our autofilled logins on a 562 // page with non-empty path, but the autofilled entry was initially saved/ 563 // imported with an empty path. Rather than just mark this entry preferred, 564 // we create a more specific copy for this exact page and leave the "master" 565 // unchanged. This is to prevent the case where that master login is used 566 // on several sites (e.g site.com/a and site.com/b) but the user actually 567 // has a different preference on each site. For example, on /a, he wants the 568 // general empty-path login so it is flagged as preferred, but on /b he logs 569 // in with a different saved entry - we don't want to remove the preferred 570 // status of the former because upon return to /a it won't be the default- 571 // fill match. 572 // TODO(timsteele): Bug 1188626 - expire the master copies. 573 PasswordForm copy(pending_credentials_); 574 copy.origin = observed_form_.origin; 575 copy.action = observed_form_.action; 576 password_store->AddLogin(copy); 577 } else if (pending_credentials_.password_element.empty() || 578 pending_credentials_.username_element.empty() || 579 pending_credentials_.submit_element.empty()) { 580 // password_element and username_element can't be updated because they are 581 // part of Sync and PasswordStore primary key. Thus, we must delete the old 582 // one and add again. 583 password_store->RemoveLogin(pending_credentials_); 584 pending_credentials_.password_element = observed_form_.password_element; 585 pending_credentials_.username_element = observed_form_.username_element; 586 pending_credentials_.submit_element = observed_form_.submit_element; 587 password_store->AddLogin(pending_credentials_); 588 } else { 589 password_store->UpdateLogin(pending_credentials_); 590 } 591 } 592 593 bool PasswordFormManager::UpdatePendingCredentialsIfOtherPossibleUsername( 594 const base::string16& username) { 595 for (PasswordFormMap::const_iterator it = best_matches_.begin(); 596 it != best_matches_.end(); ++it) { 597 for (size_t i = 0; i < it->second->other_possible_usernames.size(); ++i) { 598 if (it->second->other_possible_usernames[i] == username) { 599 pending_credentials_ = *it->second; 600 return true; 601 } 602 } 603 } 604 return false; 605 } 606 607 void PasswordFormManager::CheckForAccountCreationForm( 608 const PasswordForm& pending, const PasswordForm& observed) { 609 // We check to see if the saved form_data is the same as the observed 610 // form_data, which should never be true for passwords saved on account 611 // creation forms. This check is only made the first time a password is used 612 // to cut down on false positives. Specifically a site may have multiple login 613 // forms with different markup, which might look similar to a signup form. 614 if (pending.times_used == 1) { 615 FormStructure pending_structure(pending.form_data); 616 FormStructure observed_structure(observed.form_data); 617 // Ignore |pending_structure| if its FormData has no fields. This is to 618 // weed out those credentials that were saved before FormData was added 619 // to PasswordForm. Even without this check, these FormStructure's won't 620 // be uploaded, but it makes it hard to see if we are encountering 621 // unexpected errors. 622 if (!pending.form_data.fields.empty() && 623 pending_structure.FormSignature() != 624 observed_structure.FormSignature()) { 625 autofill::AutofillManager* autofill_manager; 626 if ((autofill_manager = driver_->GetAutofillManager())) { 627 // Note that this doesn't guarantee that the upload succeeded, only that 628 // |pending.form_data| is considered uploadable. 629 bool success = 630 autofill_manager->UploadPasswordGenerationForm(pending.form_data); 631 UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success); 632 } 633 } 634 } 635 } 636 637 int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { 638 DCHECK_EQ(state_, MATCHING_PHASE); 639 // For scoring of candidate login data: 640 // The most important element that should match is the signon_realm followed 641 // by the origin, the action, the password name, the submit button name, and 642 // finally the username input field name. 643 // If public suffix origin match was not used, it gives an addition of 644 // 128 (1 << 7). 645 // Exact origin match gives an addition of 64 (1 << 6) + # of matching url 646 // dirs. 647 // Partial match gives an addition of 32 (1 << 5) + # matching url dirs 648 // That way, a partial match cannot trump an exact match even if 649 // the partial one matches all other attributes (action, elements) (and 650 // regardless of the matching depth in the URL path). 651 int score = 0; 652 if (!candidate.IsPublicSuffixMatch()) { 653 score += 1 << 7; 654 } 655 if (candidate.origin == observed_form_.origin) { 656 // This check is here for the most common case which 657 // is we have a single match in the db for the given host, 658 // so we don't generally need to walk the entire URL path (the else 659 // clause). 660 score += (1 << 6) + static_cast<int>(form_path_tokens_.size()); 661 } else { 662 // Walk the origin URL paths one directory at a time to see how 663 // deep the two match. 664 std::vector<std::string> candidate_path_tokens; 665 base::SplitString(candidate.origin.path(), '/', &candidate_path_tokens); 666 size_t depth = 0; 667 size_t max_dirs = std::min(form_path_tokens_.size(), 668 candidate_path_tokens.size()); 669 while ((depth < max_dirs) && (form_path_tokens_[depth] == 670 candidate_path_tokens[depth])) { 671 depth++; 672 score++; 673 } 674 // do we have a partial match? 675 score += (depth > 0) ? 1 << 5 : 0; 676 } 677 if (observed_form_.scheme == PasswordForm::SCHEME_HTML) { 678 if (candidate.action == observed_form_.action) 679 score += 1 << 3; 680 if (candidate.password_element == observed_form_.password_element) 681 score += 1 << 2; 682 if (candidate.submit_element == observed_form_.submit_element) 683 score += 1 << 1; 684 if (candidate.username_element == observed_form_.username_element) 685 score += 1 << 0; 686 } 687 688 return score; 689 } 690 691 void PasswordFormManager::SubmitPassed() { 692 submit_result_ = kSubmitResultPassed; 693 if (has_generated_password_) 694 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED); 695 } 696 697 void PasswordFormManager::SubmitFailed() { 698 submit_result_ = kSubmitResultFailed; 699 if (has_generated_password_) 700 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED); 701 } 702 703 } // namespace password_manager 704