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

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: Created 6 years, 2 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 // The password form may not contain a username field
75 // (http://crbug.com/406343), but continue autofilling password field.
76 if (data.fields[j].name.empty())
vabr (Chromium) 2014/09/24 16:17:21 Looking at InitPasswordFormFillData, the username
Pritam Nikam 2014/09/25 07:22:28 Done. I've refactored code, and these changes are
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 bool FillDataContainsUsername(const PasswordFormFillData& fill_data) {
227 return !fill_data.basic_data.fields[0].name.empty();
228 }
229
221 } // namespace 230 } // namespace
222 231
223 //////////////////////////////////////////////////////////////////////////////// 232 ////////////////////////////////////////////////////////////////////////////////
224 // PasswordAutofillAgent, public: 233 // PasswordAutofillAgent, public:
225 234
226 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view) 235 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view)
227 : content::RenderViewObserver(render_view), 236 : content::RenderViewObserver(render_view),
228 usernames_usage_(NOTHING_TO_AUTOFILL), 237 usernames_usage_(NOTHING_TO_AUTOFILL),
229 web_view_(render_view->GetWebView()), 238 web_view_(render_view->GetWebView()),
230 logging_state_active_(false), 239 logging_state_active_(false),
(...skipping 564 matching lines...) Expand 10 before | Expand all | Expand 10 after
795 } 804 }
796 805
797 FormElementsList forms; 806 FormElementsList forms;
798 // We own the FormElements* in forms. 807 // We own the FormElements* in forms.
799 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); 808 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms);
800 FormElementsList::iterator iter; 809 FormElementsList::iterator iter;
801 for (iter = forms.begin(); iter != forms.end(); ++iter) { 810 for (iter = forms.begin(); iter != forms.end(); ++iter) {
802 scoped_ptr<FormElements> form_elements(*iter); 811 scoped_ptr<FormElements> form_elements(*iter);
803 812
804 // Attach autocomplete listener to enable selecting alternate logins. 813 // Attach autocomplete listener to enable selecting alternate logins.
805 // First, get pointers to username element. 814 blink::WebInputElement username_element, password_element;
806 blink::WebInputElement username_element = 815
807 form_elements->input_elements[form_data.basic_data.fields[0].name]; 816 // Check whether the password form has a username input field.
817 bool form_contains_username_field = FillDataContainsUsername(form_data);
818 if (form_contains_username_field) {
819 username_element =
820 form_elements->input_elements[form_data.basic_data.fields[0].name];
821 }
822
823 // No password field, bail out.
824 if (form_data.basic_data.fields[1].name.empty())
825 break;
808 826
809 // Get pointer to password element. (We currently only support single 827 // Get pointer to password element. (We currently only support single
810 // password forms). 828 // password forms).
811 blink::WebInputElement password_element = 829 password_element =
812 form_elements->input_elements[form_data.basic_data.fields[1].name]; 830 form_elements->input_elements[form_data.basic_data.fields[1].name];
813 831
814 // If wait_for_username is true, we don't want to initially fill the form 832 // If wait_for_username is true, we don't want to initially fill the form
815 // until the user types in a valid username. 833 // until the user types in a valid username.
816 if (!form_data.wait_for_username) 834 if (!form_data.wait_for_username)
817 FillFormOnPasswordRecieved(form_data, username_element, password_element); 835 FillFormOnPasswordRecieved(form_data, username_element, password_element);
818 836
819 // We might have already filled this form if there are two <form> elements 837 // We might have already filled this form if there are two <form> elements
820 // with identical markup. 838 // with identical markup.
821 if (login_to_password_info_.find(username_element) != 839 if (login_to_password_info_.find(username_element) !=
822 login_to_password_info_.end()) 840 login_to_password_info_.end())
823 continue; 841 continue;
824 842
825 PasswordInfo password_info; 843 PasswordInfo password_info;
826 password_info.fill_data = form_data; 844 password_info.fill_data = form_data;
827 password_info.password_field = password_element; 845 password_info.password_field = password_element;
828 login_to_password_info_[username_element] = password_info; 846 login_to_password_info_[username_element] = password_info;
829 password_to_username_[password_element] = username_element; 847 password_to_username_[password_element] = username_element;
830 848
831 FormData form; 849 FormData form;
832 FormFieldData field; 850 FormFieldData field;
833 FindFormAndFieldForFormControlElement( 851 if (form_contains_username_field) {
834 username_element, &form, &field, REQUIRE_NONE); 852 FindFormAndFieldForFormControlElement(
853 username_element, &form, &field, REQUIRE_NONE);
854 }
855
835 Send(new AutofillHostMsg_AddPasswordFormMapping( 856 Send(new AutofillHostMsg_AddPasswordFormMapping(
836 routing_id(), field, form_data)); 857 routing_id(), field, form_data));
837 } 858 }
838 } 859 }
839 860
840 void PasswordAutofillAgent::OnSetLoggingState(bool active) { 861 void PasswordAutofillAgent::OnSetLoggingState(bool active) {
841 logging_state_active_ = active; 862 logging_state_active_ = active;
842 } 863 }
843 864
844 //////////////////////////////////////////////////////////////////////////////// 865 ////////////////////////////////////////////////////////////////////////////////
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
922 943
923 void PasswordAutofillAgent::FillFormOnPasswordRecieved( 944 void PasswordAutofillAgent::FillFormOnPasswordRecieved(
924 const PasswordFormFillData& fill_data, 945 const PasswordFormFillData& fill_data,
925 blink::WebInputElement username_element, 946 blink::WebInputElement username_element,
926 blink::WebInputElement password_element) { 947 blink::WebInputElement password_element) {
927 // Do not fill if the password field is in an iframe. 948 // Do not fill if the password field is in an iframe.
928 DCHECK(password_element.document().frame()); 949 DCHECK(password_element.document().frame());
929 if (password_element.document().frame()->parent()) 950 if (password_element.document().frame()->parent())
930 return; 951 return;
931 952
953 bool form_contains_username_field = FillDataContainsUsername(fill_data);
932 if (!ShouldIgnoreAutocompleteOffForPasswordFields() && 954 if (!ShouldIgnoreAutocompleteOffForPasswordFields() &&
933 !username_element.form().autoComplete()) 955 form_contains_username_field && !username_element.form().autoComplete())
934 return; 956 return;
935 957
936 // If we can't modify the password, don't try to set the username 958 // If we can't modify the password, don't try to set the username
937 if (!IsElementAutocompletable(password_element)) 959 if (!IsElementAutocompletable(password_element))
938 return; 960 return;
939 961
940 // Try to set the username to the preferred name, but only if the field 962 // Try to set the username to the preferred name, but only if the field
941 // can be set and isn't prefilled. 963 // can be set and isn't prefilled.
942 if (IsElementAutocompletable(username_element) && 964 if (form_contains_username_field &&
965 IsElementAutocompletable(username_element) &&
943 username_element.value().isEmpty()) { 966 username_element.value().isEmpty()) {
944 // TODO(tkent): Check maxlength and pattern. 967 // TODO(tkent): Check maxlength and pattern.
945 username_element.setValue(fill_data.basic_data.fields[0].value, true); 968 username_element.setValue(fill_data.basic_data.fields[0].value, true);
946 } 969 }
947 970
948 // Fill if we have an exact match for the username. Note that this sets 971 // Fill if we have an exact match for the username. Note that this sets
949 // username to autofilled. 972 // username to autofilled.
950 FillUserNameAndPassword(&username_element, 973 FillUserNameAndPassword(&username_element,
951 &password_element, 974 &password_element,
952 fill_data, 975 fill_data,
953 true /* exact_username_match */, 976 true /* exact_username_match */,
954 false /* set_selection */); 977 false /* set_selection */);
955 } 978 }
956 979
957 bool PasswordAutofillAgent::FillUserNameAndPassword( 980 bool PasswordAutofillAgent::FillUserNameAndPassword(
958 blink::WebInputElement* username_element, 981 blink::WebInputElement* username_element,
959 blink::WebInputElement* password_element, 982 blink::WebInputElement* password_element,
960 const PasswordFormFillData& fill_data, 983 const PasswordFormFillData& fill_data,
961 bool exact_username_match, 984 bool exact_username_match,
962 bool set_selection) { 985 bool set_selection) {
963 base::string16 current_username = username_element->value();
964 // username and password will contain the match found if any. 986 // username and password will contain the match found if any.
965 base::string16 username; 987 base::string16 username;
966 base::string16 password; 988 base::string16 password;
967 989
968 // Don't fill username if password can't be set. 990 // Don't fill username if password can't be set.
969 if (!IsElementAutocompletable(*password_element)) 991 if (!IsElementAutocompletable(*password_element))
970 return false; 992 return false;
971 993
972 // Look for any suitable matches to current field text. 994 if (FillDataContainsUsername(fill_data)) {
973 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, 995 base::string16 current_username = username_element->value();
vabr (Chromium) 2014/09/24 16:17:21 Wouldn't this actually work just fine if there was
Pritam Nikam 2014/09/25 07:22:27 Done. Right! I did it this way now.
vabr (Chromium) 2014/09/25 10:39:59 Actually, I don't think you did it the way suggest
974 current_username, 996
975 exact_username_match)) { 997 // Look for any suitable matches to current field text.
976 username = fill_data.basic_data.fields[0].value; 998 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value,
977 password = fill_data.basic_data.fields[1].value; 999 current_username,
978 } else { 1000 exact_username_match)) {
979 // Scan additional logins for a match. 1001 username = fill_data.basic_data.fields[0].value;
980 PasswordFormFillData::LoginCollection::const_iterator iter; 1002 password = fill_data.basic_data.fields[1].value;
981 for (iter = fill_data.additional_logins.begin(); 1003 } else {
982 iter != fill_data.additional_logins.end(); 1004 // Scan additional logins for a match.
983 ++iter) { 1005 PasswordFormFillData::LoginCollection::const_iterator iter;
984 if (DoUsernamesMatch( 1006 for (iter = fill_data.additional_logins.begin();
985 iter->first, current_username, exact_username_match)) { 1007 iter != fill_data.additional_logins.end();
986 username = iter->first; 1008 ++iter) {
987 password = iter->second.password; 1009 if (DoUsernamesMatch(
988 break; 1010 iter->first, current_username, exact_username_match)) {
1011 username = iter->first;
1012 password = iter->second.password;
1013 break;
1014 }
1015 }
1016
1017 // Check possible usernames.
1018 if (username.empty() && password.empty()) {
1019 for (PasswordFormFillData::UsernamesCollection::const_iterator iter =
1020 fill_data.other_possible_usernames.begin();
1021 iter != fill_data.other_possible_usernames.end();
1022 ++iter) {
1023 for (size_t i = 0; i < iter->second.size(); ++i) {
1024 if (DoUsernamesMatch(
1025 iter->second[i], current_username, exact_username_match)) {
1026 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
1027 username = iter->second[i];
1028 password = iter->first.password;
1029 break;
1030 }
1031 }
1032 if (!username.empty() && !password.empty())
1033 break;
1034 }
989 } 1035 }
990 } 1036 }
1037 if (password.empty())
1038 return false; // No match was found.
991 1039
992 // Check possible usernames. 1040 // TODO(tkent): Check maxlength and pattern for both username and password
993 if (username.empty() && password.empty()) { 1041 // fields.
994 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = 1042
995 fill_data.other_possible_usernames.begin(); 1043 // Input matches the username, fill in required values.
996 iter != fill_data.other_possible_usernames.end(); 1044 if (IsElementAutocompletable(*username_element)) {
997 ++iter) { 1045 username_element->setValue(username, true);
998 for (size_t i = 0; i < iter->second.size(); ++i) { 1046 username_element->setAutofilled(true);
999 if (DoUsernamesMatch( 1047
1000 iter->second[i], current_username, exact_username_match)) { 1048 if (set_selection) {
1001 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED; 1049 username_element->setSelectionRange(current_username.length(),
1002 username = iter->second[i]; 1050 username.length());
1003 password = iter->first.password;
1004 break;
1005 }
1006 }
1007 if (!username.empty() && !password.empty())
1008 break;
1009 } 1051 }
1052 } else if (current_username != username) {
1053 // If the username can't be filled and it doesn't match a saved password
1054 // as is, don't autofill a password.
1055 return false;
1010 } 1056 }
1011 } 1057 } else {
1012 if (password.empty()) 1058 password = fill_data.basic_data.fields[1].value;
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 } 1059 }
1032 1060
1033 // Wait to fill in the password until a user gesture occurs. This is to make 1061 // 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 1062 // sure that we do not fill in the DOM with a password until we believe the
1035 // user is intentionally interacting with the page. 1063 // user is intentionally interacting with the page.
1036 password_element->setSuggestedValue(password); 1064 password_element->setSuggestedValue(password);
1037 gatekeeper_.RegisterElement(password_element); 1065 gatekeeper_.RegisterElement(password_element);
1038 1066
1039 password_element->setAutofilled(true); 1067 password_element->setAutofilled(true);
1040 return true; 1068 return true;
(...skipping 26 matching lines...) Expand all
1067 &password, 1095 &password,
1068 fill_data, 1096 fill_data,
1069 false /* exact_username_match */, 1097 false /* exact_username_match */,
1070 true /* set_selection */); 1098 true /* set_selection */);
1071 #endif 1099 #endif
1072 } 1100 }
1073 1101
1074 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) { 1102 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) {
1075 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); 1103 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
1076 iter != login_to_password_info_.end();) { 1104 iter != login_to_password_info_.end();) {
1077 if (iter->first.document().frame() == frame) { 1105 // There may not be a username field, so get the frame from the password
1106 // field.
1107 if (iter->second.password_field.document().frame() == frame) {
1078 password_to_username_.erase(iter->second.password_field); 1108 password_to_username_.erase(iter->second.password_field);
1079 login_to_password_info_.erase(iter++); 1109 login_to_password_info_.erase(iter++);
1080 } else { 1110 } else {
1081 ++iter; 1111 ++iter;
1082 } 1112 }
1083 } 1113 }
1084 for (FrameToPasswordFormMap::iterator iter = 1114 for (FrameToPasswordFormMap::iterator iter =
1085 provisionally_saved_forms_.begin(); 1115 provisionally_saved_forms_.begin();
1086 iter != provisionally_saved_forms_.end();) { 1116 iter != provisionally_saved_forms_.end();) {
1087 if (iter->first == frame) 1117 if (iter->first == frame)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
1134 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form)); 1164 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
1135 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD && 1165 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD &&
1136 password_form->password_value.empty() && 1166 password_form->password_value.empty() &&
1137 password_form->new_password_value.empty())) { 1167 password_form->new_password_value.empty())) {
1138 return; 1168 return;
1139 } 1169 }
1140 provisionally_saved_forms_[frame].reset(password_form.release()); 1170 provisionally_saved_forms_[frame].reset(password_form.release());
1141 } 1171 }
1142 1172
1143 } // namespace autofill 1173 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698