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

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

Issue 548953002: [Password Manager] Modified to support saving passwords on forms without username fields. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Incorporated review comments. Created 6 years, 3 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 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 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 64
65 // Helper to search the given form element for the specified input elements 65 // Helper to search the given form element for the specified input elements
66 // in |data|, and add results to |result|. 66 // in |data|, and add results to |result|.
67 static bool FindFormInputElements(blink::WebFormElement* fe, 67 static bool FindFormInputElements(blink::WebFormElement* fe,
68 const FormData& data, 68 const FormData& data,
69 FormElements* result) { 69 FormElements* result) {
70 // Loop through the list of elements we need to find on the form in order to 70 // Loop through the list of elements we need to find on the form in order to
71 // autofill it. If we don't find any one of them, abort processing this 71 // autofill it. If we don't find any one of them, abort processing this
72 // form; it can't be the right one. 72 // form; it can't be the right one.
73 for (size_t j = 0; j < data.fields.size(); j++) { 73 for (size_t j = 0; j < data.fields.size(); j++) {
74 // Only consider non-empty input fields for autofilling
vabr (Chromium) 2014/09/24 09:47:43 I'm confused by this comment -- http://crbug.com/4
Pritam Nikam 2014/09/24 12:57:55 Done.
75 // (http://crbug.com/406343).
76 if (data.fields[j].name.empty())
77 continue;
78
74 blink::WebVector<blink::WebNode> temp_elements; 79 blink::WebVector<blink::WebNode> temp_elements;
75 fe->getNamedElements(data.fields[j].name, temp_elements); 80 fe->getNamedElements(data.fields[j].name, temp_elements);
76 81
77 // Match the first input element, if any. 82 // Match the first input element, if any.
78 // |getNamedElements| may return non-input elements where the names match, 83 // |getNamedElements| may return non-input elements where the names match,
79 // so the results are filtered for input elements. 84 // so the results are filtered for input elements.
80 // If more than one match is made, then we have ambiguity (due to misuse 85 // If more than one match is made, then we have ambiguity (due to misuse
81 // of "name" attribute) so is it considered not found. 86 // of "name" attribute) so is it considered not found.
82 bool found_input = false; 87 bool found_input = false;
83 for (size_t i = 0; i < temp_elements.size(); ++i) { 88 for (size_t i = 0; i < temp_elements.size(); ++i) {
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 216
212 // Log a message including the name, method and action of |form|. 217 // Log a message including the name, method and action of |form|.
213 void LogHTMLForm(SavePasswordProgressLogger* logger, 218 void LogHTMLForm(SavePasswordProgressLogger* logger,
214 SavePasswordProgressLogger::StringID message_id, 219 SavePasswordProgressLogger::StringID message_id,
215 const blink::WebFormElement& form) { 220 const blink::WebFormElement& form) {
216 logger->LogHTMLForm(message_id, 221 logger->LogHTMLForm(message_id,
217 form.name().utf8(), 222 form.name().utf8(),
218 GURL(form.action().utf8())); 223 GURL(form.action().utf8()));
219 } 224 }
220 225
226 // Returns |true| if |fill_data| holds an empty username field; otherwise
227 // |false|.
228 static bool FormWithEmptyUsernameField(const PasswordFormFillData& fill_data) {
vabr (Chromium) 2014/09/24 09:47:43 Again -- the comment and the function name say "em
vabr (Chromium) 2014/09/24 09:47:44 No need to include "static" -- the function is inv
Pritam Nikam 2014/09/24 12:57:55 Done.
Pritam Nikam 2014/09/24 12:57:55 Done.
229 return fill_data.basic_data.fields[0].name.empty();
230 }
231
221 } // namespace 232 } // namespace
222 233
223 //////////////////////////////////////////////////////////////////////////////// 234 ////////////////////////////////////////////////////////////////////////////////
224 // PasswordAutofillAgent, public: 235 // PasswordAutofillAgent, public:
225 236
226 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view) 237 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view)
227 : content::RenderViewObserver(render_view), 238 : content::RenderViewObserver(render_view),
228 usernames_usage_(NOTHING_TO_AUTOFILL), 239 usernames_usage_(NOTHING_TO_AUTOFILL),
229 web_view_(render_view->GetWebView()), 240 web_view_(render_view->GetWebView()),
230 logging_state_active_(false), 241 logging_state_active_(false),
(...skipping 564 matching lines...) Expand 10 before | Expand all | Expand 10 after
795 } 806 }
796 807
797 FormElementsList forms; 808 FormElementsList forms;
798 // We own the FormElements* in forms. 809 // We own the FormElements* in forms.
799 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); 810 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms);
800 FormElementsList::iterator iter; 811 FormElementsList::iterator iter;
801 for (iter = forms.begin(); iter != forms.end(); ++iter) { 812 for (iter = forms.begin(); iter != forms.end(); ++iter) {
802 scoped_ptr<FormElements> form_elements(*iter); 813 scoped_ptr<FormElements> form_elements(*iter);
803 814
804 // Attach autocomplete listener to enable selecting alternate logins. 815 // Attach autocomplete listener to enable selecting alternate logins.
805 // First, get pointers to username element. 816 blink::WebInputElement username_element, password_element;
806 blink::WebInputElement username_element = 817
807 form_elements->input_elements[form_data.basic_data.fields[0].name]; 818 // Check whether password form has username input field
vabr (Chromium) 2014/09/24 09:47:43 grammar nit: add articles -- the password form, a
Pritam Nikam 2014/09/24 12:57:55 Done.
819 // (http://crbug.com/406343).
vabr (Chromium) 2014/09/24 09:47:43 nit: I'm not sure it's necessary to include the re
Pritam Nikam 2014/09/24 12:57:55 Done.
820 bool is_password_only_form = FormWithEmptyUsernameField(form_data);
vabr (Chromium) 2014/09/24 09:47:43 (Note: if you change the function to FillDataConta
Pritam Nikam 2014/09/24 12:57:55 Done.
821 if (!is_password_only_form)
vabr (Chromium) 2014/09/24 09:47:43 nit: Add braces, because the body of the if-clause
Pritam Nikam 2014/09/24 12:57:55 Done.
822 username_element =
823 form_elements->input_elements[form_data.basic_data.fields[0].name];
824
825 // No password field, bail out.
826 if (form_data.basic_data.fields[1].name.empty())
827 break;
808 828
809 // Get pointer to password element. (We currently only support single 829 // Get pointer to password element. (We currently only support single
810 // password forms). 830 // password forms).
811 blink::WebInputElement password_element = 831 password_element =
812 form_elements->input_elements[form_data.basic_data.fields[1].name]; 832 form_elements->input_elements[form_data.basic_data.fields[1].name];
813 833
814 // If wait_for_username is true, we don't want to initially fill the form 834 // If wait_for_username is true, we don't want to initially fill the form
815 // until the user types in a valid username. 835 // until the user types in a valid username.
816 if (!form_data.wait_for_username) 836 if (!form_data.wait_for_username)
817 FillFormOnPasswordRecieved(form_data, username_element, password_element); 837 FillFormOnPasswordRecieved(form_data, username_element, password_element);
818 838
819 // We might have already filled this form if there are two <form> elements 839 // We might have already filled this form if there are two <form> elements
820 // with identical markup. 840 // with identical markup.
821 if (login_to_password_info_.find(username_element) != 841 if (!is_password_only_form &&
vabr (Chromium) 2014/09/24 09:47:43 Why do you exclude the case of no-username passwor
Pritam Nikam 2014/09/24 12:57:55 Done.
822 login_to_password_info_.end()) 842 login_to_password_info_.find(username_element) !=
843 login_to_password_info_.end())
823 continue; 844 continue;
824 845
825 PasswordInfo password_info; 846 PasswordInfo password_info;
826 password_info.fill_data = form_data; 847 password_info.fill_data = form_data;
827 password_info.password_field = password_element; 848 password_info.password_field = password_element;
828 login_to_password_info_[username_element] = password_info; 849 login_to_password_info_[username_element] = password_info;
829 password_to_username_[password_element] = username_element; 850 password_to_username_[password_element] = username_element;
830 851
831 FormData form; 852 FormData form;
832 FormFieldData field; 853 FormFieldData field;
833 FindFormAndFieldForFormControlElement( 854 if (!is_password_only_form)
834 username_element, &form, &field, REQUIRE_NONE); 855 FindFormAndFieldForFormControlElement(
vabr (Chromium) 2014/09/24 09:47:43 nit: missing braces
Pritam Nikam 2014/09/24 12:57:55 Done.
856 username_element, &form, &field, REQUIRE_NONE);
857
835 Send(new AutofillHostMsg_AddPasswordFormMapping( 858 Send(new AutofillHostMsg_AddPasswordFormMapping(
836 routing_id(), field, form_data)); 859 routing_id(), field, form_data));
837 } 860 }
838 } 861 }
839 862
840 void PasswordAutofillAgent::OnSetLoggingState(bool active) { 863 void PasswordAutofillAgent::OnSetLoggingState(bool active) {
841 logging_state_active_ = active; 864 logging_state_active_ = active;
842 } 865 }
843 866
844 //////////////////////////////////////////////////////////////////////////////// 867 ////////////////////////////////////////////////////////////////////////////////
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
922 945
923 void PasswordAutofillAgent::FillFormOnPasswordRecieved( 946 void PasswordAutofillAgent::FillFormOnPasswordRecieved(
924 const PasswordFormFillData& fill_data, 947 const PasswordFormFillData& fill_data,
925 blink::WebInputElement username_element, 948 blink::WebInputElement username_element,
926 blink::WebInputElement password_element) { 949 blink::WebInputElement password_element) {
927 // Do not fill if the password field is in an iframe. 950 // Do not fill if the password field is in an iframe.
928 DCHECK(password_element.document().frame()); 951 DCHECK(password_element.document().frame());
929 if (password_element.document().frame()->parent()) 952 if (password_element.document().frame()->parent())
930 return; 953 return;
931 954
955 bool is_password_only_form = FormWithEmptyUsernameField(fill_data);
932 if (!ShouldIgnoreAutocompleteOffForPasswordFields() && 956 if (!ShouldIgnoreAutocompleteOffForPasswordFields() &&
933 !username_element.form().autoComplete()) 957 (!is_password_only_form && !username_element.form().autoComplete()))
vabr (Chromium) 2014/09/24 09:47:43 nit: no need for the additional parentheses -- &&
Pritam Nikam 2014/09/24 12:57:55 Done.
934 return; 958 return;
935 959
936 // If we can't modify the password, don't try to set the username 960 // If we can't modify the password, don't try to set the username
937 if (!IsElementAutocompletable(password_element)) 961 if (!IsElementAutocompletable(password_element))
938 return; 962 return;
939 963
940 // Try to set the username to the preferred name, but only if the field 964 // Try to set the username to the preferred name, but only if the field
941 // can be set and isn't prefilled. 965 // can be set and isn't prefilled.
942 if (IsElementAutocompletable(username_element) && 966 if (!is_password_only_form && IsElementAutocompletable(username_element) &&
943 username_element.value().isEmpty()) { 967 username_element.value().isEmpty()) {
944 // TODO(tkent): Check maxlength and pattern. 968 // TODO(tkent): Check maxlength and pattern.
945 username_element.setValue(fill_data.basic_data.fields[0].value, true); 969 username_element.setValue(fill_data.basic_data.fields[0].value, true);
946 } 970 }
947 971
948 // Fill if we have an exact match for the username. Note that this sets 972 // Fill if we have an exact match for the username. Note that this sets
949 // username to autofilled. 973 // username to autofilled.
950 FillUserNameAndPassword(&username_element, 974 FillUserNameAndPassword(&username_element,
951 &password_element, 975 &password_element,
952 fill_data, 976 fill_data,
953 true /* exact_username_match */, 977 true /* exact_username_match */,
954 false /* set_selection */); 978 false /* set_selection */);
955 } 979 }
956 980
957 bool PasswordAutofillAgent::FillUserNameAndPassword( 981 bool PasswordAutofillAgent::FillUserNameAndPassword(
958 blink::WebInputElement* username_element, 982 blink::WebInputElement* username_element,
959 blink::WebInputElement* password_element, 983 blink::WebInputElement* password_element,
960 const PasswordFormFillData& fill_data, 984 const PasswordFormFillData& fill_data,
961 bool exact_username_match, 985 bool exact_username_match,
962 bool set_selection) { 986 bool set_selection) {
963 base::string16 current_username = username_element->value();
964 // username and password will contain the match found if any. 987 // username and password will contain the match found if any.
965 base::string16 username; 988 base::string16 username;
966 base::string16 password; 989 base::string16 password;
990 bool is_password_only_form = FormWithEmptyUsernameField(fill_data);
967 991
968 // Don't fill username if password can't be set. 992 // Don't fill username if password can't be set.
969 if (!IsElementAutocompletable(*password_element)) 993 if (!IsElementAutocompletable(*password_element))
970 return false; 994 return false;
971 995
972 // Look for any suitable matches to current field text. 996 // Password form can have only password-input field (http://crbug.com/406343).
973 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, 997 if (is_password_only_form) {
vabr (Chromium) 2014/09/24 09:47:44 Suggestion: if you modify this check to also consi
Pritam Nikam 2014/09/24 12:57:55 I think, I didn't get this correctly.
974 current_username,
975 exact_username_match)) {
976 username = fill_data.basic_data.fields[0].value;
977 password = fill_data.basic_data.fields[1].value; 998 password = fill_data.basic_data.fields[1].value;
978 } else { 999 } else {
979 // Scan additional logins for a match. 1000 base::string16 current_username = username_element->value();
980 PasswordFormFillData::LoginCollection::const_iterator iter; 1001
981 for (iter = fill_data.additional_logins.begin(); 1002 // Look for any suitable matches to current field text.
982 iter != fill_data.additional_logins.end(); 1003 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value,
983 ++iter) { 1004 current_username,
984 if (DoUsernamesMatch( 1005 exact_username_match)) {
985 iter->first, current_username, exact_username_match)) { 1006 username = fill_data.basic_data.fields[0].value;
986 username = iter->first; 1007 password = fill_data.basic_data.fields[1].value;
987 password = iter->second.password; 1008 } else {
988 break; 1009 // Scan additional logins for a match.
1010 PasswordFormFillData::LoginCollection::const_iterator iter;
1011 for (iter = fill_data.additional_logins.begin();
1012 iter != fill_data.additional_logins.end();
1013 ++iter) {
1014 if (DoUsernamesMatch(
1015 iter->first, current_username, exact_username_match)) {
1016 username = iter->first;
1017 password = iter->second.password;
1018 break;
1019 }
1020 }
1021
1022 // Check possible usernames.
1023 if (username.empty() && password.empty()) {
1024 for (PasswordFormFillData::UsernamesCollection::const_iterator iter =
1025 fill_data.other_possible_usernames.begin();
1026 iter != fill_data.other_possible_usernames.end();
1027 ++iter) {
1028 for (size_t i = 0; i < iter->second.size(); ++i) {
1029 if (DoUsernamesMatch(
1030 iter->second[i], current_username, exact_username_match)) {
1031 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
1032 username = iter->second[i];
1033 password = iter->first.password;
1034 break;
1035 }
1036 }
1037 if (!username.empty() && !password.empty())
1038 break;
1039 }
989 } 1040 }
990 } 1041 }
991 1042
992 // Check possible usernames. 1043 if (password.empty())
993 if (username.empty() && password.empty()) { 1044 return false; // No match was found.
994 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = 1045
995 fill_data.other_possible_usernames.begin(); 1046 // TODO(tkent): Check maxlength and pattern for both username and password
996 iter != fill_data.other_possible_usernames.end(); 1047 // fields.
997 ++iter) { 1048
998 for (size_t i = 0; i < iter->second.size(); ++i) { 1049 // Input matches the username, fill in required values.
999 if (DoUsernamesMatch( 1050 if (IsElementAutocompletable(*username_element)) {
1000 iter->second[i], current_username, exact_username_match)) { 1051 username_element->setValue(username, true);
1001 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED; 1052 username_element->setAutofilled(true);
1002 username = iter->second[i]; 1053
1003 password = iter->first.password; 1054 if (set_selection) {
1004 break; 1055 username_element->setSelectionRange(current_username.length(),
1005 } 1056 username.length());
1006 }
1007 if (!username.empty() && !password.empty())
1008 break;
1009 } 1057 }
1058 } else if (current_username != username) {
1059 // If the username can't be filled and it doesn't match a saved password
1060 // as is, don't autofill a password.
1061 return false;
1010 } 1062 }
1011 } 1063 }
1012 if (password.empty())
1013 return false; // No match was found.
1014
1015 // TODO(tkent): Check maxlength and pattern for both username and password
1016 // fields.
1017
1018 // Input matches the username, fill in required values.
1019 if (IsElementAutocompletable(*username_element)) {
1020 username_element->setValue(username, true);
1021 username_element->setAutofilled(true);
1022
1023 if (set_selection) {
1024 username_element->setSelectionRange(current_username.length(),
1025 username.length());
1026 }
1027 } else if (current_username != username) {
1028 // If the username can't be filled and it doesn't match a saved password
1029 // as is, don't autofill a password.
1030 return false;
1031 }
1032 1064
1033 // Wait to fill in the password until a user gesture occurs. This is to make 1065 // Wait to fill in the password until a user gesture occurs. This is to make
1034 // sure that we do not fill in the DOM with a password until we believe the 1066 // sure that we do not fill in the DOM with a password until we believe the
1035 // user is intentionally interacting with the page. 1067 // user is intentionally interacting with the page.
1036 password_element->setSuggestedValue(password); 1068 password_element->setSuggestedValue(password);
1037 gatekeeper_.RegisterElement(password_element); 1069 gatekeeper_.RegisterElement(password_element);
1038 1070
1039 password_element->setAutofilled(true); 1071 password_element->setAutofilled(true);
1040 return true; 1072 return true;
1041 } 1073 }
(...skipping 25 matching lines...) Expand all
1067 &password, 1099 &password,
1068 fill_data, 1100 fill_data,
1069 false /* exact_username_match */, 1101 false /* exact_username_match */,
1070 true /* set_selection */); 1102 true /* set_selection */);
1071 #endif 1103 #endif
1072 } 1104 }
1073 1105
1074 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) { 1106 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) {
1075 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); 1107 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
1076 iter != login_to_password_info_.end();) { 1108 iter != login_to_password_info_.end();) {
1077 if (iter->first.document().frame() == frame) { 1109 // Password form may not have username input fields
vabr (Chromium) 2014/09/24 09:47:43 Suggested rephrasing to simplify the comment: //
Pritam Nikam 2014/09/24 12:57:55 Done.
1110 // (http://crbug.com/406343), and hence consider password input field
1111 // instead as a reference.
1112 if (iter->second.password_field.document().frame() == frame) {
1078 password_to_username_.erase(iter->second.password_field); 1113 password_to_username_.erase(iter->second.password_field);
1079 login_to_password_info_.erase(iter++); 1114 login_to_password_info_.erase(iter++);
1080 } else { 1115 } else {
1081 ++iter; 1116 ++iter;
1082 } 1117 }
1083 } 1118 }
1084 for (FrameToPasswordFormMap::iterator iter = 1119 for (FrameToPasswordFormMap::iterator iter =
1085 provisionally_saved_forms_.begin(); 1120 provisionally_saved_forms_.begin();
1086 iter != provisionally_saved_forms_.end();) { 1121 iter != provisionally_saved_forms_.end();) {
1087 if (iter->first == frame) 1122 if (iter->first == frame)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
1134 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form)); 1169 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
1135 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD && 1170 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD &&
1136 password_form->password_value.empty() && 1171 password_form->password_value.empty() &&
1137 password_form->new_password_value.empty())) { 1172 password_form->new_password_value.empty())) {
1138 return; 1173 return;
1139 } 1174 }
1140 provisionally_saved_forms_[frame].reset(password_form.release()); 1175 provisionally_saved_forms_[frame].reset(password_form.release());
1141 } 1176 }
1142 1177
1143 } // namespace autofill 1178 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698