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.
5 #include "chrome/browser/password_manager/password_manager.h"
7 #include "base/command_line.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/threading/platform_thread.h"
14 #include "chrome/browser/password_manager/password_form_manager.h"
15 #include "chrome/browser/password_manager/password_manager_client.h"
16 #include "chrome/browser/password_manager/password_manager_driver.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/autofill/core/common/password_autofill_util.h"
19 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
20 #include "components/password_manager/core/common/password_manager_pref_names.h"
21 #include "components/user_prefs/pref_registry_syncable.h"
22 #include "grit/generated_resources.h"
24 using autofill::PasswordForm
;
25 using autofill::PasswordFormMap
;
29 const char kSpdyProxyRealm
[] = "/SpdyProxy";
31 // This routine is called when PasswordManagers are constructed.
33 // Currently we report metrics only once at startup. We require
34 // that this is only ever called from a single thread in order to
35 // avoid needing to lock (a static boolean flag is then sufficient to
36 // guarantee running only once).
37 void ReportMetrics(bool password_manager_enabled
) {
38 static base::PlatformThreadId initial_thread_id
=
39 base::PlatformThread::CurrentId();
40 DCHECK(initial_thread_id
== base::PlatformThread::CurrentId());
42 static bool ran_once
= false;
47 UMA_HISTOGRAM_BOOLEAN("PasswordManager.Enabled", password_manager_enabled
);
52 const char PasswordManager::kOtherPossibleUsernamesExperiment
[] =
53 "PasswordManagerOtherPossibleUsernames";
56 void PasswordManager::RegisterProfilePrefs(
57 user_prefs::PrefRegistrySyncable
* registry
) {
58 registry
->RegisterBooleanPref(
59 prefs::kPasswordManagerEnabled
,
61 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);
62 registry
->RegisterBooleanPref(
63 prefs::kPasswordManagerAllowShowPasswords
,
65 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
66 registry
->RegisterListPref(prefs::kPasswordManagerGroupsForDomains
,
67 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
70 PasswordManager::PasswordManager(PasswordManagerClient
* client
)
71 : client_(client
), driver_(client
->GetDriver()) {
74 password_manager_enabled_
.Init(prefs::kPasswordManagerEnabled
,
77 ReportMetrics(*password_manager_enabled_
);
80 PasswordManager::~PasswordManager() {
81 FOR_EACH_OBSERVER(LoginModelObserver
, observers_
, OnLoginModelDestroying());
84 void PasswordManager::SetFormHasGeneratedPassword(const PasswordForm
& form
) {
85 for (ScopedVector
<PasswordFormManager
>::iterator iter
=
86 pending_login_managers_
.begin();
87 iter
!= pending_login_managers_
.end(); ++iter
) {
88 if ((*iter
)->DoesManage(
89 form
, PasswordFormManager::ACTION_MATCH_REQUIRED
)) {
90 (*iter
)->SetHasGeneratedPassword();
94 // If there is no corresponding PasswordFormManager, we create one. This is
95 // not the common case, and should only happen when there is a bug in our
96 // ability to detect forms.
97 bool ssl_valid
= (form
.origin
.SchemeIsSecure() &&
98 !driver_
->DidLastPageLoadEncounterSSLErrors());
99 PasswordFormManager
* manager
= new PasswordFormManager(
100 this, client_
, driver_
, form
, ssl_valid
);
101 pending_login_managers_
.push_back(manager
);
102 manager
->SetHasGeneratedPassword();
103 // TODO(gcasto): Add UMA stats to track this.
106 bool PasswordManager::IsSavingEnabled() const {
107 return *password_manager_enabled_
&& !driver_
->IsOffTheRecord();
110 void PasswordManager::ProvisionallySavePassword(const PasswordForm
& form
) {
111 if (!IsSavingEnabled()) {
112 RecordFailure(SAVING_DISABLED
, form
.origin
.host());
116 // No password to save? Then don't.
117 if (form
.password_value
.empty()) {
118 RecordFailure(EMPTY_PASSWORD
, form
.origin
.host());
122 scoped_ptr
<PasswordFormManager
> manager
;
123 ScopedVector
<PasswordFormManager
>::iterator matched_manager_it
=
124 pending_login_managers_
.end();
125 for (ScopedVector
<PasswordFormManager
>::iterator iter
=
126 pending_login_managers_
.begin();
127 iter
!= pending_login_managers_
.end(); ++iter
) {
128 // If we find a manager that exactly matches the submitted form including
129 // the action URL, exit the loop.
130 if ((*iter
)->DoesManage(
131 form
, PasswordFormManager::ACTION_MATCH_REQUIRED
)) {
132 matched_manager_it
= iter
;
134 // If the current manager matches the submitted form excluding the action
135 // URL, remember it as a candidate and continue searching for an exact
137 } else if ((*iter
)->DoesManage(
138 form
, PasswordFormManager::ACTION_MATCH_NOT_REQUIRED
)) {
139 matched_manager_it
= iter
;
142 // If we didn't find a manager, this means a form was submitted without
143 // first loading the page containing the form. Don't offer to save
144 // passwords in this case.
145 if (matched_manager_it
!= pending_login_managers_
.end()) {
146 // Transfer ownership of the manager from |pending_login_managers_| to
148 manager
.reset(*matched_manager_it
);
149 pending_login_managers_
.weak_erase(matched_manager_it
);
151 RecordFailure(NO_MATCHING_FORM
, form
.origin
.host());
155 // If we found a manager but it didn't finish matching yet, the user has
156 // tried to submit credentials before we had time to even find matching
157 // results for the given form and autofill. If this is the case, we just
159 if (!manager
->HasCompletedMatching()) {
160 RecordFailure(MATCHING_NOT_COMPLETE
, form
.origin
.host());
164 // Also get out of here if the user told us to 'never remember' passwords for
166 if (manager
->IsBlacklisted()) {
167 RecordFailure(FORM_BLACKLISTED
, form
.origin
.host());
171 // Bail if we're missing any of the necessary form components.
172 if (!manager
->HasValidPasswordForm()) {
173 RecordFailure(INVALID_FORM
, form
.origin
.host());
177 // Always save generated passwords, as the user expresses explicit intent for
178 // Chrome to manage such passwords. For other passwords, respect the
179 // autocomplete attribute if autocomplete='off' is not ignored.
180 if (!autofill::ShouldIgnoreAutocompleteOffForPasswordFields() &&
181 !manager
->HasGeneratedPassword() &&
182 !form
.password_autocomplete_set
) {
183 RecordFailure(AUTOCOMPLETE_OFF
, form
.origin
.host());
187 PasswordForm
provisionally_saved_form(form
);
188 provisionally_saved_form
.ssl_valid
=
189 form
.origin
.SchemeIsSecure() &&
190 !driver_
->DidLastPageLoadEncounterSSLErrors();
191 provisionally_saved_form
.preferred
= true;
192 PasswordFormManager::OtherPossibleUsernamesAction action
=
193 PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES
;
194 if (OtherPossibleUsernamesEnabled())
195 action
= PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES
;
196 manager
->ProvisionallySave(provisionally_saved_form
, action
);
197 provisional_save_manager_
.swap(manager
);
200 void PasswordManager::RecordFailure(ProvisionalSaveFailure failure
,
201 const std::string
& form_origin
) {
202 UMA_HISTOGRAM_ENUMERATION("PasswordManager.ProvisionalSaveFailure",
203 failure
, MAX_FAILURE_VALUE
);
205 std::string group_name
= password_manager_metrics_util::GroupIdToString(
206 password_manager_metrics_util::MonitoredDomainGroupId(
207 form_origin
, client_
->GetPrefs()));
208 if (!group_name
.empty()) {
209 password_manager_metrics_util::LogUMAHistogramEnumeration(
210 "PasswordManager.ProvisionalSaveFailure_" + group_name
, failure
,
215 void PasswordManager::AddSubmissionCallback(
216 const PasswordSubmittedCallback
& callback
) {
217 submission_callbacks_
.push_back(callback
);
220 void PasswordManager::AddObserver(LoginModelObserver
* observer
) {
221 observers_
.AddObserver(observer
);
224 void PasswordManager::RemoveObserver(LoginModelObserver
* observer
) {
225 observers_
.RemoveObserver(observer
);
228 void PasswordManager::DidNavigateMainFrame(bool is_in_page
) {
229 // Clear data after main frame navigation if the navigation was to a
232 pending_login_managers_
.clear();
235 void PasswordManager::OnPasswordFormSubmitted(
236 const PasswordForm
& password_form
) {
237 ProvisionallySavePassword(password_form
);
238 for (size_t i
= 0; i
< submission_callbacks_
.size(); ++i
) {
239 submission_callbacks_
[i
].Run(password_form
);
242 pending_login_managers_
.clear();
245 void PasswordManager::OnPasswordFormsParsed(
246 const std::vector
<PasswordForm
>& forms
) {
247 // Ask the SSLManager for current security.
248 bool had_ssl_error
= driver_
->DidLastPageLoadEncounterSSLErrors();
250 for (std::vector
<PasswordForm
>::const_iterator iter
= forms
.begin();
251 iter
!= forms
.end(); ++iter
) {
252 // Don't involve the password manager if this form corresponds to
253 // SpdyProxy authentication, as indicated by the realm.
254 if (EndsWith(iter
->signon_realm
, kSpdyProxyRealm
, true))
257 bool ssl_valid
= iter
->origin
.SchemeIsSecure() && !had_ssl_error
;
258 PasswordFormManager
* manager
= new PasswordFormManager(
259 this, client_
, driver_
, *iter
, ssl_valid
);
260 pending_login_managers_
.push_back(manager
);
262 // Avoid prompting the user for access to a password if they don't have
263 // password saving enabled.
264 PasswordStore::AuthorizationPromptPolicy prompt_policy
=
265 *password_manager_enabled_
? PasswordStore::ALLOW_PROMPT
266 : PasswordStore::DISALLOW_PROMPT
;
268 manager
->FetchMatchingLoginsFromPasswordStore(prompt_policy
);
272 bool PasswordManager::ShouldPromptUserToSavePassword() const {
273 return provisional_save_manager_
->IsNewLogin() &&
274 !provisional_save_manager_
->HasGeneratedPassword() &&
275 !provisional_save_manager_
->IsPendingCredentialsPublicSuffixMatch();
278 void PasswordManager::OnPasswordFormsRendered(
279 const std::vector
<PasswordForm
>& visible_forms
) {
280 if (!provisional_save_manager_
.get())
283 DCHECK(IsSavingEnabled());
285 // If we see the login form again, then the login failed.
286 for (size_t i
= 0; i
< visible_forms
.size(); ++i
) {
287 // TODO(vabr): The similarity check is just action equality for now. If it
288 // becomes more complex, it may make sense to consider modifying and using
289 // PasswordFormManager::DoesManage for it.
290 if (visible_forms
[i
].action
.is_valid() &&
291 provisional_save_manager_
->pending_credentials().action
==
292 visible_forms
[i
].action
) {
293 provisional_save_manager_
->SubmitFailed();
294 provisional_save_manager_
.reset();
299 // Looks like a successful login attempt. Either show an infobar or
300 // automatically save the login data. We prompt when the user hasn't already
301 // given consent, either through previously accepting the infobar or by having
302 // the browser generate the password.
303 provisional_save_manager_
->SubmitPassed();
304 if (provisional_save_manager_
->HasGeneratedPassword())
305 UMA_HISTOGRAM_COUNTS("PasswordGeneration.Submitted", 1);
307 if (ShouldPromptUserToSavePassword()) {
308 client_
->PromptUserToSavePassword(provisional_save_manager_
.release());
310 provisional_save_manager_
->Save();
311 provisional_save_manager_
.reset();
315 void PasswordManager::PossiblyInitializeUsernamesExperiment(
316 const PasswordFormMap
& best_matches
) const {
317 if (base::FieldTrialList::Find(kOtherPossibleUsernamesExperiment
))
320 bool other_possible_usernames_exist
= false;
321 for (autofill::PasswordFormMap::const_iterator it
= best_matches
.begin();
322 it
!= best_matches
.end(); ++it
) {
323 if (!it
->second
->other_possible_usernames
.empty()) {
324 other_possible_usernames_exist
= true;
329 if (!other_possible_usernames_exist
)
332 const base::FieldTrial::Probability kDivisor
= 100;
333 scoped_refptr
<base::FieldTrial
> trial(
334 base::FieldTrialList::FactoryGetFieldTrial(
335 kOtherPossibleUsernamesExperiment
,
339 base::FieldTrial::ONE_TIME_RANDOMIZED
,
341 base::FieldTrial::Probability enabled_probability
=
342 client_
->GetProbabilityForExperiment(kOtherPossibleUsernamesExperiment
);
343 trial
->AppendGroup("Enabled", enabled_probability
);
346 bool PasswordManager::OtherPossibleUsernamesEnabled() const {
347 return base::FieldTrialList::FindFullName(
348 kOtherPossibleUsernamesExperiment
) == "Enabled";
351 void PasswordManager::Autofill(
352 const PasswordForm
& form_for_autofill
,
353 const PasswordFormMap
& best_matches
,
354 const PasswordForm
& preferred_match
,
355 bool wait_for_username
) const {
356 PossiblyInitializeUsernamesExperiment(best_matches
);
358 // TODO(tedchoc): Switch to only requesting authentication if the user is
359 // acting on the autofilled forms (crbug.com/342594) instead
361 bool authentication_required
= preferred_match
.use_additional_authentication
;
362 for (autofill::PasswordFormMap::const_iterator it
= best_matches
.begin();
363 !authentication_required
&& it
!= best_matches
.end(); ++it
) {
364 if (it
->second
->use_additional_authentication
)
365 authentication_required
= true;
368 switch (form_for_autofill
.scheme
) {
369 case PasswordForm::SCHEME_HTML
: {
370 // Note the check above is required because the observers_ for a non-HTML
371 // schemed password form may have been freed, so we need to distinguish.
372 scoped_ptr
<autofill::PasswordFormFillData
> fill_data(
373 new autofill::PasswordFormFillData());
374 InitPasswordFormFillData(form_for_autofill
,
378 OtherPossibleUsernamesEnabled(),
380 if (authentication_required
)
381 client_
->AuthenticateAutofillAndFillForm(fill_data
.Pass());
383 driver_
->FillPasswordForm(*fill_data
.get());
390 OnAutofillDataAvailable(preferred_match
.username_value
,
391 preferred_match
.password_value
));
395 client_
->PasswordWasAutofilled(best_matches
);