15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "components/password_manager/core/browser/password_form_manager.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm> 81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <set> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h" 11116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/metrics/user_metrics.h" 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h" 13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 14116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/strings/utf_string_conversions.h" 155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/autofill/core/browser/autofill_manager.h" 16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/autofill/core/browser/form_structure.h" 17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "components/autofill/core/browser/validation.h" 1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "components/autofill/core/common/password_form.h" 19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "components/password_manager/core/browser/password_manager.h" 20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "components/password_manager/core/browser/password_manager_client.h" 21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "components/password_manager/core/browser/password_manager_driver.h" 225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/password_manager/core/browser/password_store.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)using autofill::FormStructure; 2558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using autofill::PasswordForm; 2658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using autofill::PasswordFormMap; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::Time; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochnamespace password_manager { 30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 3123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)namespace { 3223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 3323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)enum PasswordGenerationSubmissionEvent { 3423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // Generated password was submitted and saved. 3523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) PASSWORD_SUBMITTED, 3623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 3723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // Generated password submission failed. These passwords aren't saved. 3823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) PASSWORD_SUBMISSION_FAILED, 3923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 4023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // Generated password was not submitted before navigation. Currently these 4123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // passwords are not saved. 4223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) PASSWORD_NOT_SUBMITTED, 4323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 4423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // Generated password was overridden by a non-generated one. This generally 4523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // signals that the user was unhappy with the generated password for some 4623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // reason. 4723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) PASSWORD_OVERRIDDEN, 4823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 4923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) // Number of enum entries, used for UMA histogram reporting macros. 5023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) SUBMISSION_EVENT_ENUM_COUNT 5123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}; 5223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 5323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void LogPasswordGenerationSubmissionEvent( 5423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) PasswordGenerationSubmissionEvent event) { 5523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.SubmissionEvent", 5623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) event, SUBMISSION_EVENT_ENUM_COUNT); 5723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)} 5823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 59116680a4aac90f2aa7413d9095a592090648e557Ben MurdochPasswordForm CopyAndModifySSLValidity(const PasswordForm& orig, 60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch bool ssl_valid) { 61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch PasswordForm result(orig); 62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result.ssl_valid = ssl_valid; 63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return result; 64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch} 65116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 6623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)} // namespace 6723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordFormManager::PasswordFormManager(PasswordManager* password_manager, 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordManagerClient* client, 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordManagerDriver* driver, 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const PasswordForm& observed_form, 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool ssl_valid) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : best_matches_deleter_(&best_matches_), 74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch observed_form_(CopyAndModifySSLValidity(observed_form, ssl_valid)), 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_new_login_(true), 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) has_generated_password_(false), 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_manager_(password_manager), 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) preferred_match_(NULL), 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_(PRE_MATCHING_PHASE), 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) client_(client), 815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) driver_(driver), 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manager_action_(kManagerActionNone), 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_action_(kUserActionNone), 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) submit_result_(kSubmitResultNotSubmitted) { 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (observed_form_.origin.is_valid()) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(observed_form_.origin.path(), '/', &form_path_tokens_); 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PasswordFormManager::~PasswordFormManager() { 90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_ENUMERATION( 91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "PasswordManager.ActionsTakenV3", GetActionsTaken(), kMaxNumActionsTaken); 9223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) if (has_generated_password_ && submit_result_ == kSubmitResultNotSubmitted) 9323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) LogPasswordGenerationSubmissionEvent(PASSWORD_NOT_SUBMITTED); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int PasswordFormManager::GetActionsTaken() { 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return user_action_ + kUserActionMax * (manager_action_ + 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) kManagerActionMax * submit_result_); 991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(timsteele): use a hash of some sort in the future? 102116680a4aac90f2aa7413d9095a592090648e557Ben MurdochPasswordFormManager::MatchResultMask PasswordFormManager::DoesManage( 103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const PasswordForm& form) const { 104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Non-HTML form case. 105116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (observed_form_.scheme != PasswordForm::SCHEME_HTML || 106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch form.scheme != PasswordForm::SCHEME_HTML) { 107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const bool forms_match = observed_form_.signon_realm == form.signon_realm && 108116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch observed_form_.scheme == form.scheme; 109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return forms_match ? RESULT_COMPLETE_MATCH : RESULT_NO_MATCH; 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // HTML form case. 113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch MatchResultMask result = RESULT_NO_MATCH; 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Easiest case of matching origins. 116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch bool origins_match = form.origin == observed_form_.origin; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If this is a replay of the same form in the case a user entered an invalid 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // password, the origin of the new form may equal the action of the "first" 119116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // form instead. 120116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch origins_match = origins_match || (form.origin == observed_form_.action); 121116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Otherwise, if action hosts are the same, the old URL scheme is HTTP while 122116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // the new one is HTTPS, and the new path equals to or extends the old path, 123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // we also consider the actions a match. This is to accommodate cases where 124116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // the original login form is on an HTTP page, but a failed login attempt 125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // redirects to HTTPS (as in http://example.org -> https://example.org/auth). 126116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!origins_match && !observed_form_.origin.SchemeIsSecure() && 127116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch form.origin.SchemeIsSecure()) { 128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const std::string& old_path = observed_form_.origin.path(); 129116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const std::string& new_path = form.origin.path(); 130116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch origins_match = 131116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch observed_form_.origin.host() == form.origin.host() && 132116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch observed_form_.origin.port() == form.origin.port() && 133116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch StartsWithASCII(new_path, old_path, /*case_sensitive=*/true); 134116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 135116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 136116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (form.username_element == observed_form_.username_element && 137116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch form.password_element == observed_form_.password_element && 138116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch origins_match) { 139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result |= RESULT_MANDATORY_ATTRIBUTES_MATCH; 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Note: although saved password forms might actually have an empty action 143116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // URL if they were imported (see bug 1107719), the |form| we see here comes 144116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // never from the password store, and should have an exactly matching action. 145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (form.action == observed_form_.action) 146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result |= RESULT_ACTION_MATCH; 147116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 148116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return result; 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PasswordFormManager::IsBlacklisted() { 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (preferred_match_ && preferred_match_->blacklisted_by_user) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::PermanentlyBlacklist() { 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Configure the form about to be saved for blacklist status. 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.preferred = true; 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.blacklisted_by_user = true; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.username_value.clear(); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.password_value.clear(); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Retroactively forget existing matches for this form, so we NEVER prompt or 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // autofill it again. 1694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) int num_passwords_deleted = 0; 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!best_matches_.empty()) { 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PasswordFormMap::const_iterator iter; 1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordStore* password_store = client_->GetPasswordStore(); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!password_store) { 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (iter = best_matches_.begin(); iter != best_matches_.end(); ++iter) { 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We want to remove existing matches for this form so that the exact 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // origin match with |blackisted_by_user == true| is the only result that 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // shows up in the future for this origin URL. However, we don't want to 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // delete logins that were actually saved on a different page (hence with 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // different origin URL) and just happened to match this form because of 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the scoring algorithm. See bug 1204493. 1844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (iter->second->origin == observed_form_.origin) { 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_store->RemoveLogin(*iter->second); 1864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) ++num_passwords_deleted; 1874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) } 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsDeletedWhenBlacklisting", 1924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) num_passwords_deleted); 1934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Save the pending_credentials_ entry marked as blacklisted. 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SaveAsNewLogin(false); 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void PasswordFormManager::SetUseAdditionalPasswordAuthentication( 1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool use_additional_authentication) { 2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) pending_credentials_.use_additional_authentication = 2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) use_additional_authentication; 2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PasswordFormManager::IsNewLogin() { 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return is_new_login_; 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 209eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() { 210eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return pending_credentials_.IsPublicSuffixMatch(); 211eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 212eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::SetHasGeneratedPassword() { 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) has_generated_password_ = true; 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PasswordFormManager::HasGeneratedPassword() { 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This check is permissive, as the user may have generated a password and 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // then edited it in the form itself. However, even in this case the user 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // has already given consent, so we treat these cases the same. 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return has_generated_password_; 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PasswordFormManager::HasValidPasswordForm() { 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Non-HTML password forms (primarily HTTP and FTP autentication) 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // do not contain username_element and password_element values. 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (observed_form_.scheme != PasswordForm::SCHEME_HTML) 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return !observed_form_.password_element.empty() || 2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci !observed_form_.new_password_element.empty(); 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 234868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void PasswordFormManager::ProvisionallySave( 235868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) const PasswordForm& credentials, 236868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) OtherPossibleUsernamesAction action) { 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 238116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch DCHECK_NE(RESULT_NO_MATCH, DoesManage(credentials)); 239116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 240116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // If this was a sign-up or change password form, we want to persist the new 241116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // password; if this was a login form, then the current password (which might 242116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // still be "new" in the sense that we see these credentials for the first 243116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // time, or that the user manually entered his actual password to overwrite an 244116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // obsolete password we had in the store). 245116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::string16 password_to_save(credentials.new_password_element.empty() ? 246116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch credentials.password_value : credentials.new_password_value); 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make sure the important fields stay the same as the initially observed or 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // autofilled ones, as they may have changed if the user experienced a login 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // failure. 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Look for these credentials in the list containing auto-fill entries. 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PasswordFormMap::const_iterator it = 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) best_matches_.find(credentials.username_value); 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it != best_matches_.end()) { 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The user signed in with a login we autofilled. 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_ = *it->second; 2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) bool password_changed = 2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pending_credentials_.password_value != password_to_save; 2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (IsPendingCredentialsPublicSuffixMatch()) { 2605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // If the autofilled credentials were only a PSL match, store a copy with 2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // the current origin and signon realm. This ensures that on the next 2625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // visit, a precise match is found. 2635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) is_new_login_ = true; 2645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) user_action_ = password_changed ? kUserActionChoosePslMatch 2655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) : kUserActionOverridePassword; 2665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // Normally, the copy of the PSL matched credentials, adapted for the 2675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // current domain, is saved automatically without asking the user, because 2685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // the copy likely represents the same account, i.e., the one for which 2695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // the user already agreed to store a password. 2705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // 2715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // However, if the user changes the suggested password, it might indicate 2725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // that the autofilled credentials and |credentials| actually correspond 2735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // to two different accounts (see http://crbug.com/385619). In that case 2745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // the user should be asked again before saving the password. This is 2755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // ensured by clearing |original_signon_realm| on |pending_credentials_|, 2765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // which unmarks it as a PSL match. 2775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // 2785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // There is still the edge case when the autofilled credentials represent 2795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // the same account as |credentials| but the stored password was out of 2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // date. In that case, the user just had to manually enter the new 2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // password, which is now in |credentials|. The best thing would be to 2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // save automatically, and also update the original credentials. However, 2835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // we have no way to tell if this is the case. This will likely happen 2845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // infrequently, and the inconvenience put on the user by asking them is 2855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // not significant, so we are fine with asking here again. 2865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (password_changed) { 2875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pending_credentials_.original_signon_realm.clear(); 2885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) DCHECK(!IsPendingCredentialsPublicSuffixMatch()); 2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } else { // Not a PSL match. 2915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) is_new_login_ = false; 2925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (password_changed) 2935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) user_action_ = kUserActionOverridePassword; 2945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } else if (action == ALLOW_OTHER_POSSIBLE_USERNAMES && 296868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) UpdatePendingCredentialsIfOtherPossibleUsername( 297868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) credentials.username_value)) { 298868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // |pending_credentials_| is now set. Note we don't update 299868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // |pending_credentials_.username_value| to |credentials.username_value| 300868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // yet because we need to keep the original username to modify the stored 301868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // credential. 302868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) selected_username_ = credentials.username_value; 303868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) is_new_login_ = false; 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // User typed in a new, unknown username. 306effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch user_action_ = kUserActionOverrideUsernameAndPassword; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_ = observed_form_; 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.username_value = credentials.username_value; 309868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_credentials_.other_possible_usernames = 310868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) credentials.other_possible_usernames; 311116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 312116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // The password value will be filled in later, remove any garbage for now. 313116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.password_value.clear(); 314116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.new_password_value.clear(); 315116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 316116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // If this was a sign-up or change password form, the names of the elements 317116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // are likely different than those on a login form, so do not bother saving 318116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // them. We will fill them with meaningful values in UpdateLogin() when the 319116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // user goes onto a real login form for the first time. 320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!credentials.new_password_element.empty()) { 321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.password_element.clear(); 322116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.new_password_element.clear(); 323116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.action = credentials.action; 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the user selected credentials we autofilled from a PasswordForm 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // that contained no action URL (IE6/7 imported passwords, for example), 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // bless it with the action URL from the observed form. See bug 1107719. 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pending_credentials_.action.is_empty()) 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.action = observed_form_.action; 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 333116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.password_value = password_to_save; 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.preferred = credentials.preferred; 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 336effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (user_action_ == kUserActionOverridePassword && 33723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) pending_credentials_.type == PasswordForm::TYPE_GENERATED && 33823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) !has_generated_password_) { 33923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) LogPasswordGenerationSubmissionEvent(PASSWORD_OVERRIDDEN); 34023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) } 34123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (has_generated_password_) 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.type = PasswordForm::TYPE_GENERATED; 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::Save() { 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(!driver_->IsOffTheRecord()); 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsNewLogin()) 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SaveAsNewLogin(true); 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdateLogin(); 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void PasswordFormManager::FetchMatchingLoginsFromPasswordStore( 3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordStore::AuthorizationPromptPolicy prompt_policy) { 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, PRE_MATCHING_PHASE); 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = MATCHING_PHASE; 3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordStore* password_store = client_->GetPasswordStore(); 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!password_store) { 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) password_store->GetLogins(observed_form_, prompt_policy, this); 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PasswordFormManager::HasCompletedMatching() { 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return state_ == POST_MATCHING_PHASE; 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void PasswordFormManager::OnRequestDone( 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::vector<PasswordForm*>& logins_result) { 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Note that the result gets deleted after this call completes, but we own 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the PasswordForm objects pointed to by the result vector, thus we keep 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // copies to a minimum here. 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int best_score = 0; 37968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // These credentials will be in the final result regardless of score. 38068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) std::vector<PasswordForm> credentials_to_keep; 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < logins_result.size(); i++) { 3826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (ShouldIgnoreResult(*logins_result[i])) { 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete logins_result[i]; 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Score and update best matches. 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int current_score = ScoreResult(*logins_result[i]); 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This check is here so we can append empty path matches in the event 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // they don't score as high as others and aren't added to best_matches_. 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This is most commonly imported firefox logins. We skip blacklisted 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ones because clearly we don't want to autofill them, and secondly 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // because they only mean something when we have no other matches already 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // saved in Chrome - in which case they'll make it through the regular 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // scoring flow below by design. Note signon_realm == origin implies empty 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // path logins_result, since signon_realm is a prefix of origin for HTML 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // password forms. 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(timsteele): Bug 1269400. We probably should do something more 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // elegant for any shorter-path match instead of explicitly handling empty 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // path matches. 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (observed_form_.signon_realm == logins_result[i]->origin.spec()) && 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (current_score > 0) && (!logins_result[i]->blacklisted_by_user)) { 40368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) credentials_to_keep.push_back(*logins_result[i]); 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 40668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // Always keep generated passwords as part of the result set. If a user 40768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // generates a password on a signup form, it should show on a login form 40868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // even if they have a previous login saved. 40968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // TODO(gcasto): We don't want to cut credentials that were saved on signup 41068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // forms even if they weren't generated, but currently it's hard to 41168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // distinguish between those forms and two different login forms on the 41268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // same domain. Filed http://crbug.com/294468 to look into this. 41368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if (logins_result[i]->type == PasswordForm::TYPE_GENERATED) 41468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) credentials_to_keep.push_back(*logins_result[i]); 41568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (current_score < best_score) { 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete logins_result[i]; 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (current_score == best_score) { 4215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) PasswordForm* old_form = best_matches_[logins_result[i]->username_value]; 4225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (old_form) { 4235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (preferred_match_ == old_form) 4245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) preferred_match_ = NULL; 4255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) delete old_form; 4265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) } 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) best_matches_[logins_result[i]->username_value] = logins_result[i]; 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (current_score > best_score) { 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) best_score = current_score; 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This new login has a better score than all those up to this point 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Note 'this' owns all the PasswordForms in best_matches_. 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) STLDeleteValues(&best_matches_); 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) preferred_match_ = NULL; // Don't delete, its owned by best_matches_. 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) best_matches_[logins_result[i]->username_value] = logins_result[i]; 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) preferred_match_ = logins_result[i]->preferred ? logins_result[i] 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : preferred_match_; 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We're done matching now. 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = POST_MATCHING_PHASE; 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) client_->AutofillResultsComputed(); 4436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 4441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // TODO(gcasto): Change this to check that best_matches_ is empty. This should 4451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // be equivalent for the moment, but it's less clear and may not be 4461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // equivalent in the future. 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (best_score <= 0) { 4481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // If no saved forms can be used, then it isn't blacklisted and generation 4491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // should be allowed. 4501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci driver_->AllowPasswordGenerationForForm(observed_form_); 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 45468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) for (std::vector<PasswordForm>::const_iterator it = 45568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) credentials_to_keep.begin(); 45668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) it != credentials_to_keep.end(); ++it) { 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we don't already have a result with the same username, add the 45868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // lower-scored match (if it had equal score it would already be in 45968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // best_matches_). 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (best_matches_.find(it->username_value) == best_matches_.end()) 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) best_matches_[it->username_value] = new PasswordForm(*it); 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) UMA_HISTOGRAM_COUNTS("PasswordManager.NumPasswordsNotShown", 4654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) logins_result.size() - best_matches_.size()); 4664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It is possible we have at least one match but have no preferred_match_, 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // because a user may have chosen to 'Forget' the preferred match. So we 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // just pick the first one and whichever the user selects for submit will 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // be saved as preferred. 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!best_matches_.empty()); 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!preferred_match_) 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) preferred_match_ = best_matches_.begin()->second; 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check to see if the user told us to ignore this site in the past. 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (preferred_match_->blacklisted_by_user) { 477010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) client_->PasswordAutofillWasBlocked(best_matches_); 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manager_action_ = kManagerActionBlacklisted; 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // If not blacklisted, inform the driver that password generation is allowed 4835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // for |observed_form_|. 484116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch driver_->AllowPasswordGenerationForForm(observed_form_); 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Proceed to autofill. 487eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Note that we provide the choices but don't actually prefill a value if: 488eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // (1) we are in Incognito mode, (2) the ACTION paths don't match, 489eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // or (3) if it matched using public suffix domain matching. 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool wait_for_username = 4915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) driver_->IsOffTheRecord() || 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) observed_form_.action.GetWithEmptyPath() != 493eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch preferred_match_->action.GetWithEmptyPath() || 494eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch preferred_match_->IsPublicSuffixMatch(); 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (wait_for_username) 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manager_action_ = kManagerActionNone; 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) manager_action_ = kManagerActionAutofilled; 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_manager_->Autofill(observed_form_, best_matches_, 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *preferred_match_, wait_for_username); 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void PasswordFormManager::OnGetPasswordStoreResults( 50458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) const std::vector<autofill::PasswordForm*>& results) { 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, MATCHING_PHASE); 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (results.empty()) { 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state_ = POST_MATCHING_PHASE; 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No result means that we visit this site the first time so we don't need 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to check whether this site is blacklisted or not. Just send a message 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to allow password generation. 512116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch driver_->AllowPasswordGenerationForForm(observed_form_); 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) OnRequestDone(results); 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool PasswordFormManager::ShouldIgnoreResult(const PasswordForm& form) const { 519116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Do not autofill on sign-up or change password forms (until we have some 520116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // working change password functionality). 521116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!observed_form_.new_password_element.empty()) 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 523116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Don't match an invalid SSL form with one saved under secure circumstances. 524116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (form.ssl_valid && !observed_form_.ssl_valid) 5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 5266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) if (client_->ShouldFilterAutofillResult(form)) 5286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) return true; 5296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) 5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::SaveAsNewLogin(bool reset_preferred_login) { 5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(IsNewLogin()); 5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The new_form is being used to sign in, so it is preferred. 5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(pending_credentials_.preferred); 5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // new_form contains the same basic data as observed_form_ (because its the 5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // same form), but with the newly added credentials. 5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(!driver_->IsOffTheRecord()); 5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordStore* password_store = client_->GetPasswordStore(); 5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!password_store) { 5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Upload credentials the first time they are saved. This data is used 5501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // by password generation to help determine account creation sites. 5511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Blacklisted credentials will never be used, so don't upload a vote for 5521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // them. 5531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!pending_credentials_.blacklisted_by_user) 5541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UploadPasswordForm(pending_credentials_.form_data, autofill::PASSWORD); 5551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pending_credentials_.date_created = Time::Now(); 557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) SanitizePossibleUsernames(&pending_credentials_); 5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_store->AddLogin(pending_credentials_); 5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (reset_preferred_login) { 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdatePreferredLoginState(password_store); 5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void PasswordFormManager::SanitizePossibleUsernames(PasswordForm* form) { 566c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Remove any possible usernames that could be credit cards or SSN for privacy 567868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // reasons. Also remove duplicates, both in other_possible_usernames and 568868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // between other_possible_usernames and username_value. 5695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::set<base::string16> set; 5705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (std::vector<base::string16>::iterator it = 571868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) form->other_possible_usernames.begin(); 572868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) it != form->other_possible_usernames.end(); ++it) { 573c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!autofill::IsValidCreditCardNumber(*it) && !autofill::IsSSN(*it)) 574c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) set.insert(*it); 575c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 576c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) set.erase(form->username_value); 5775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::vector<base::string16> temp(set.begin(), set.end()); 578868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) form->other_possible_usernames.swap(temp); 579c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)} 580c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::UpdatePreferredLoginState( 5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PasswordStore* password_store) { 5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(password_store); 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PasswordFormMap::iterator iter; 5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (iter = best_matches_.begin(); iter != best_matches_.end(); iter++) { 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (iter->second->username_value != pending_credentials_.username_value && 5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter->second->preferred) { 5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This wasn't the selected login but it used to be preferred. 5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter->second->preferred = false; 5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (user_action_ == kUserActionNone) 5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) user_action_ = kUserActionChoose; 5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_store->UpdateLogin(*iter->second); 5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::UpdateLogin() { 5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, POST_MATCHING_PHASE); 5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(preferred_match_); 6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we're doing an Update, we either autofilled correctly and need to 6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // update the stats, or the user typed in a new password for autofilled 6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // username, or the user selected one of the non-preferred matches, 6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // thus requiring a swap of preferred bits. 6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!IsNewLogin() && pending_credentials_.preferred); 6055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(!driver_->IsOffTheRecord()); 6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) PasswordStore* password_store = client_->GetPasswordStore(); 6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!password_store) { 6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 61390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) // Update metadata. 61490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ++pending_credentials_.times_used; 61590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 616116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (client_->IsSyncAccountCredential( 617116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::UTF16ToUTF8(pending_credentials_.username_value), 618116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.signon_realm)) { 619116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::RecordAction( 620116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch base::UserMetricsAction("PasswordManager_SyncCredentialUsed")); 621116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 622116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 623d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // Check to see if this form is a candidate for password generation. 624d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) CheckForAccountCreationForm(pending_credentials_, observed_form_); 625d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdatePreferredLoginState(password_store); 6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 628868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Remove alternate usernames. At this point we assume that we have found 629868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // the right username. 630868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_credentials_.other_possible_usernames.clear(); 631868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update the new preferred login. 633868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!selected_username_.empty()) { 634868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // An other possible username is selected. We set this selected username 635868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // as the real username. The PasswordStore API isn't designed to update 636868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // username, so we delete the old credentials and add a new one instead. 637868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) password_store->RemoveLogin(pending_credentials_); 638868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_credentials_.username_value = selected_username_; 639868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) password_store->AddLogin(pending_credentials_); 640868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } else if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && 641868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) (observed_form_.origin.spec().length() > 642868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) observed_form_.signon_realm.length()) && 643868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) (observed_form_.signon_realm == 644868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_credentials_.origin.spec())) { 645868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Note origin.spec().length > signon_realm.length implies the origin has a 646868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // path, since signon_realm is a prefix of origin for HTML password forms. 647868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // 6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The user logged in successfully with one of our autofilled logins on a 6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // page with non-empty path, but the autofilled entry was initially saved/ 6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // imported with an empty path. Rather than just mark this entry preferred, 6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we create a more specific copy for this exact page and leave the "master" 6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // unchanged. This is to prevent the case where that master login is used 6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // on several sites (e.g site.com/a and site.com/b) but the user actually 6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // has a different preference on each site. For example, on /a, he wants the 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // general empty-path login so it is flagged as preferred, but on /b he logs 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // in with a different saved entry - we don't want to remove the preferred 6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // status of the former because upon return to /a it won't be the default- 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // fill match. 6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(timsteele): Bug 1188626 - expire the master copies. 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PasswordForm copy(pending_credentials_); 6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy.origin = observed_form_.origin; 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) copy.action = observed_form_.action; 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_store->AddLogin(copy); 664116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else if (observed_form_.new_password_element.empty() && 665116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch (pending_credentials_.password_element.empty() || 666116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.username_element.empty() || 667116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch pending_credentials_.submit_element.empty())) { 668116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // If |observed_form_| was a sign-up or change password form, there is no 669116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // point in trying to update element names: they are likely going to be 670116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // different than those on a login form. 671116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Otherwise, given that |password_element| and |username_element| can't be 672116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // updated because they are part of Sync and PasswordStore primary key, we 673116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // must delete the old credentials altogether and then add the new ones. 674cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) password_store->RemoveLogin(pending_credentials_); 675cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pending_credentials_.password_element = observed_form_.password_element; 676cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pending_credentials_.username_element = observed_form_.username_element; 677cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) pending_credentials_.submit_element = observed_form_.submit_element; 678cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) password_store->AddLogin(pending_credentials_); 6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) password_store->UpdateLogin(pending_credentials_); 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 684868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool PasswordFormManager::UpdatePendingCredentialsIfOtherPossibleUsername( 685a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& username) { 686868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) for (PasswordFormMap::const_iterator it = best_matches_.begin(); 687868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) it != best_matches_.end(); ++it) { 688868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) for (size_t i = 0; i < it->second->other_possible_usernames.size(); ++i) { 689868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (it->second->other_possible_usernames[i] == username) { 690868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) pending_credentials_ = *it->second; 691868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return true; 692868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 693868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 694868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 695868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return false; 696868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 697868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 698d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void PasswordFormManager::CheckForAccountCreationForm( 699d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) const PasswordForm& pending, const PasswordForm& observed) { 700d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // We check to see if the saved form_data is the same as the observed 701d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // form_data, which should never be true for passwords saved on account 702d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // creation forms. This check is only made the first time a password is used 703d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // to cut down on false positives. Specifically a site may have multiple login 704d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) // forms with different markup, which might look similar to a signup form. 705d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) if (pending.times_used == 1) { 706d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) FormStructure pending_structure(pending.form_data); 707d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) FormStructure observed_structure(observed.form_data); 7084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // Ignore |pending_structure| if its FormData has no fields. This is to 7094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // weed out those credentials that were saved before FormData was added 7104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // to PasswordForm. Even without this check, these FormStructure's won't 7114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // be uploaded, but it makes it hard to see if we are encountering 7124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) // unexpected errors. 7134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (!pending.form_data.fields.empty() && 7144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) pending_structure.FormSignature() != 715d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) observed_structure.FormSignature()) { 7161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UploadPasswordForm(pending.form_data, 7171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci autofill::ACCOUNT_CREATION_PASSWORD); 718d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) } 719d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) } 720d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)} 721d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) 7221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid PasswordFormManager::UploadPasswordForm( 7231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const autofill::FormData& form_data, 7241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const autofill::ServerFieldType& password_type) { 7251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci autofill::AutofillManager* autofill_manager = 7261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci driver_->GetAutofillManager(); 7271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!autofill_manager) 7281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return; 7291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 7301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Note that this doesn't guarantee that the upload succeeded, only that 7311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // |form_data| is considered uploadable. 7321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci bool success = 73334680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles) autofill_manager->UploadPasswordForm(form_data, password_type); 7341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success); 7351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 7361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { 7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(state_, MATCHING_PHASE); 7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // For scoring of candidate login data: 740010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // The most important element that should match is the signon_realm followed 741010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // by the origin, the action, the password name, the submit button name, and 742010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // finally the username input field name. 743010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // If public suffix origin match was not used, it gives an addition of 744010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // 128 (1 << 7). 745eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Exact origin match gives an addition of 64 (1 << 6) + # of matching url 7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // dirs. 747eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Partial match gives an addition of 32 (1 << 5) + # matching url dirs 7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // That way, a partial match cannot trump an exact match even if 7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the partial one matches all other attributes (action, elements) (and 7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // regardless of the matching depth in the URL path). 7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int score = 0; 752010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (!candidate.IsPublicSuffixMatch()) { 753010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) score += 1 << 7; 754010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (candidate.origin == observed_form_.origin) { 7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This check is here for the most common case which 7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is we have a single match in the db for the given host, 7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // so we don't generally need to walk the entire URL path (the else 7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // clause). 760eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch score += (1 << 6) + static_cast<int>(form_path_tokens_.size()); 7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Walk the origin URL paths one directory at a time to see how 7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // deep the two match. 7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<std::string> candidate_path_tokens; 7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(candidate.origin.path(), '/', &candidate_path_tokens); 7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t depth = 0; 7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t max_dirs = std::min(form_path_tokens_.size(), 7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) candidate_path_tokens.size()); 7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while ((depth < max_dirs) && (form_path_tokens_[depth] == 7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) candidate_path_tokens[depth])) { 7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) depth++; 7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) score++; 7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // do we have a partial match? 775eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch score += (depth > 0) ? 1 << 5 : 0; 7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (observed_form_.scheme == PasswordForm::SCHEME_HTML) { 7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (candidate.action == observed_form_.action) 7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) score += 1 << 3; 7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (candidate.password_element == observed_form_.password_element) 7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) score += 1 << 2; 7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (candidate.submit_element == observed_form_.submit_element) 7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) score += 1 << 1; 7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (candidate.username_element == observed_form_.username_element) 7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) score += 1 << 0; 7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return score; 7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::SubmitPassed() { 7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) submit_result_ = kSubmitResultPassed; 79323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) if (has_generated_password_) 79423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED); 7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PasswordFormManager::SubmitFailed() { 7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) submit_result_ = kSubmitResultFailed; 79923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) if (has_generated_password_) 80023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED); 8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 802c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch 803c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch} // namespace password_manager 804