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

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

Issue 56653002: [Password Autofill] Make better use of prefilled usernames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month 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
« no previous file with comments | « components/autofill/content/renderer/password_autofill_agent.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 curr_elements->form_element = fe; 157 curr_elements->form_element = fe;
158 results->push_back(curr_elements.release()); 158 results->push_back(curr_elements.release());
159 } 159 }
160 } 160 }
161 } 161 }
162 162
163 bool IsElementEditable(const WebKit::WebInputElement& element) { 163 bool IsElementEditable(const WebKit::WebInputElement& element) {
164 return element.isEnabled() && !element.isReadOnly(); 164 return element.isEnabled() && !element.isReadOnly();
165 } 165 }
166 166
167 void FillForm(FormElements* fe, const FormData& data) {
168 if (!fe->form_element.autoComplete())
169 return;
170
171 std::map<base::string16, base::string16> data_map;
172 for (size_t i = 0; i < data.fields.size(); i++)
173 data_map[data.fields[i].name] = data.fields[i].value;
174
175 for (FormInputElementMap::iterator it = fe->input_elements.begin();
176 it != fe->input_elements.end(); ++it) {
177 WebKit::WebInputElement element = it->second;
178 // Don't fill a form that has pre-filled values distinct from the ones we
179 // want to fill with.
180 if (!element.value().isEmpty() && element.value() != data_map[it->first])
181 return;
182
183 // Don't fill forms with uneditable fields or fields with autocomplete
184 // disabled.
185 if (!IsElementEditable(element) || !element.autoComplete())
186 return;
187 }
188
189 for (FormInputElementMap::iterator it = fe->input_elements.begin();
190 it != fe->input_elements.end(); ++it) {
191 WebKit::WebInputElement element = it->second;
192
193 // TODO(tkent): Check maxlength and pattern.
194 element.setValue(data_map[it->first]);
195 element.setAutofilled(true);
196 element.dispatchFormControlChangeEvent();
197 }
198 }
199
200 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { 167 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) {
201 if (element->isAutofilled() == autofilled) 168 if (element->isAutofilled() == autofilled)
202 return; 169 return;
203 element->setAutofilled(autofilled); 170 element->setAutofilled(autofilled);
204 // Notify any changeEvent listeners. 171 // Notify any changeEvent listeners.
205 element->dispatchFormControlChangeEvent(); 172 element->dispatchFormControlChangeEvent();
206 } 173 }
207 174
208 bool DoUsernamesMatch(const base::string16& username1, 175 bool DoUsernamesMatch(const base::string16& username1,
209 const base::string16& username2, 176 const base::string16& username2,
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 return false; 210 return false;
244 211
245 WebKit::WebInputElement password = iter->second.password_field; 212 WebKit::WebInputElement password = iter->second.password_field;
246 if (!IsElementEditable(password)) 213 if (!IsElementEditable(password))
247 return false; 214 return false;
248 215
249 WebKit::WebInputElement username = element; // We need a non-const. 216 WebKit::WebInputElement username = element; // We need a non-const.
250 217
251 // Do not set selection when ending an editing session, otherwise it can 218 // Do not set selection when ending an editing session, otherwise it can
252 // mess with focus. 219 // mess with focus.
253 FillUserNameAndPassword(&username, &password, fill_data, true, false); 220 FillUserNameAndPassword(&username, &password, fill_data,
221 true /* exact_username_match */,
222 false /* set_selection */);
254 return true; 223 return true;
255 } 224 }
256 225
257 bool PasswordAutofillAgent::TextDidChangeInTextField( 226 bool PasswordAutofillAgent::TextDidChangeInTextField(
258 const WebKit::WebInputElement& element) { 227 const WebKit::WebInputElement& element) {
259 LoginToPasswordInfoMap::const_iterator iter = 228 LoginToPasswordInfoMap::const_iterator iter =
260 login_to_password_info_.find(element); 229 login_to_password_info_.find(element);
261 if (iter == login_to_password_info_.end()) 230 if (iter == login_to_password_info_.end())
262 return false; 231 return false;
263 232
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 const WebKit::WebString& value) { 293 const WebKit::WebString& value) {
325 WebKit::WebInputElement input; 294 WebKit::WebInputElement input;
326 PasswordInfo password; 295 PasswordInfo password;
327 if (!FindLoginInfo(node, &input, &password)) 296 if (!FindLoginInfo(node, &input, &password))
328 return false; 297 return false;
329 298
330 // Set the incoming |value| in the text field and |FillUserNameAndPassword| 299 // Set the incoming |value| in the text field and |FillUserNameAndPassword|
331 // will do the rest. 300 // will do the rest.
332 input.setValue(value, true); 301 input.setValue(value, true);
333 return FillUserNameAndPassword(&input, &password.password_field, 302 return FillUserNameAndPassword(&input, &password.password_field,
334 password.fill_data, true, true); 303 password.fill_data,
304 true /* exact_username_match */,
305 true /* set_selection */);
335 } 306 }
336 307
337 bool PasswordAutofillAgent::DidClearAutofillSelection( 308 bool PasswordAutofillAgent::DidClearAutofillSelection(
338 const WebKit::WebNode& node) { 309 const WebKit::WebNode& node) {
339 WebKit::WebInputElement input; 310 WebKit::WebInputElement input;
340 PasswordInfo password; 311 PasswordInfo password;
341 return FindLoginInfo(node, &input, &password); 312 return FindLoginInfo(node, &input, &password);
342 } 313 }
343 314
344 bool PasswordAutofillAgent::ShowSuggestions( 315 bool PasswordAutofillAgent::ShowSuggestions(
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
509 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; 480 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT;
510 } 481 }
511 482
512 FormElementsList forms; 483 FormElementsList forms;
513 // We own the FormElements* in forms. 484 // We own the FormElements* in forms.
514 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); 485 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms);
515 FormElementsList::iterator iter; 486 FormElementsList::iterator iter;
516 for (iter = forms.begin(); iter != forms.end(); ++iter) { 487 for (iter = forms.begin(); iter != forms.end(); ++iter) {
517 scoped_ptr<FormElements> form_elements(*iter); 488 scoped_ptr<FormElements> form_elements(*iter);
518 489
519 // If wait_for_username is true, we don't want to initially fill the form
520 // until the user types in a valid username.
521 if (!form_data.wait_for_username)
522 FillForm(form_elements.get(), form_data.basic_data);
523
524 // Attach autocomplete listener to enable selecting alternate logins. 490 // Attach autocomplete listener to enable selecting alternate logins.
525 // First, get pointers to username element. 491 // First, get pointers to username element.
526 WebKit::WebInputElement username_element = 492 WebKit::WebInputElement username_element =
527 form_elements->input_elements[form_data.basic_data.fields[0].name]; 493 form_elements->input_elements[form_data.basic_data.fields[0].name];
528 494
529 // Get pointer to password element. (We currently only support single 495 // Get pointer to password element. (We currently only support single
530 // password forms). 496 // password forms).
531 WebKit::WebInputElement password_element = 497 WebKit::WebInputElement password_element =
532 form_elements->input_elements[form_data.basic_data.fields[1].name]; 498 form_elements->input_elements[form_data.basic_data.fields[1].name];
533 499
500 // If wait_for_username is true, we don't want to initially fill the form
501 // until the user types in a valid username.
502 if (!form_data.wait_for_username)
503 FillFormOnPasswordRecieved(form_data, username_element, password_element);
504
534 // We might have already filled this form if there are two <form> elements 505 // We might have already filled this form if there are two <form> elements
535 // with identical markup. 506 // with identical markup.
536 if (login_to_password_info_.find(username_element) != 507 if (login_to_password_info_.find(username_element) !=
537 login_to_password_info_.end()) 508 login_to_password_info_.end())
538 continue; 509 continue;
539 510
540 PasswordInfo password_info; 511 PasswordInfo password_info;
541 password_info.fill_data = form_data; 512 password_info.fill_data = form_data;
542 password_info.password_field = password_element; 513 password_info.password_field = password_element;
543 login_to_password_info_[username_element] = password_info; 514 login_to_password_info_[username_element] = password_info;
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
618 bounding_box.width() * scale, 589 bounding_box.width() * scale,
619 bounding_box.height() * scale); 590 bounding_box.height() * scale);
620 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), 591 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(),
621 field, 592 field,
622 bounding_box_scaled, 593 bounding_box_scaled,
623 suggestions, 594 suggestions,
624 realms)); 595 realms));
625 return !suggestions.empty(); 596 return !suggestions.empty();
626 } 597 }
627 598
599 void PasswordAutofillAgent::FillFormOnPasswordRecieved(
600 const PasswordFormFillData& fill_data,
601 WebKit::WebInputElement username_element,
602 WebKit::WebInputElement password_element) {
603 if (!username_element.form().autoComplete())
604 return;
605
606 // If we can't modify the password, don't try to set the username
607 if (!IsElementEditable(password_element) || !password_element.autoComplete())
608 return;
609
610 // Try to set the username to the preferred name, but only if the field
611 // can be set and isn't prefilled.
612 if (IsElementEditable(username_element) &&
613 username_element.autoComplete() &&
614 username_element.value().isEmpty()) {
615 // TODO(tkent): Check maxlength and pattern.
616 username_element.setValue(fill_data.basic_data.fields[0].value);
617 }
618
619 // Fill if we have an exact match for the username. Note that this sets
620 // username to autofilled.
621 FillUserNameAndPassword(&username_element, &password_element, fill_data,
622 true /* exact_username_match */,
623 false /* set_selection */);
624 }
625
628 bool PasswordAutofillAgent::FillUserNameAndPassword( 626 bool PasswordAutofillAgent::FillUserNameAndPassword(
629 WebKit::WebInputElement* username_element, 627 WebKit::WebInputElement* username_element,
630 WebKit::WebInputElement* password_element, 628 WebKit::WebInputElement* password_element,
631 const PasswordFormFillData& fill_data, 629 const PasswordFormFillData& fill_data,
632 bool exact_username_match, 630 bool exact_username_match,
633 bool set_selection) { 631 bool set_selection) {
634 base::string16 current_username = username_element->value(); 632 base::string16 current_username = username_element->value();
635 // username and password will contain the match found if any. 633 // username and password will contain the match found if any.
636 base::string16 username; 634 base::string16 username;
637 base::string16 password; 635 base::string16 password;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 } 667 }
670 } 668 }
671 if (!username.empty() && !password.empty()) 669 if (!username.empty() && !password.empty())
672 break; 670 break;
673 } 671 }
674 } 672 }
675 } 673 }
676 if (password.empty()) 674 if (password.empty())
677 return false; // No match was found. 675 return false; // No match was found.
678 676
679 // Input matches the username, fill in required values. 677 // TODO(tkent): Check maxlength and pattern for both username and password
680 username_element->setValue(username); 678 // fields.
681 679
682 if (set_selection) { 680 // Don't fill username if password can't be set.
683 username_element->setSelectionRange(current_username.length(), 681 if (!IsElementEditable(*password_element) ||
684 username.length()); 682 !password_element->autoComplete()) {
683 return false;
685 } 684 }
686 685
687 SetElementAutofilled(username_element, true); 686 // Input matches the username, fill in required values.
688 if (IsElementEditable(*password_element)) 687 if (IsElementEditable(*username_element) &&
689 password_element->setValue(password); 688 username_element->autoComplete()) {
689 username_element->setValue(username);
690 SetElementAutofilled(username_element, true);
691
692 if (set_selection) {
693 username_element->setSelectionRange(current_username.length(),
694 username.length());
695 }
696 } else if (current_username != username) {
697 // If the username can't be filled and it doesn't match a saved password
698 // as is, don't autofill a password.
699 return false;
700 }
701
702 password_element->setValue(password);
690 SetElementAutofilled(password_element, true); 703 SetElementAutofilled(password_element, true);
691 return true; 704 return true;
692 } 705 }
693 706
694 void PasswordAutofillAgent::PerformInlineAutocomplete( 707 void PasswordAutofillAgent::PerformInlineAutocomplete(
695 const WebKit::WebInputElement& username_input, 708 const WebKit::WebInputElement& username_input,
696 const WebKit::WebInputElement& password_input, 709 const WebKit::WebInputElement& password_input,
697 const PasswordFormFillData& fill_data) { 710 const PasswordFormFillData& fill_data) {
698 DCHECK(!fill_data.wait_for_username); 711 DCHECK(!fill_data.wait_for_username);
699 712
700 // We need non-const versions of the username and password inputs. 713 // We need non-const versions of the username and password inputs.
701 WebKit::WebInputElement username = username_input; 714 WebKit::WebInputElement username = username_input;
702 WebKit::WebInputElement password = password_input; 715 WebKit::WebInputElement password = password_input;
703 716
704 // Don't inline autocomplete if the caret is not at the end. 717 // Don't inline autocomplete if the caret is not at the end.
705 // TODO(jcivelli): is there a better way to test the caret location? 718 // TODO(jcivelli): is there a better way to test the caret location?
706 if (username.selectionStart() != username.selectionEnd() || 719 if (username.selectionStart() != username.selectionEnd() ||
707 username.selectionEnd() != static_cast<int>(username.value().length())) { 720 username.selectionEnd() != static_cast<int>(username.value().length())) {
708 return; 721 return;
709 } 722 }
710 723
711 // Show the popup with the list of available usernames. 724 // Show the popup with the list of available usernames.
712 ShowSuggestionPopup(fill_data, username); 725 ShowSuggestionPopup(fill_data, username);
713 726
714 727
715 #if !defined(OS_ANDROID) 728 #if !defined(OS_ANDROID)
716 // Fill the user and password field with the most relevant match. Android 729 // Fill the user and password field with the most relevant match. Android
717 // only fills in the fields after the user clicks on the suggestion popup. 730 // only fills in the fields after the user clicks on the suggestion popup.
718 FillUserNameAndPassword(&username, &password, fill_data, false, true); 731 FillUserNameAndPassword(&username, &password, fill_data,
732 false /* exact_username_match */,
733 true /* set_selection */);
719 #endif 734 #endif
720 } 735 }
721 736
722 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { 737 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) {
723 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); 738 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
724 iter != login_to_password_info_.end();) { 739 iter != login_to_password_info_.end();) {
725 if (iter->first.document().frame() == frame) 740 if (iter->first.document().frame() == frame)
726 login_to_password_info_.erase(iter++); 741 login_to_password_info_.erase(iter++);
727 else 742 else
728 ++iter; 743 ++iter;
(...skipping 14 matching lines...) Expand all
743 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); 758 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input);
744 if (iter == login_to_password_info_.end()) 759 if (iter == login_to_password_info_.end())
745 return false; 760 return false;
746 761
747 *found_input = input; 762 *found_input = input;
748 *found_password = iter->second; 763 *found_password = iter->second;
749 return true; 764 return true;
750 } 765 }
751 766
752 } // namespace autofill 767 } // namespace autofill
OLDNEW
« no previous file with comments | « components/autofill/content/renderer/password_autofill_agent.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698