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

Side by Side Diff: components/autofill/content/renderer/password_autofill_agent.cc

Issue 414013003: Password autofill should not override explicitly typed password (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments addressed Created 6 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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/autofill/content/renderer/password_autofill_agent.h" 5 #include "components/autofill/content/renderer/password_autofill_agent.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/memory/scoped_ptr.h" 8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
266 elements_.clear(); 266 elements_.clear();
267 } 267 }
268 268
269 void PasswordAutofillAgent::PasswordValueGatekeeper::Reset() { 269 void PasswordAutofillAgent::PasswordValueGatekeeper::Reset() {
270 was_user_gesture_seen_ = false; 270 was_user_gesture_seen_ = false;
271 elements_.clear(); 271 elements_.clear();
272 } 272 }
273 273
274 void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue( 274 void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue(
275 blink::WebInputElement* element) { 275 blink::WebInputElement* element) {
276 if (!element->isNull() && !element->suggestedValue().isNull()) 276 if (!element->isNull() && !element->suggestedValue().isEmpty())
277 element->setValue(element->suggestedValue(), true); 277 element->setValue(element->suggestedValue(), true);
278 } 278 }
279 279
280 bool PasswordAutofillAgent::TextFieldDidEndEditing( 280 bool PasswordAutofillAgent::TextFieldDidEndEditing(
281 const blink::WebInputElement& element) { 281 const blink::WebInputElement& element) {
282 LoginToPasswordInfoMap::const_iterator iter = 282 LoginToPasswordInfoMap::const_iterator iter =
283 login_to_password_info_.find(element); 283 login_to_password_info_.find(element);
284 if (iter == login_to_password_info_.end()) 284 if (iter == login_to_password_info_.end())
285 return false; 285 return false;
286 286
287 const PasswordFormFillData& fill_data = iter->second.fill_data; 287 const PasswordInfo& password_info = iter->second;
288 // Don't let autofill overwrite an explicit change made by the user.
289 if (password_info.wait_for_username_change)
290 return false;
291
292 const PasswordFormFillData& fill_data = password_info.fill_data;
288 293
289 // If wait_for_username is false, we should have filled when the text changed. 294 // If wait_for_username is false, we should have filled when the text changed.
290 if (!fill_data.wait_for_username) 295 if (!fill_data.wait_for_username)
291 return false; 296 return false;
292 297
293 blink::WebInputElement password = iter->second.password_field; 298 blink::WebInputElement password = password_info.password_field;
294 if (!IsElementEditable(password)) 299 if (!IsElementEditable(password))
295 return false; 300 return false;
296 301
297 blink::WebInputElement username = element; // We need a non-const. 302 blink::WebInputElement username = element; // We need a non-const.
298 303
299 // Do not set selection when ending an editing session, otherwise it can 304 // Do not set selection when ending an editing session, otherwise it can
300 // mess with focus. 305 // mess with focus.
301 FillUserNameAndPassword(&username, 306 FillUserNameAndPassword(&username,
302 &password, 307 &password,
303 fill_data, 308 fill_data,
304 true /* exact_username_match */, 309 true /* exact_username_match */,
305 false /* set_selection */); 310 false /* set_selection */);
306 return true; 311 return true;
307 } 312 }
308 313
309 bool PasswordAutofillAgent::TextDidChangeInTextField( 314 bool PasswordAutofillAgent::TextDidChangeInTextField(
310 const blink::WebInputElement& element) { 315 const blink::WebInputElement& element) {
316 // TODO(vabr): Get a mutable argument instead. http://crbug.com/397083
317 blink::WebInputElement mutable_element = element; // We need a non-const.
318
311 if (element.isPasswordField()) { 319 if (element.isPasswordField()) {
312 // Some login forms have event handlers that put a hash of the password into 320 // Some login forms have event handlers that put a hash of the password into
313 // a hidden field and then clear the password (http://crbug.com/28910, 321 // a hidden field and then clear the password (http://crbug.com/28910,
314 // http://crbug.com/391693). This method gets called before any of those 322 // http://crbug.com/391693). This method gets called before any of those
315 // handlers run, so save away a copy of the password in case it gets lost. 323 // handlers run, so save away a copy of the password in case it gets lost.
316 // To honor the user having explicitly cleared the password, even an empty 324 // To honor the user having explicitly cleared the password, even an empty
317 // password will be saved here. 325 // password will be saved here.
318 ProvisionallySavePassword( 326 ProvisionallySavePassword(
319 element.document().frame(), element.form(), RESTRICTION_NONE); 327 element.document().frame(), element.form(), RESTRICTION_NONE);
328
329 PasswordToLoginMap::iterator iter = password_to_username_.find(element);
330 if (iter != password_to_username_.end()) {
331 // The user changed the password after it was autofilled. Flipping the
engedy 2014/08/06 10:18:17 nit: I would personally drop this comment, and int
vabr (Chromium) 2014/08/25 14:53:29 Comment dropped. The summary came out as a duplica
332 // flag below to true makes sure it is not overwritten by autofill
333 // re-firing for the same username.
334 login_to_password_info_[iter->second].wait_for_username_change = true;
engedy 2014/08/06 10:18:17 I think this is not really needed, but for clarity
vabr (Chromium) 2014/08/25 14:53:29 Only made a comment to that effect. The suggested
335 mutable_element.setAutofilled(false);
336 }
320 return false; 337 return false;
321 } 338 }
322 339
323 LoginToPasswordInfoMap::const_iterator iter = 340 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element);
324 login_to_password_info_.find(element);
325 if (iter == login_to_password_info_.end()) 341 if (iter == login_to_password_info_.end())
326 return false; 342 return false;
327 343
328 // The input text is being changed, so any autofilled password is now 344 // The input text is being changed, so any autofilled password is now
329 // outdated. 345 // outdated.
330 blink::WebInputElement username = element; // We need a non-const. 346 mutable_element.setAutofilled(false);
331 username.setAutofilled(false); 347 iter->second.wait_for_username_change = false;
332 348
333 blink::WebInputElement password = iter->second.password_field; 349 blink::WebInputElement password = iter->second.password_field;
334 if (password.isAutofilled()) { 350 if (password.isAutofilled()) {
335 password.setValue(base::string16(), true); 351 password.setValue(base::string16(), true);
336 password.setAutofilled(false); 352 password.setAutofilled(false);
337 } 353 }
338 354
339 // If wait_for_username is true we will fill when the username loses focus. 355 // If wait_for_username is true we will fill when the username loses focus.
340 if (iter->second.fill_data.wait_for_username) 356 if (iter->second.fill_data.wait_for_username)
341 return false; 357 return false;
342 358
343 if (!element.isText() || !IsElementAutocompletable(element) || 359 if (!element.isText() || !IsElementAutocompletable(element) ||
344 !IsElementAutocompletable(password)) { 360 !IsElementAutocompletable(password)) {
345 return false; 361 return false;
346 } 362 }
347 363
348 // Don't inline autocomplete if the user is deleting, that would be confusing. 364 // Don't inline autocomplete if the user is deleting, that would be confusing.
349 // But refresh the popup. Note, since this is ours, return true to signal 365 // But refresh the popup. Note, since this is ours, return true to signal
350 // no further processing is required. 366 // no further processing is required.
351 if (iter->second.backspace_pressed_last) { 367 if (iter->second.backspace_pressed_last) {
352 ShowSuggestionPopup(iter->second.fill_data, username, false); 368 ShowSuggestionPopup(iter->second.fill_data, element, false);
353 return true; 369 return true;
354 } 370 }
355 371
356 blink::WebString name = element.nameForAutofill(); 372 blink::WebString name = element.nameForAutofill();
357 if (name.isEmpty()) 373 if (name.isEmpty())
358 return false; // If the field has no name, then we won't have values. 374 return false; // If the field has no name, then we won't have values.
359 375
360 // Don't attempt to autofill with values that are too large. 376 // Don't attempt to autofill with values that are too large.
361 if (element.value().length() > kMaximumTextSizeForAutocomplete) 377 if (element.value().length() > kMaximumTextSizeForAutocomplete)
362 return false; 378 return false;
(...skipping 18 matching lines...) Expand all
381 iter->second.backspace_pressed_last = 397 iter->second.backspace_pressed_last =
382 (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE); 398 (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE);
383 return true; 399 return true;
384 } 400 }
385 401
386 bool PasswordAutofillAgent::FillSuggestion( 402 bool PasswordAutofillAgent::FillSuggestion(
387 const blink::WebNode& node, 403 const blink::WebNode& node,
388 const blink::WebString& username, 404 const blink::WebString& username,
389 const blink::WebString& password) { 405 const blink::WebString& password) {
390 blink::WebInputElement username_element; 406 blink::WebInputElement username_element;
391 PasswordInfo password_info; 407 PasswordInfo* password_info;
392 408
393 if (!FindLoginInfo(node, &username_element, &password_info) || 409 if (!FindLoginInfo(node, &username_element, &password_info) ||
394 !IsElementAutocompletable(username_element) || 410 !IsElementAutocompletable(username_element) ||
395 !IsElementAutocompletable(password_info.password_field)) { 411 !IsElementAutocompletable(password_info->password_field)) {
396 return false; 412 return false;
397 } 413 }
398 414
399 base::string16 current_username = username_element.value(); 415 if (username_element.value() != username_element.value())
engedy 2014/08/06 10:18:17 If we are overwriting the password nevertheless, w
vabr (Chromium) 2014/08/25 14:53:29 Good catch! Agreed and done.
416 password_info->wait_for_username_change = false;
400 username_element.setValue(username, true); 417 username_element.setValue(username, true);
401 username_element.setAutofilled(true); 418 username_element.setAutofilled(true);
402 username_element.setSelectionRange(username.length(), username.length()); 419 username_element.setSelectionRange(username.length(), username.length());
403 420
404 password_info.password_field.setValue(password, true); 421 password_info->password_field.setValue(password, true);
405 password_info.password_field.setAutofilled(true); 422 password_info->password_field.setAutofilled(true);
406 423
407 return true; 424 return true;
408 } 425 }
409 426
410 bool PasswordAutofillAgent::PreviewSuggestion( 427 bool PasswordAutofillAgent::PreviewSuggestion(
411 const blink::WebNode& node, 428 const blink::WebNode& node,
412 const blink::WebString& username, 429 const blink::WebString& username,
413 const blink::WebString& password) { 430 const blink::WebString& password) {
414 blink::WebInputElement username_element; 431 blink::WebInputElement username_element;
415 PasswordInfo password_info; 432 PasswordInfo* password_info;
416 433
417 if (!FindLoginInfo(node, &username_element, &password_info) || 434 if (!FindLoginInfo(node, &username_element, &password_info) ||
418 !IsElementAutocompletable(username_element) || 435 !IsElementAutocompletable(username_element) ||
419 !IsElementAutocompletable(password_info.password_field)) { 436 !IsElementAutocompletable(password_info->password_field)) {
420 return false; 437 return false;
421 } 438 }
422 439
423 was_username_autofilled_ = username_element.isAutofilled(); 440 was_username_autofilled_ = username_element.isAutofilled();
424 username_selection_start_ = username_element.selectionStart(); 441 username_selection_start_ = username_element.selectionStart();
425 username_element.setSuggestedValue(username); 442 username_element.setSuggestedValue(username);
426 username_element.setAutofilled(true); 443 username_element.setAutofilled(true);
427 username_element.setSelectionRange( 444 username_element.setSelectionRange(
428 username_selection_start_, 445 username_selection_start_,
429 username_element.suggestedValue().length()); 446 username_element.suggestedValue().length());
430 447
431 was_password_autofilled_ = password_info.password_field.isAutofilled(); 448 was_password_autofilled_ = password_info->password_field.isAutofilled();
432 password_info.password_field.setSuggestedValue(password); 449 password_info->password_field.setSuggestedValue(password);
433 password_info.password_field.setAutofilled(true); 450 password_info->password_field.setAutofilled(true);
434 451
435 return true; 452 return true;
436 } 453 }
437 454
438 bool PasswordAutofillAgent::DidClearAutofillSelection( 455 bool PasswordAutofillAgent::DidClearAutofillSelection(
439 const blink::WebNode& node) { 456 const blink::WebNode& node) {
440 blink::WebInputElement username_element; 457 blink::WebInputElement username_element;
441 PasswordInfo password_info; 458 PasswordInfo* password_info;
442 if (!FindLoginInfo(node, &username_element, &password_info)) 459 if (!FindLoginInfo(node, &username_element, &password_info))
443 return false; 460 return false;
444 461
445 ClearPreview(&username_element, &password_info.password_field); 462 ClearPreview(&username_element, &password_info->password_field);
446 return true; 463 return true;
447 } 464 }
448 465
449 bool PasswordAutofillAgent::ShowSuggestions( 466 bool PasswordAutofillAgent::ShowSuggestions(
450 const blink::WebInputElement& element, 467 const blink::WebInputElement& element,
451 bool show_all) { 468 bool show_all) {
452 LoginToPasswordInfoMap::const_iterator iter = 469 LoginToPasswordInfoMap::const_iterator iter =
453 login_to_password_info_.find(element); 470 login_to_password_info_.find(element);
454 if (iter == login_to_password_info_.end()) 471 if (iter == login_to_password_info_.end())
455 return false; 472 return false;
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after
804 // We might have already filled this form if there are two <form> elements 821 // We might have already filled this form if there are two <form> elements
805 // with identical markup. 822 // with identical markup.
806 if (login_to_password_info_.find(username_element) != 823 if (login_to_password_info_.find(username_element) !=
807 login_to_password_info_.end()) 824 login_to_password_info_.end())
808 continue; 825 continue;
809 826
810 PasswordInfo password_info; 827 PasswordInfo password_info;
811 password_info.fill_data = form_data; 828 password_info.fill_data = form_data;
812 password_info.password_field = password_element; 829 password_info.password_field = password_element;
813 login_to_password_info_[username_element] = password_info; 830 login_to_password_info_[username_element] = password_info;
831 password_to_username_[password_element] = username_element;
814 832
815 FormData form; 833 FormData form;
816 FormFieldData field; 834 FormFieldData field;
817 FindFormAndFieldForFormControlElement( 835 FindFormAndFieldForFormControlElement(
818 username_element, &form, &field, REQUIRE_NONE); 836 username_element, &form, &field, REQUIRE_NONE);
819 Send(new AutofillHostMsg_AddPasswordFormMapping( 837 Send(new AutofillHostMsg_AddPasswordFormMapping(
820 routing_id(), field, form_data)); 838 routing_id(), field, form_data));
821 } 839 }
822 } 840 }
823 841
824 void PasswordAutofillAgent::OnSetLoggingState(bool active) { 842 void PasswordAutofillAgent::OnSetLoggingState(bool active) {
825 logging_state_active_ = active; 843 logging_state_active_ = active;
826 } 844 }
827 845
828 //////////////////////////////////////////////////////////////////////////////// 846 ////////////////////////////////////////////////////////////////////////////////
829 // PasswordAutofillAgent, private: 847 // PasswordAutofillAgent, private:
830 848
849 PasswordAutofillAgent::PasswordInfo::PasswordInfo()
850 : backspace_pressed_last(false), wait_for_username_change(false) {
851 }
852
831 void PasswordAutofillAgent::GetSuggestions( 853 void PasswordAutofillAgent::GetSuggestions(
832 const PasswordFormFillData& fill_data, 854 const PasswordFormFillData& fill_data,
833 const base::string16& input, 855 const base::string16& input,
834 std::vector<base::string16>* suggestions, 856 std::vector<base::string16>* suggestions,
835 std::vector<base::string16>* realms, 857 std::vector<base::string16>* realms,
836 bool show_all) { 858 bool show_all) {
837 if (show_all || 859 if (show_all ||
838 StartsWith(fill_data.basic_data.fields[0].value, input, false)) { 860 StartsWith(fill_data.basic_data.fields[0].value, input, false)) {
839 suggestions->push_back(fill_data.basic_data.fields[0].value); 861 suggestions->push_back(fill_data.basic_data.fields[0].value);
840 realms->push_back(base::UTF8ToUTF16(fill_data.preferred_realm)); 862 realms->push_back(base::UTF8ToUTF16(fill_data.preferred_realm));
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
1048 &password, 1070 &password,
1049 fill_data, 1071 fill_data,
1050 false /* exact_username_match */, 1072 false /* exact_username_match */,
1051 true /* set_selection */); 1073 true /* set_selection */);
1052 #endif 1074 #endif
1053 } 1075 }
1054 1076
1055 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) { 1077 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) {
1056 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); 1078 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
1057 iter != login_to_password_info_.end();) { 1079 iter != login_to_password_info_.end();) {
1058 if (iter->first.document().frame() == frame) 1080 if (iter->first.document().frame() == frame) {
1081 password_to_username_.erase(iter->second.password_field);
1059 login_to_password_info_.erase(iter++); 1082 login_to_password_info_.erase(iter++);
1060 else 1083 } else {
1061 ++iter; 1084 ++iter;
1085 }
1062 } 1086 }
1063 for (FrameToPasswordFormMap::iterator iter = 1087 for (FrameToPasswordFormMap::iterator iter =
1064 provisionally_saved_forms_.begin(); 1088 provisionally_saved_forms_.begin();
1065 iter != provisionally_saved_forms_.end();) { 1089 iter != provisionally_saved_forms_.end();) {
1066 if (iter->first == frame) 1090 if (iter->first == frame)
1067 provisionally_saved_forms_.erase(iter++); 1091 provisionally_saved_forms_.erase(iter++);
1068 else 1092 else
1069 ++iter; 1093 ++iter;
1070 } 1094 }
1071 } 1095 }
1072 1096
1073 bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node, 1097 bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node,
1074 blink::WebInputElement* found_input, 1098 blink::WebInputElement* found_input,
1075 PasswordInfo* found_password) { 1099 PasswordInfo** found_password) {
1076 if (!node.isElementNode()) 1100 if (!node.isElementNode())
1077 return false; 1101 return false;
1078 1102
1079 blink::WebElement element = node.toConst<blink::WebElement>(); 1103 blink::WebElement element = node.toConst<blink::WebElement>();
1080 if (!element.hasHTMLTagName("input")) 1104 if (!element.hasHTMLTagName("input"))
1081 return false; 1105 return false;
1082 1106
1083 blink::WebInputElement input = element.to<blink::WebInputElement>(); 1107 blink::WebInputElement input = element.to<blink::WebInputElement>();
1084 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); 1108 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input);
1085 if (iter == login_to_password_info_.end()) 1109 if (iter == login_to_password_info_.end())
1086 return false; 1110 return false;
1087 1111
1088 *found_input = input; 1112 *found_input = input;
1089 *found_password = iter->second; 1113 *found_password = &iter->second;
1090 return true; 1114 return true;
1091 } 1115 }
1092 1116
1093 void PasswordAutofillAgent::ClearPreview( 1117 void PasswordAutofillAgent::ClearPreview(
1094 blink::WebInputElement* username, 1118 blink::WebInputElement* username,
1095 blink::WebInputElement* password) { 1119 blink::WebInputElement* password) {
1096 if (!username->suggestedValue().isEmpty()) { 1120 if (!username->suggestedValue().isEmpty()) {
1097 username->setSuggestedValue(blink::WebString()); 1121 username->setSuggestedValue(blink::WebString());
1098 username->setAutofilled(was_username_autofilled_); 1122 username->setAutofilled(was_username_autofilled_);
1099 username->setSelectionRange(username_selection_start_, 1123 username->setSelectionRange(username_selection_start_,
(...skipping 13 matching lines...) Expand all
1113 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form)); 1137 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
1114 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD && 1138 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD &&
1115 password_form->password_value.empty() && 1139 password_form->password_value.empty() &&
1116 password_form->new_password_value.empty())) { 1140 password_form->new_password_value.empty())) {
1117 return; 1141 return;
1118 } 1142 }
1119 provisionally_saved_forms_[frame].reset(password_form.release()); 1143 provisionally_saved_forms_[frame].reset(password_form.release());
1120 } 1144 }
1121 1145
1122 } // namespace autofill 1146 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698