Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(381)

Side by Side Diff: components/password_manager/core/browser/password_form_manager.cc

Issue 870513002: [PasswordManager] Improve detection of ignorable change password forms. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addresses Vaclav's review inputs. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/password_manager/core/browser/password_form_manager.h" 5 #include "components/password_manager/core/browser/password_form_manager.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <set> 8 #include <set>
9 9
10 #include "base/command_line.h"
10 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
11 #include "base/metrics/user_metrics.h" 12 #include "base/metrics/user_metrics.h"
12 #include "base/strings/string_split.h" 13 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
15 #include "components/autofill/core/browser/autofill_manager.h" 16 #include "components/autofill/core/browser/autofill_manager.h"
16 #include "components/autofill/core/browser/form_structure.h" 17 #include "components/autofill/core/browser/form_structure.h"
17 #include "components/autofill/core/browser/validation.h" 18 #include "components/autofill/core/browser/validation.h"
19 #include "components/autofill/core/common/autofill_switches.h"
18 #include "components/autofill/core/common/password_form.h" 20 #include "components/autofill/core/common/password_form.h"
19 #include "components/password_manager/core/browser/browser_save_password_progres s_logger.h" 21 #include "components/password_manager/core/browser/browser_save_password_progres s_logger.h"
20 #include "components/password_manager/core/browser/password_manager.h" 22 #include "components/password_manager/core/browser/password_manager.h"
21 #include "components/password_manager/core/browser/password_manager_client.h" 23 #include "components/password_manager/core/browser/password_manager_client.h"
22 #include "components/password_manager/core/browser/password_manager_driver.h" 24 #include "components/password_manager/core/browser/password_manager_driver.h"
23 #include "components/password_manager/core/browser/password_store.h" 25 #include "components/password_manager/core/browser/password_store.h"
24 26
25 using autofill::FormStructure; 27 using autofill::FormStructure;
26 using autofill::PasswordForm; 28 using autofill::PasswordForm;
27 using autofill::PasswordFormMap; 29 using autofill::PasswordFormMap;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 event, SUBMISSION_EVENT_ENUM_COUNT); 63 event, SUBMISSION_EVENT_ENUM_COUNT);
62 } 64 }
63 65
64 PasswordForm CopyAndModifySSLValidity(const PasswordForm& orig, 66 PasswordForm CopyAndModifySSLValidity(const PasswordForm& orig,
65 bool ssl_valid) { 67 bool ssl_valid) {
66 PasswordForm result(orig); 68 PasswordForm result(orig);
67 result.ssl_valid = ssl_valid; 69 result.ssl_valid = ssl_valid;
68 return result; 70 return result;
69 } 71 }
70 72
73 bool IsChangePasswordForm(const PasswordForm& candidate) {
74 return !candidate.new_password_element.empty() &&
75 !candidate.password_element.empty();
76 }
77
71 } // namespace 78 } // namespace
72 79
73 PasswordFormManager::PasswordFormManager( 80 PasswordFormManager::PasswordFormManager(
74 PasswordManager* password_manager, 81 PasswordManager* password_manager,
75 PasswordManagerClient* client, 82 PasswordManagerClient* client,
76 const base::WeakPtr<PasswordManagerDriver>& driver, 83 const base::WeakPtr<PasswordManagerDriver>& driver,
77 const PasswordForm& observed_form, 84 const PasswordForm& observed_form,
78 bool ssl_valid) 85 bool ssl_valid)
79 : best_matches_deleter_(&best_matches_), 86 : best_matches_deleter_(&best_matches_),
80 observed_form_(CopyAndModifySSLValidity(observed_form, ssl_valid)), 87 observed_form_(CopyAndModifySSLValidity(observed_form, ssl_valid)),
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 DCHECK_NE(RESULT_NO_MATCH, DoesManage(credentials)); 246 DCHECK_NE(RESULT_NO_MATCH, DoesManage(credentials));
240 247
241 // If this was a sign-up or change password form, we want to persist the new 248 // If this was a sign-up or change password form, we want to persist the new
242 // password; if this was a login form, then the current password (which might 249 // password; if this was a login form, then the current password (which might
243 // still be "new" in the sense that we see these credentials for the first 250 // still be "new" in the sense that we see these credentials for the first
244 // time, or that the user manually entered his actual password to overwrite an 251 // time, or that the user manually entered his actual password to overwrite an
245 // obsolete password we had in the store). 252 // obsolete password we had in the store).
246 base::string16 password_to_save(credentials.new_password_element.empty() ? 253 base::string16 password_to_save(credentials.new_password_element.empty() ?
247 credentials.password_value : credentials.new_password_value); 254 credentials.password_value : credentials.new_password_value);
248 255
256 bool is_username_certainly_correct = credentials.username_marked_by_site;
vabr (Chromium) 2015/02/02 14:18:49 nit: Because credentials.username_marked_by_site i
Pritam Nikam 2015/02/05 06:12:07 Done.
257
249 // Make sure the important fields stay the same as the initially observed or 258 // Make sure the important fields stay the same as the initially observed or
250 // autofilled ones, as they may have changed if the user experienced a login 259 // autofilled ones, as they may have changed if the user experienced a login
251 // failure. 260 // failure.
252 // Look for these credentials in the list containing auto-fill entries. 261 // Look for these credentials in the list containing auto-fill entries.
253 PasswordFormMap::const_iterator it = 262 PasswordFormMap::const_iterator it =
254 best_matches_.find(credentials.username_value); 263 best_matches_.find(credentials.username_value);
255 if (it != best_matches_.end()) { 264 if (!is_username_certainly_correct && it != best_matches_.end()) {
vabr (Chromium) 2015/02/02 14:18:49 Don't narrow the condition this way -- observe tha
Pritam Nikam 2015/02/05 06:12:06 Done.
256 // The user signed in with a login we autofilled. 265 // The user signed in with a login we autofilled.
257 pending_credentials_ = *it->second; 266 pending_credentials_ = *it->second;
258 bool password_changed = 267 bool password_changed =
259 pending_credentials_.password_value != password_to_save; 268 pending_credentials_.password_value != password_to_save;
260 if (IsPendingCredentialsPublicSuffixMatch()) { 269 if (IsPendingCredentialsPublicSuffixMatch()) {
261 // If the autofilled credentials were only a PSL match, store a copy with 270 // If the autofilled credentials were only a PSL match, store a copy with
262 // the current origin and signon realm. This ensures that on the next 271 // the current origin and signon realm. This ensures that on the next
263 // visit, a precise match is found. 272 // visit, a precise match is found.
264 is_new_login_ = true; 273 is_new_login_ = true;
265 user_action_ = password_changed ? kUserActionChoosePslMatch 274 user_action_ = password_changed ? kUserActionChoosePslMatch
(...skipping 30 matching lines...) Expand all
296 // infrequently, and the inconvenience put on the user by asking them is 305 // infrequently, and the inconvenience put on the user by asking them is
297 // not significant, so we are fine with asking here again. 306 // not significant, so we are fine with asking here again.
298 if (password_changed) { 307 if (password_changed) {
299 pending_credentials_.original_signon_realm.clear(); 308 pending_credentials_.original_signon_realm.clear();
300 DCHECK(!IsPendingCredentialsPublicSuffixMatch()); 309 DCHECK(!IsPendingCredentialsPublicSuffixMatch());
301 } 310 }
302 } else { // Not a PSL match. 311 } else { // Not a PSL match.
303 is_new_login_ = false; 312 is_new_login_ = false;
304 if (password_changed) 313 if (password_changed)
305 user_action_ = kUserActionOverridePassword; 314 user_action_ = kUserActionOverridePassword;
315
316 // Check whether its a change-password form with matching credentials in
317 // stored form.
318 PasswordForm matching_form;
319 if (IsChangePasswordForm(credentials) &&
320 HasMatchingCredentialsInStore(credentials, &matching_form)) {
vabr (Chromium) 2015/02/02 14:18:49 This is actually unnecessary -- you already know t
Pritam Nikam 2015/02/05 06:12:07 Done.
321 pending_credentials_ = matching_form;
vabr (Chromium) 2015/02/02 14:18:50 Save the copy -- just pass pending_credentials_ to
vabr (Chromium) 2015/02/02 14:18:50 Given the other comments, the modification needed
Pritam Nikam 2015/02/05 06:12:06 Done.
Pritam Nikam 2015/02/05 06:12:06 Done.
322 selected_username_ = matching_form.username_value;
vabr (Chromium) 2015/02/02 14:18:50 Why do you set this? According to password_form_ma
Pritam Nikam 2015/02/05 06:12:06 Done.
323 user_action_ = kUserActionOverridePassword;
vabr (Chromium) 2015/02/02 14:18:49 How do you know the password is overridden? Should
Pritam Nikam 2015/02/05 06:12:07 Done.
324 }
306 } 325 }
307 } else if (action == ALLOW_OTHER_POSSIBLE_USERNAMES && 326 } else if (action == ALLOW_OTHER_POSSIBLE_USERNAMES &&
308 UpdatePendingCredentialsIfOtherPossibleUsername( 327 UpdatePendingCredentialsIfOtherPossibleUsername(
309 credentials.username_value)) { 328 credentials.username_value)) {
310 // |pending_credentials_| is now set. Note we don't update 329 // |pending_credentials_| is now set. Note we don't update
311 // |pending_credentials_.username_value| to |credentials.username_value| 330 // |pending_credentials_.username_value| to |credentials.username_value|
312 // yet because we need to keep the original username to modify the stored 331 // yet because we need to keep the original username to modify the stored
313 // credential. 332 // credential.
314 selected_username_ = credentials.username_value; 333 selected_username_ = credentials.username_value;
315 is_new_login_ = false; 334 is_new_login_ = false;
316 } else { 335 } else {
317 // User typed in a new, unknown username. 336 // User typed in a new, unknown username.
318 user_action_ = kUserActionOverrideUsernameAndPassword; 337 user_action_ = kUserActionOverrideUsernameAndPassword;
319 pending_credentials_ = observed_form_; 338 pending_credentials_ = observed_form_;
320 pending_credentials_.username_value = credentials.username_value; 339 pending_credentials_.username_value = credentials.username_value;
321 pending_credentials_.other_possible_usernames = 340 pending_credentials_.other_possible_usernames =
322 credentials.other_possible_usernames; 341 credentials.other_possible_usernames;
323 342
324 // The password value will be filled in later, remove any garbage for now. 343 // The password value will be filled in later, remove any garbage for now.
325 pending_credentials_.password_value.clear(); 344 pending_credentials_.password_value.clear();
326 pending_credentials_.new_password_value.clear(); 345 pending_credentials_.new_password_value.clear();
327 346
328 // If this was a sign-up or change password form, the names of the elements 347 // If this was a sign-up or change password form, the names of the
329 // are likely different than those on a login form, so do not bother saving 348 // elements are likely different than those on a login form, so do not
vabr (Chromium) 2015/02/02 14:18:49 Please do not just reformat the comments, if you d
Pritam Nikam 2015/02/05 06:12:06 Done.
330 // them. We will fill them with meaningful values in UpdateLogin() when the 349 // bother saving them. We will fill them with meaningful values in
331 // user goes onto a real login form for the first time. 350 // UpdateLogin() when the user goes onto a real login form for the first
351 // time.
332 if (!credentials.new_password_element.empty()) { 352 if (!credentials.new_password_element.empty()) {
333 pending_credentials_.password_element.clear(); 353 pending_credentials_.password_element.clear();
334 pending_credentials_.new_password_element.clear(); 354 pending_credentials_.new_password_element.clear();
335 } 355 }
336 } 356 }
337 357
338 pending_credentials_.action = credentials.action; 358 pending_credentials_.action = credentials.action;
339 // If the user selected credentials we autofilled from a PasswordForm 359 // If the user selected credentials we autofilled from a PasswordForm
340 // that contained no action URL (IE6/7 imported passwords, for example), 360 // that contained no action URL (IE6/7 imported passwords, for example),
341 // bless it with the action URL from the observed form. See bug 1107719. 361 // bless it with the action URL from the observed form. See bug 1107719.
(...skipping 29 matching lines...) Expand all
371 state_ = MATCHING_PHASE; 391 state_ = MATCHING_PHASE;
372 392
373 scoped_ptr<BrowserSavePasswordProgressLogger> logger; 393 scoped_ptr<BrowserSavePasswordProgressLogger> logger;
374 if (client_->IsLoggingActive()) { 394 if (client_->IsLoggingActive()) {
375 logger.reset(new BrowserSavePasswordProgressLogger(client_)); 395 logger.reset(new BrowserSavePasswordProgressLogger(client_));
376 logger->LogMessage(Logger::STRING_FETCH_LOGINS_METHOD); 396 logger->LogMessage(Logger::STRING_FETCH_LOGINS_METHOD);
377 } 397 }
378 398
379 // Do not autofill on sign-up or change password forms (until we have some 399 // Do not autofill on sign-up or change password forms (until we have some
380 // working change password functionality). 400 // working change password functionality).
381 if (!observed_form_.new_password_element.empty()) { 401 if (!observed_form_.new_password_element.empty() &&
402 !base::CommandLine::ForCurrentProcess()->HasSwitch(
vabr (Chromium) 2015/02/02 14:18:49 This change looks like a bad merge, and nothing to
Pritam Nikam 2015/02/05 06:12:06 Done.
403 autofill::switches::kEnablePasswordSaveOnInPageNavigation)) {
382 if (logger) 404 if (logger)
383 logger->LogMessage(Logger::STRING_FORM_NOT_AUTOFILLED); 405 logger->LogMessage(Logger::STRING_FORM_NOT_AUTOFILLED);
384 client_->AutofillResultsComputed(); 406 client_->AutofillResultsComputed();
385 // There is no point in looking for the credentials in the store when they 407 // There is no point in looking for the credentials in the store when they
386 // won't be autofilled, so pretend there were none. 408 // won't be autofilled, so pretend there were none.
387 std::vector<autofill::PasswordForm*> dummy_results; 409 std::vector<autofill::PasswordForm*> dummy_results;
388 OnGetPasswordStoreResults(dummy_results); 410 OnGetPasswordStoreResults(dummy_results);
389 return; 411 return;
390 } 412 }
391 413
392 PasswordStore* password_store = client_->GetPasswordStore(); 414 PasswordStore* password_store = client_->GetPasswordStore();
393 if (!password_store) { 415 if (!password_store) {
394 if (logger) 416 if (logger)
395 logger->LogMessage(Logger::STRING_NO_STORE); 417 logger->LogMessage(Logger::STRING_NO_STORE);
396 NOTREACHED(); 418 NOTREACHED();
397 return; 419 return;
398 } 420 }
399 password_store->GetLogins(observed_form_, prompt_policy, this); 421 password_store->GetLogins(observed_form_, prompt_policy, this);
400 } 422 }
401 423
402 bool PasswordFormManager::HasCompletedMatching() const { 424 bool PasswordFormManager::HasCompletedMatching() const {
403 return state_ == POST_MATCHING_PHASE; 425 return state_ == POST_MATCHING_PHASE;
404 } 426 }
405 427
406 bool PasswordFormManager::IsIgnorableChangePasswordForm() const { 428 bool PasswordFormManager::IsIgnorableChangePasswordForm() const {
407 bool is_change_password_form = !observed_form_.new_password_element.empty() &&
408 !observed_form_.password_element.empty();
409 bool is_username_certainly_correct = observed_form_.username_marked_by_site; 429 bool is_username_certainly_correct = observed_form_.username_marked_by_site;
410 return is_change_password_form && !is_username_certainly_correct; 430 return IsChangePasswordForm(observed_form_) && !is_username_certainly_correct;
411 } 431 }
412 432
413 void PasswordFormManager::OnRequestDone( 433 void PasswordFormManager::OnRequestDone(
414 const std::vector<PasswordForm*>& logins_result) { 434 const std::vector<PasswordForm*>& logins_result) {
415 // Note that the result gets deleted after this call completes, but we own 435 // Note that the result gets deleted after this call completes, but we own
416 // the PasswordForm objects pointed to by the result vector, thus we keep 436 // the PasswordForm objects pointed to by the result vector, thus we keep
417 // copies to a minimum here. 437 // copies to a minimum here.
418 438
419 scoped_ptr<BrowserSavePasswordProgressLogger> logger; 439 scoped_ptr<BrowserSavePasswordProgressLogger> logger;
420 if (client_->IsLoggingActive()) { 440 if (client_->IsLoggingActive()) {
(...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after
861 if (has_generated_password_) 881 if (has_generated_password_)
862 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED); 882 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMITTED);
863 } 883 }
864 884
865 void PasswordFormManager::SubmitFailed() { 885 void PasswordFormManager::SubmitFailed() {
866 submit_result_ = kSubmitResultFailed; 886 submit_result_ = kSubmitResultFailed;
867 if (has_generated_password_) 887 if (has_generated_password_)
868 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED); 888 LogPasswordGenerationSubmissionEvent(PASSWORD_SUBMISSION_FAILED);
869 } 889 }
870 890
891 bool PasswordFormManager::HasMatchingCredentialsInStore(
892 const autofill::PasswordForm& candidate,
893 autofill::PasswordForm* matching_form) const {
894 bool match_found = false;
895 for (const auto& match : best_matches_) {
896 if (match.second->username_value == candidate.username_value &&
897 match.second->password_value == candidate.password_value) {
898 if (matching_form)
vabr (Chromium) 2015/02/02 14:18:49 There should also be a check that the candidate ha
Pritam Nikam 2015/02/05 06:12:06 Done. Removed this function, instead performed in-
899 *matching_form = *(match.second);
900 match_found = true;
901 break;
902 }
903 }
904
905 return match_found;
906 }
907
871 } // namespace password_manager 908 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698