Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / password_manager / password_manager.cc
blob38a5dffb6aee0ca96f125a260e89bbda01e467bf
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_delegate.h"
16 #include "chrome/browser/password_manager/password_manager_metrics_util.h"
17 #include "chrome/browser/password_manager/password_manager_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/passwords/manage_passwords_bubble_ui_controller.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/chrome_version_info.h"
22 #include "chrome/common/pref_names.h"
23 #include "components/autofill/content/common/autofill_messages.h"
24 #include "components/autofill/core/common/password_autofill_util.h"
25 #include "components/user_prefs/pref_registry_syncable.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/navigation_details.h"
28 #include "content/public/browser/user_metrics.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/frame_navigate_params.h"
31 #include "grit/generated_resources.h"
33 using autofill::PasswordForm;
34 using autofill::PasswordFormMap;
35 using base::UserMetricsAction;
36 using content::BrowserThread;
37 using content::WebContents;
39 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PasswordManager);
41 namespace {
43 const char kSpdyProxyRealm[] = "/SpdyProxy";
44 const char kOtherPossibleUsernamesExperiment[] =
45 "PasswordManagerOtherPossibleUsernames";
47 void ReportOsPassword() {
48 password_manager_util::OsPasswordStatus status =
49 password_manager_util::GetOsPasswordStatus();
51 UMA_HISTOGRAM_ENUMERATION("PasswordManager.OsPasswordStatus",
52 status,
53 password_manager_util::MAX_PASSWORD_STATUS);
56 // This routine is called when PasswordManagers are constructed.
58 // Currently we report metrics only once at startup. We require
59 // that this is only ever called from a single thread in order to
60 // avoid needing to lock (a static boolean flag is then sufficient to
61 // guarantee running only once).
62 void ReportMetrics(bool password_manager_enabled) {
63 static base::PlatformThreadId initial_thread_id =
64 base::PlatformThread::CurrentId();
65 DCHECK(initial_thread_id == base::PlatformThread::CurrentId());
67 static bool ran_once = false;
68 if (ran_once)
69 return;
70 ran_once = true;
72 // Avoid checking OS password until later on in browser startup
73 // since it calls a few Windows APIs.
74 BrowserThread::PostDelayedTask(BrowserThread::UI,
75 FROM_HERE,
76 base::Bind(&ReportOsPassword),
77 base::TimeDelta::FromSeconds(10));
79 UMA_HISTOGRAM_BOOLEAN("PasswordManager.Enabled", password_manager_enabled);
82 } // namespace
84 // static
85 void PasswordManager::RegisterProfilePrefs(
86 user_prefs::PrefRegistrySyncable* registry) {
87 registry->RegisterBooleanPref(
88 prefs::kPasswordManagerEnabled,
89 true,
90 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
91 registry->RegisterBooleanPref(
92 prefs::kPasswordManagerAllowShowPasswords,
93 true,
94 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
95 registry->RegisterListPref(prefs::kPasswordManagerGroupsForDomains,
96 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
99 // static
100 void PasswordManager::CreateForWebContentsAndDelegate(
101 content::WebContents* contents,
102 PasswordManagerDelegate* delegate) {
103 if (FromWebContents(contents)) {
104 DCHECK_EQ(delegate, FromWebContents(contents)->delegate_);
105 return;
108 contents->SetUserData(UserDataKey(),
109 new PasswordManager(contents, delegate));
112 PasswordManager::PasswordManager(WebContents* web_contents,
113 PasswordManagerDelegate* delegate)
114 : content::WebContentsObserver(web_contents),
115 delegate_(delegate) {
116 DCHECK(delegate_);
117 password_manager_enabled_.Init(prefs::kPasswordManagerEnabled,
118 delegate_->GetProfile()->GetPrefs());
120 ReportMetrics(*password_manager_enabled_);
123 PasswordManager::~PasswordManager() {
124 FOR_EACH_OBSERVER(LoginModelObserver, observers_, OnLoginModelDestroying());
127 void PasswordManager::SetFormHasGeneratedPassword(const PasswordForm& form) {
128 for (ScopedVector<PasswordFormManager>::iterator iter =
129 pending_login_managers_.begin();
130 iter != pending_login_managers_.end(); ++iter) {
131 if ((*iter)->DoesManage(
132 form, PasswordFormManager::ACTION_MATCH_REQUIRED)) {
133 (*iter)->SetHasGeneratedPassword();
134 return;
137 // If there is no corresponding PasswordFormManager, we create one. This is
138 // not the common case, and should only happen when there is a bug in our
139 // ability to detect forms.
140 bool ssl_valid = (form.origin.SchemeIsSecure() &&
141 !delegate_->DidLastPageLoadEncounterSSLErrors());
142 PasswordFormManager* manager =
143 new PasswordFormManager(delegate_->GetProfile(),
144 this,
145 web_contents(),
146 form,
147 ssl_valid);
148 pending_login_managers_.push_back(manager);
149 manager->SetHasGeneratedPassword();
150 // TODO(gcasto): Add UMA stats to track this.
153 bool PasswordManager::IsSavingEnabled() const {
154 return *password_manager_enabled_ &&
155 !delegate_->GetProfile()->IsOffTheRecord();
158 void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
159 if (!IsSavingEnabled()) {
160 RecordFailure(SAVING_DISABLED, form.origin.host());
161 return;
164 // No password to save? Then don't.
165 if (form.password_value.empty()) {
166 RecordFailure(EMPTY_PASSWORD, form.origin.host());
167 return;
170 scoped_ptr<PasswordFormManager> manager;
171 ScopedVector<PasswordFormManager>::iterator matched_manager_it =
172 pending_login_managers_.end();
173 for (ScopedVector<PasswordFormManager>::iterator iter =
174 pending_login_managers_.begin();
175 iter != pending_login_managers_.end(); ++iter) {
176 // If we find a manager that exactly matches the submitted form including
177 // the action URL, exit the loop.
178 if ((*iter)->DoesManage(
179 form, PasswordFormManager::ACTION_MATCH_REQUIRED)) {
180 matched_manager_it = iter;
181 break;
182 // If the current manager matches the submitted form excluding the action
183 // URL, remember it as a candidate and continue searching for an exact
184 // match.
185 } else if ((*iter)->DoesManage(
186 form, PasswordFormManager::ACTION_MATCH_NOT_REQUIRED)) {
187 matched_manager_it = iter;
190 // If we didn't find a manager, this means a form was submitted without
191 // first loading the page containing the form. Don't offer to save
192 // passwords in this case.
193 if (matched_manager_it != pending_login_managers_.end()) {
194 // Transfer ownership of the manager from |pending_login_managers_| to
195 // |manager|.
196 manager.reset(*matched_manager_it);
197 pending_login_managers_.weak_erase(matched_manager_it);
198 } else {
199 RecordFailure(NO_MATCHING_FORM, form.origin.host());
200 return;
203 // If we found a manager but it didn't finish matching yet, the user has
204 // tried to submit credentials before we had time to even find matching
205 // results for the given form and autofill. If this is the case, we just
206 // give up.
207 if (!manager->HasCompletedMatching()) {
208 RecordFailure(MATCHING_NOT_COMPLETE, form.origin.host());
209 return;
212 // Also get out of here if the user told us to 'never remember' passwords for
213 // this form.
214 if (manager->IsBlacklisted()) {
215 RecordFailure(FORM_BLACKLISTED, form.origin.host());
216 return;
219 // Bail if we're missing any of the necessary form components.
220 if (!manager->HasValidPasswordForm()) {
221 RecordFailure(INVALID_FORM, form.origin.host());
222 return;
225 // Always save generated passwords, as the user expresses explicit intent for
226 // Chrome to manage such passwords. For other passwords, respect the
227 // autocomplete attribute if autocomplete='off' is not ignored.
228 if (!autofill::ShouldIgnoreAutocompleteOffForPasswordFields() &&
229 !manager->HasGeneratedPassword() &&
230 !form.password_autocomplete_set) {
231 RecordFailure(AUTOCOMPLETE_OFF, form.origin.host());
232 return;
235 PasswordForm provisionally_saved_form(form);
236 provisionally_saved_form.ssl_valid = form.origin.SchemeIsSecure() &&
237 !delegate_->DidLastPageLoadEncounterSSLErrors();
238 provisionally_saved_form.preferred = true;
239 PasswordFormManager::OtherPossibleUsernamesAction action =
240 PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES;
241 if (OtherPossibleUsernamesEnabled())
242 action = PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES;
243 manager->ProvisionallySave(provisionally_saved_form, action);
244 provisional_save_manager_.swap(manager);
247 void PasswordManager::RecordFailure(ProvisionalSaveFailure failure,
248 const std::string& form_origin) {
249 UMA_HISTOGRAM_ENUMERATION("PasswordManager.ProvisionalSaveFailure",
250 failure, MAX_FAILURE_VALUE);
252 std::string group_name = password_manager_metrics_util::GroupIdToString(
253 password_manager_metrics_util::MonitoredDomainGroupId(
254 form_origin, delegate_->GetProfile()->GetPrefs()));
255 if (!group_name.empty()) {
256 password_manager_metrics_util::LogUMAHistogramEnumeration(
257 "PasswordManager.ProvisionalSaveFailure_" + group_name, failure,
258 MAX_FAILURE_VALUE);
262 void PasswordManager::AddSubmissionCallback(
263 const PasswordSubmittedCallback& callback) {
264 submission_callbacks_.push_back(callback);
267 void PasswordManager::AddObserver(LoginModelObserver* observer) {
268 observers_.AddObserver(observer);
271 void PasswordManager::RemoveObserver(LoginModelObserver* observer) {
272 observers_.RemoveObserver(observer);
275 void PasswordManager::DidNavigateMainFrame(
276 const content::LoadCommittedDetails& details,
277 const content::FrameNavigateParams& params) {
278 // Clear data after main frame navigation. We don't want to clear data after
279 // subframe navigation as there might be password forms on other frames that
280 // could be submitted.
281 if (!details.is_in_page)
282 pending_login_managers_.clear();
285 bool PasswordManager::OnMessageReceived(const IPC::Message& message) {
286 bool handled = true;
287 IPC_BEGIN_MESSAGE_MAP(PasswordManager, message)
288 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsParsed,
289 OnPasswordFormsParsed)
290 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsRendered,
291 OnPasswordFormsRendered)
292 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormSubmitted,
293 OnPasswordFormSubmitted)
294 IPC_MESSAGE_UNHANDLED(handled = false)
295 IPC_END_MESSAGE_MAP()
296 return handled;
299 void PasswordManager::OnPasswordFormSubmitted(
300 const PasswordForm& password_form) {
301 ProvisionallySavePassword(password_form);
302 for (size_t i = 0; i < submission_callbacks_.size(); ++i) {
303 submission_callbacks_[i].Run(password_form);
306 pending_login_managers_.clear();
309 void PasswordManager::OnPasswordFormsParsed(
310 const std::vector<PasswordForm>& forms) {
311 // Ask the SSLManager for current security.
312 bool had_ssl_error = delegate_->DidLastPageLoadEncounterSSLErrors();
314 for (std::vector<PasswordForm>::const_iterator iter = forms.begin();
315 iter != forms.end(); ++iter) {
316 // Don't involve the password manager if this form corresponds to
317 // SpdyProxy authentication, as indicated by the realm.
318 if (EndsWith(iter->signon_realm, kSpdyProxyRealm, true))
319 continue;
321 bool ssl_valid = iter->origin.SchemeIsSecure() && !had_ssl_error;
322 PasswordFormManager* manager =
323 new PasswordFormManager(delegate_->GetProfile(),
324 this,
325 web_contents(),
326 *iter,
327 ssl_valid);
328 pending_login_managers_.push_back(manager);
330 // Avoid prompting the user for access to a password if they don't have
331 // password saving enabled.
332 PasswordStore::AuthorizationPromptPolicy prompt_policy =
333 *password_manager_enabled_ ? PasswordStore::ALLOW_PROMPT
334 : PasswordStore::DISALLOW_PROMPT;
336 manager->FetchMatchingLoginsFromPasswordStore(prompt_policy);
340 bool PasswordManager::ShouldShowSavePasswordInfoBar() const {
341 return provisional_save_manager_->IsNewLogin() &&
342 !provisional_save_manager_->HasGeneratedPassword() &&
343 !provisional_save_manager_->IsPendingCredentialsPublicSuffixMatch();
346 void PasswordManager::OnPasswordFormsRendered(
347 const std::vector<PasswordForm>& visible_forms) {
348 if (!provisional_save_manager_.get())
349 return;
351 DCHECK(IsSavingEnabled());
353 // If we see the login form again, then the login failed.
354 for (size_t i = 0; i < visible_forms.size(); ++i) {
355 // TODO(vabr): The similarity check is just action equality for now. If it
356 // becomes more complex, it may make sense to consider modifying and using
357 // PasswordFormManager::DoesManage for it.
358 if (visible_forms[i].action.is_valid() &&
359 provisional_save_manager_->pending_credentials().action ==
360 visible_forms[i].action) {
361 provisional_save_manager_->SubmitFailed();
362 provisional_save_manager_.reset();
363 return;
367 // Looks like a successful login attempt. Either show an infobar or
368 // automatically save the login data. We prompt when the user hasn't already
369 // given consent, either through previously accepting the infobar or by having
370 // the browser generate the password.
371 provisional_save_manager_->SubmitPassed();
372 if (provisional_save_manager_->HasGeneratedPassword())
373 UMA_HISTOGRAM_COUNTS("PasswordGeneration.Submitted", 1);
375 if (ShouldShowSavePasswordInfoBar()) {
376 if (CommandLine::ForCurrentProcess()->HasSwitch(
377 switches::kEnableSavePasswordBubble)) {
378 ManagePasswordsBubbleUIController* manage_passwords_bubble_ui_controller =
379 ManagePasswordsBubbleUIController::FromWebContents(web_contents());
380 if (manage_passwords_bubble_ui_controller) {
381 manage_passwords_bubble_ui_controller->OnPasswordSubmitted(
382 provisional_save_manager_.release());
383 } else {
384 provisional_save_manager_.reset();
386 } else {
387 delegate_->AddSavePasswordInfoBarIfPermitted(
388 provisional_save_manager_.release());
390 } else {
391 provisional_save_manager_->Save();
392 provisional_save_manager_.reset();
396 void PasswordManager::PossiblyInitializeUsernamesExperiment(
397 const PasswordFormMap& best_matches) const {
398 if (base::FieldTrialList::Find(kOtherPossibleUsernamesExperiment))
399 return;
401 bool other_possible_usernames_exist = false;
402 for (autofill::PasswordFormMap::const_iterator it = best_matches.begin();
403 it != best_matches.end(); ++it) {
404 if (!it->second->other_possible_usernames.empty()) {
405 other_possible_usernames_exist = true;
406 break;
410 if (!other_possible_usernames_exist)
411 return;
413 const base::FieldTrial::Probability kDivisor = 100;
414 scoped_refptr<base::FieldTrial> trial(
415 base::FieldTrialList::FactoryGetFieldTrial(
416 kOtherPossibleUsernamesExperiment,
417 kDivisor, "Disabled", 2013, 12, 31,
418 base::FieldTrial::ONE_TIME_RANDOMIZED, NULL));
419 base::FieldTrial::Probability enabled_probability = 0;
421 switch (chrome::VersionInfo::GetChannel()) {
422 case chrome::VersionInfo::CHANNEL_DEV:
423 case chrome::VersionInfo::CHANNEL_BETA:
424 enabled_probability = 50;
425 break;
426 default:
427 break;
430 trial->AppendGroup("Enabled", enabled_probability);
433 bool PasswordManager::OtherPossibleUsernamesEnabled() const {
434 return base::FieldTrialList::FindFullName(
435 kOtherPossibleUsernamesExperiment) == "Enabled";
438 void PasswordManager::Autofill(
439 const PasswordForm& form_for_autofill,
440 const PasswordFormMap& best_matches,
441 const PasswordForm& preferred_match,
442 bool wait_for_username) const {
443 PossiblyInitializeUsernamesExperiment(best_matches);
444 switch (form_for_autofill.scheme) {
445 case PasswordForm::SCHEME_HTML: {
446 // Note the check above is required because the observers_ for a non-HTML
447 // schemed password form may have been freed, so we need to distinguish.
448 autofill::PasswordFormFillData fill_data;
449 InitPasswordFormFillData(form_for_autofill,
450 best_matches,
451 &preferred_match,
452 wait_for_username,
453 OtherPossibleUsernamesEnabled(),
454 &fill_data);
455 delegate_->FillPasswordForm(fill_data);
456 break;
458 default:
459 FOR_EACH_OBSERVER(
460 LoginModelObserver,
461 observers_,
462 OnAutofillDataAvailable(preferred_match.username_value,
463 preferred_match.password_value));
464 break;
467 ManagePasswordsBubbleUIController* manage_passwords_bubble_ui_controller =
468 ManagePasswordsBubbleUIController::FromWebContents(web_contents());
469 if (manage_passwords_bubble_ui_controller &&
470 CommandLine::ForCurrentProcess()->HasSwitch(
471 switches::kEnableSavePasswordBubble)) {
472 manage_passwords_bubble_ui_controller->OnPasswordAutofilled(best_matches);