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

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: Comments 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
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, form_elements->form_element,
504 username_element, password_element);
505 }
506
534 // We might have already filled this form if there are two <form> elements 507 // We might have already filled this form if there are two <form> elements
535 // with identical markup. 508 // with identical markup.
536 if (login_to_password_info_.find(username_element) != 509 if (login_to_password_info_.find(username_element) !=
537 login_to_password_info_.end()) 510 login_to_password_info_.end())
538 continue; 511 continue;
539 512
540 PasswordInfo password_info; 513 PasswordInfo password_info;
541 password_info.fill_data = form_data; 514 password_info.fill_data = form_data;
542 password_info.password_field = password_element; 515 password_info.password_field = password_element;
543 login_to_password_info_[username_element] = password_info; 516 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, 591 bounding_box.width() * scale,
619 bounding_box.height() * scale); 592 bounding_box.height() * scale);
620 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), 593 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(),
621 field, 594 field,
622 bounding_box_scaled, 595 bounding_box_scaled,
623 suggestions, 596 suggestions,
624 realms)); 597 realms));
625 return !suggestions.empty(); 598 return !suggestions.empty();
626 } 599 }
627 600
601 void PasswordAutofillAgent::FillFormOnPasswordRecieved(
602 const PasswordFormFillData& fill_data,
603 const WebKit::WebFormElement& form_element,
604 WebKit::WebInputElement username_element,
605 WebKit::WebInputElement password_element) {
606 if (!form_element.autoComplete())
607 return;
608
609 // If we can't modify the password, don't try to set the username
610 if (!IsElementEditable(password_element) || !password_element.autoComplete())
611 return;
612
613 // Try and set the username to the preferred name, but only if the field
614 // can be set and isn't prefilled.
615 if (IsElementEditable(username_element) &&
616 username_element.autoComplete() &&
617 username_element.value().isEmpty()) {
618 username_element.setValue(fill_data.basic_data.fields[0].value);
619 }
620
621 // Fill if we have an exact match for the username. Note that this sets
622 // username to autofilled.
623 FillUserNameAndPassword(&username_element, &password_element, fill_data,
624 true /* exact_username_match */,
625 false /* set_selection */);
626 }
627
628 bool PasswordAutofillAgent::FillUserNameAndPassword( 628 bool PasswordAutofillAgent::FillUserNameAndPassword(
629 WebKit::WebInputElement* username_element, 629 WebKit::WebInputElement* username_element,
630 WebKit::WebInputElement* password_element, 630 WebKit::WebInputElement* password_element,
631 const PasswordFormFillData& fill_data, 631 const PasswordFormFillData& fill_data,
632 bool exact_username_match, 632 bool exact_username_match,
633 bool set_selection) { 633 bool set_selection) {
634 base::string16 current_username = username_element->value(); 634 base::string16 current_username = username_element->value();
635 // username and password will contain the match found if any. 635 // username and password will contain the match found if any.
636 base::string16 username; 636 base::string16 username;
637 base::string16 password; 637 base::string16 password;
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
678 678
679 // Input matches the username, fill in required values. 679 // Input matches the username, fill in required values.
680 username_element->setValue(username); 680 username_element->setValue(username);
681 681
682 if (set_selection) { 682 if (set_selection) {
683 username_element->setSelectionRange(current_username.length(), 683 username_element->setSelectionRange(current_username.length(),
684 username.length()); 684 username.length());
685 } 685 }
686 686
687 SetElementAutofilled(username_element, true); 687 SetElementAutofilled(username_element, true);
688 if (IsElementEditable(*password_element)) 688 if (IsElementEditable(*password_element)) {
689 // TODO(tkent): Check maxlength and pattern
Ilya Sherman 2013/11/05 01:25:12 This should apply to the username element as well,
Garrett Casto 2013/11/05 01:56:39 Done.
689 password_element->setValue(password); 690 password_element->setValue(password);
691 }
690 SetElementAutofilled(password_element, true); 692 SetElementAutofilled(password_element, true);
691 return true; 693 return true;
692 } 694 }
693 695
694 void PasswordAutofillAgent::PerformInlineAutocomplete( 696 void PasswordAutofillAgent::PerformInlineAutocomplete(
695 const WebKit::WebInputElement& username_input, 697 const WebKit::WebInputElement& username_input,
696 const WebKit::WebInputElement& password_input, 698 const WebKit::WebInputElement& password_input,
697 const PasswordFormFillData& fill_data) { 699 const PasswordFormFillData& fill_data) {
698 DCHECK(!fill_data.wait_for_username); 700 DCHECK(!fill_data.wait_for_username);
699 701
700 // We need non-const versions of the username and password inputs. 702 // We need non-const versions of the username and password inputs.
701 WebKit::WebInputElement username = username_input; 703 WebKit::WebInputElement username = username_input;
702 WebKit::WebInputElement password = password_input; 704 WebKit::WebInputElement password = password_input;
703 705
704 // Don't inline autocomplete if the caret is not at the end. 706 // 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? 707 // TODO(jcivelli): is there a better way to test the caret location?
706 if (username.selectionStart() != username.selectionEnd() || 708 if (username.selectionStart() != username.selectionEnd() ||
707 username.selectionEnd() != static_cast<int>(username.value().length())) { 709 username.selectionEnd() != static_cast<int>(username.value().length())) {
708 return; 710 return;
709 } 711 }
710 712
711 // Show the popup with the list of available usernames. 713 // Show the popup with the list of available usernames.
712 ShowSuggestionPopup(fill_data, username); 714 ShowSuggestionPopup(fill_data, username);
713 715
714 716
715 #if !defined(OS_ANDROID) 717 #if !defined(OS_ANDROID)
716 // Fill the user and password field with the most relevant match. Android 718 // 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. 719 // only fills in the fields after the user clicks on the suggestion popup.
718 FillUserNameAndPassword(&username, &password, fill_data, false, true); 720 FillUserNameAndPassword(&username, &password, fill_data,
721 false /* exact_username_match */,
722 true /* set_selection */);
719 #endif 723 #endif
720 } 724 }
721 725
722 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { 726 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) {
723 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); 727 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
724 iter != login_to_password_info_.end();) { 728 iter != login_to_password_info_.end();) {
725 if (iter->first.document().frame() == frame) 729 if (iter->first.document().frame() == frame)
726 login_to_password_info_.erase(iter++); 730 login_to_password_info_.erase(iter++);
727 else 731 else
728 ++iter; 732 ++iter;
(...skipping 14 matching lines...) Expand all
743 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); 747 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input);
744 if (iter == login_to_password_info_.end()) 748 if (iter == login_to_password_info_.end())
745 return false; 749 return false;
746 750
747 *found_input = input; 751 *found_input = input;
748 *found_password = iter->second; 752 *found_password = iter->second;
749 return true; 753 return true;
750 } 754 }
751 755
752 } // namespace autofill 756 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698