• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LogPasswordGenerationSubmissionEvent(PasswordGenerationSubmissionEvent event)50 void LogPasswordGenerationSubmissionEvent(
51     PasswordGenerationSubmissionEvent event) {
52   UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.SubmissionEvent",
53                             event, SUBMISSION_EVENT_ENUM_COUNT);
54 }
55 
56 }  // namespace
57 
PasswordFormManager(PasswordManager * password_manager,PasswordManagerClient * client,PasswordManagerDriver * driver,const PasswordForm & observed_form,bool ssl_valid)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 
~PasswordFormManager()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 
GetActionsTaken()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?
DoesManage(const PasswordForm & form,ActionMatch action_match) const93 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 
IsBlacklisted()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 
PermanentlyBlacklist()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 
SetUseAdditionalPasswordAuthentication(bool use_additional_authentication)188 void PasswordFormManager::SetUseAdditionalPasswordAuthentication(
189     bool use_additional_authentication) {
190   pending_credentials_.use_additional_authentication =
191       use_additional_authentication;
192 }
193 
IsNewLogin()194 bool PasswordFormManager::IsNewLogin() {
195   DCHECK_EQ(state_, POST_MATCHING_PHASE);
196   return is_new_login_;
197 }
198 
IsPendingCredentialsPublicSuffixMatch()199 bool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() {
200   return pending_credentials_.IsPublicSuffixMatch();
201 }
202 
SetHasGeneratedPassword()203 void PasswordFormManager::SetHasGeneratedPassword() {
204   has_generated_password_ = true;
205 }
206 
HasGeneratedPassword()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 
HasValidPasswordForm()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 
ProvisionallySave(const PasswordForm & credentials,OtherPossibleUsernamesAction action)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 
Save()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 
FetchMatchingLoginsFromPasswordStore(PasswordStore::AuthorizationPromptPolicy prompt_policy)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 
HasCompletedMatching()309 bool PasswordFormManager::HasCompletedMatching() {
310   return state_ == POST_MATCHING_PHASE;
311 }
312 
OnRequestDone(const std::vector<PasswordForm * > & logins_result)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 
OnGetPasswordStoreResults(const std::vector<autofill::PasswordForm * > & results)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 
IgnoreResult(const PasswordForm & form) const446 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 
SaveAsNewLogin(bool reset_preferred_login)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 
SanitizePossibleUsernames(PasswordForm * form)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 
UpdatePreferredLoginState(PasswordStore * password_store)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 
UpdateLogin()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 
UpdatePendingCredentialsIfOtherPossibleUsername(const base::string16 & username)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 
CheckForAccountCreationForm(const PasswordForm & pending,const PasswordForm & observed)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 
ScoreResult(const PasswordForm & candidate) const637 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 
SubmitPassed()691 void PasswordFormManager::SubmitPassed() {
692   submit_result_ = kSubmitResultPassed;
693   if (has_generated_password_)
694     LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED);
695 }
696 
SubmitFailed()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