password_form_manager.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 password_manager {
27
28namespace {
29
30enum 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
50void LogPasswordGenerationSubmissionEvent(
51    PasswordGenerationSubmissionEvent event) {
52  UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.SubmissionEvent",
53                            event, SUBMISSION_EVENT_ENUM_COUNT);
54}
55
56}  // namespace
57
58PasswordFormManager::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
80PasswordFormManager::~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
87int 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?
93bool 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
141bool 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
148void 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
188void PasswordFormManager::SetUseAdditionalPasswordAuthentication(
189    bool use_additional_authentication) {
190  pending_credentials_.use_additional_authentication =
191      use_additional_authentication;
192}
193
194bool PasswordFormManager::IsNewLogin() {
195  DCHECK_EQ(state_, POST_MATCHING_PHASE);
196  return is_new_login_;
197}
198
199bool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() {
200  return pending_credentials_.IsPublicSuffixMatch();
201}
202
203void PasswordFormManager::SetHasGeneratedPassword() {
204  has_generated_password_ = true;
205}
206
207bool 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
214bool 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
224void 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
287void 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
297void 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
309bool PasswordFormManager::HasCompletedMatching() {
310  return state_ == POST_MATCHING_PHASE;
311}
312
313void 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
431void 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
446bool 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
460void 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
485void 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
501void 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
517void 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
593bool 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
607void 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
637int 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
691void PasswordFormManager::SubmitPassed() {
692  submit_result_ = kSubmitResultPassed;
693  if (has_generated_password_)
694    LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED);
695}
696
697void PasswordFormManager::SubmitFailed() {
698  submit_result_ = kSubmitResultFailed;
699  if (has_generated_password_)
700    LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED);
701}
702
703}  // namespace password_manager
704