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