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

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

Issue 2918543002: Add username field discovery heuristic. (Closed)
Patch Set: Created 3 years, 6 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
« 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 <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm>
9 #include <memory> 10 #include <memory>
10 #include <string> 11 #include <string>
11 #include <utility> 12 #include <utility>
12 #include <vector> 13 #include <vector>
13 14
14 #include "base/bind.h" 15 #include "base/bind.h"
15 #include "base/i18n/case_conversion.h" 16 #include "base/i18n/case_conversion.h"
16 #include "base/memory/linked_ptr.h" 17 #include "base/memory/linked_ptr.h"
17 #include "base/memory/ptr_util.h" 18 #include "base/memory/ptr_util.h"
18 #include "base/message_loop/message_loop.h" 19 #include "base/message_loop/message_loop.h"
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 270
270 bool DoUsernamesMatch(const base::string16& username1, 271 bool DoUsernamesMatch(const base::string16& username1,
271 const base::string16& username2, 272 const base::string16& username2,
272 bool exact_match) { 273 bool exact_match) {
273 if (exact_match) 274 if (exact_match)
274 return username1 == username2; 275 return username1 == username2;
275 return FieldIsSuggestionSubstringStartingOnTokenBoundary(username1, username2, 276 return FieldIsSuggestionSubstringStartingOnTokenBoundary(username1, username2,
276 true); 277 true);
277 } 278 }
278 279
279 // Returns |true| if the given element is editable. Otherwise, returns |false|. 280 // Returns whether the given |element| is editable.
280 bool IsElementAutocompletable(const blink::WebInputElement& element) { 281 bool IsElementAutocompletable(const blink::WebInputElement& element) {
281 return IsElementEditable(element); 282 return IsElementEditable(element);
282 } 283 }
283 284
284 // Returns whether the |username_element| is allowed to be autofilled. 285 // Returns whether the |username_element| is allowed to be autofilled.
285 // 286 //
286 // Note that if the user interacts with the |password_field| and the 287 // Note that if the user interacts with the |password_field| and the
287 // |username_element| is user-defined (i.e., non-empty and non-autofilled), then 288 // |username_element| is user-defined (i.e., non-empty and non-autofilled), then
288 // this function returns false. This is a precaution, to not override the field 289 // this function returns false. This is a precaution, to not override the field
289 // if it has been classified as username by accident. 290 // if it has been classified as username by accident.
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 if (element.IsFormControlElement()) { 601 if (element.IsFormControlElement()) {
601 const blink::WebFormControlElement& control = 602 const blink::WebFormControlElement& control =
602 element.To<blink::WebFormControlElement>(); 603 element.To<blink::WebFormControlElement>();
603 if (control.FormControlType() == kPassword) 604 if (control.FormControlType() == kPassword)
604 return true; 605 return true;
605 } 606 }
606 } 607 }
607 return false; 608 return false;
608 } 609 }
609 610
611 // Returns the closest visible autocompletable non-password text element
612 // preceding the |password_element| either in a form, if it belongs to one, or
613 // in the |frame|.
614 blink::WebInputElement FindUsernameElementPrecedingPasswordElement(
615 blink::WebFrame* frame,
616 const blink::WebInputElement& password_element) {
617 DCHECK(!password_element.IsNull());
618
619 std::vector<blink::WebFormControlElement> elements;
620 if (password_element.Form().IsNull()) {
621 elements = form_util::GetUnownedAutofillableFormFieldElements(
622 frame->GetDocument().All(), nullptr);
623 } else {
624 blink::WebVector<blink::WebFormControlElement> web_control_elements;
625 password_element.Form().GetFormControlElements(web_control_elements);
626 elements.assign(web_control_elements.begin(), web_control_elements.end());
627 }
628
629 auto iter = std::find(elements.begin(), elements.end(), password_element);
630 if (iter == elements.end())
631 return blink::WebInputElement();
632
633 for (auto begin = elements.begin(); iter != begin;) {
634 --iter;
635 const blink::WebInputElement* input = blink::ToWebInputElement(&*iter);
636 if (input && input->IsTextField() && !input->IsPasswordField() &&
637 IsElementAutocompletable(*input) &&
638 form_util::IsWebElementVisible(*input)) {
639 return *input;
640 }
641 }
642
643 return blink::WebInputElement();
644 }
645
610 } // namespace 646 } // namespace
611 647
612 class PasswordAutofillAgent::FormElementObserverCallback 648 class PasswordAutofillAgent::FormElementObserverCallback
613 : public blink::WebFormElementObserverCallback { 649 : public blink::WebFormElementObserverCallback {
614 public: 650 public:
615 explicit FormElementObserverCallback(PasswordAutofillAgent* agent) 651 explicit FormElementObserverCallback(PasswordAutofillAgent* agent)
616 : agent_(agent) {} 652 : agent_(agent) {}
617 ~FormElementObserverCallback() override = default; 653 ~FormElementObserverCallback() override = default;
618 654
619 void ElementWasHiddenOrRemoved() override { 655 void ElementWasHiddenOrRemoved() override {
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
768 const blink::WebFormControlElement& control_element, 804 const blink::WebFormControlElement& control_element,
769 const base::string16& username, 805 const base::string16& username,
770 const base::string16& password) { 806 const base::string16& password) {
771 // The element in context of the suggestion popup. 807 // The element in context of the suggestion popup.
772 const blink::WebInputElement* element = ToWebInputElement(&control_element); 808 const blink::WebInputElement* element = ToWebInputElement(&control_element);
773 if (!element) 809 if (!element)
774 return false; 810 return false;
775 811
776 blink::WebInputElement username_element; 812 blink::WebInputElement username_element;
777 blink::WebInputElement password_element; 813 blink::WebInputElement password_element;
778 PasswordInfo* password_info; 814 PasswordInfo* password_info = nullptr;
779 815
780 if (!FindPasswordInfoForElement(*element, &username_element, 816 if (!FindPasswordInfoForElement(*element, &username_element,
781 &password_element, &password_info) || 817 &password_element, &password_info) ||
782 !IsElementAutocompletable(password_element)) { 818 !IsElementAutocompletable(password_element)) {
783 return false; 819 return false;
784 } 820 }
785 821
786 password_info->password_was_edited_last = false; 822 password_info->password_was_edited_last = false;
787 if (element->IsPasswordField()) { 823 if (element->IsPasswordField()) {
788 password_info->password_field_suggestion_was_accepted = true; 824 password_info->password_field_suggestion_was_accepted = true;
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
876 if (!element.IsPasswordField()) { 912 if (!element.IsPasswordField()) {
877 *username_element = element; 913 *username_element = element;
878 } else { 914 } else {
879 // If there is a password field, but a request to the store hasn't been sent 915 // If there is a password field, but a request to the store hasn't been sent
880 // yet, then do fetch saved credentials now. 916 // yet, then do fetch saved credentials now.
881 if (!sent_request_to_store_) { 917 if (!sent_request_to_store_) {
882 SendPasswordForms(false); 918 SendPasswordForms(false);
883 return false; 919 return false;
884 } 920 }
885 921
922 *password_element = element;
923
886 WebInputToPasswordInfoMap::iterator iter = 924 WebInputToPasswordInfoMap::iterator iter =
887 web_input_to_password_info_.find(element); 925 web_input_to_password_info_.find(element);
926 if (iter == web_input_to_password_info_.end()) {
927 PasswordToLoginMap::const_iterator password_iter =
928 password_to_username_.find(element);
929 if (password_iter == password_to_username_.end()) {
930 if (web_input_to_password_info_.empty())
931 return false;
932 // Now all PasswordInfo items refer to the same set of credentials for
933 // fill, so it is ok to take any of them.
934 iter = web_input_to_password_info_.begin();
935 } else {
936 *username_element = password_iter->second;
937 }
938 }
939
888 if (iter != web_input_to_password_info_.end()) { 940 if (iter != web_input_to_password_info_.end()) {
889 // It's a password field without corresponding username field. 941 // It's a password field without corresponding username field. Try to find
890 *password_element = element; 942 // the username field based on visibility.
943 *username_element = FindUsernameElementPrecedingPasswordElement(
944 render_frame()->GetWebFrame(), *password_element);
891 *password_info = &iter->second; 945 *password_info = &iter->second;
892 return true; 946 return true;
893 } 947 }
894 PasswordToLoginMap::const_iterator password_iter = 948 // Otherwise |username_element| has been set above.
895 password_to_username_.find(element);
896 if (password_iter == password_to_username_.end()) {
897 if (web_input_to_password_info_.empty())
898 return false;
899
900 *password_element = element;
901 // Now all PasswordInfo items refer to the same set of credentials for
902 // fill, so it is ok to take any of them.
903 *password_info = &web_input_to_password_info_.begin()->second;
904 return true;
905 }
906 *username_element = password_iter->second;
907 *password_element = element;
908 } 949 }
909 950
910 WebInputToPasswordInfoMap::iterator iter = 951 WebInputToPasswordInfoMap::iterator iter =
911 web_input_to_password_info_.find(*username_element); 952 web_input_to_password_info_.find(*username_element);
912
913 if (iter == web_input_to_password_info_.end()) 953 if (iter == web_input_to_password_info_.end())
914 return false; 954 return false;
915 955
916 *password_info = &iter->second; 956 *password_info = &iter->second;
917 if (password_element->IsNull()) 957 if (password_element->IsNull())
918 *password_element = (*password_info)->password_field; 958 *password_element = (*password_info)->password_field;
919 959
920 return true; 960 return true;
921 } 961 }
922 962
(...skipping 809 matching lines...) Expand 10 before | Expand all | Expand 10 after
1732 PasswordAutofillAgent::GetPasswordManagerDriver() { 1772 PasswordAutofillAgent::GetPasswordManagerDriver() {
1733 if (!password_manager_driver_) { 1773 if (!password_manager_driver_) {
1734 render_frame()->GetRemoteInterfaces()->GetInterface( 1774 render_frame()->GetRemoteInterfaces()->GetInterface(
1735 mojo::MakeRequest(&password_manager_driver_)); 1775 mojo::MakeRequest(&password_manager_driver_));
1736 } 1776 }
1737 1777
1738 return password_manager_driver_; 1778 return password_manager_driver_;
1739 } 1779 }
1740 1780
1741 } // namespace autofill 1781 } // 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