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