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

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

Issue 1292693004: [Password Manager] Autofill forms with field name and id attributes missing. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addresses Vadym's inputs. Created 5 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
« no previous file with comments | « chrome/test/data/password/ambiguous_password_form.html ('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/command_line.h" 8 #include "base/command_line.h"
9 #include "base/i18n/case_conversion.h" 9 #include "base/i18n/case_conversion.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 const std::vector<blink::WebFormControlElement>& control_elements, 77 const std::vector<blink::WebFormControlElement>& control_elements,
78 const base::string16& name) { 78 const base::string16& name) {
79 for (size_t i = 0; i < control_elements.size(); ++i) { 79 for (size_t i = 0; i < control_elements.size(); ++i) {
80 if (control_elements[i].nameForAutofill() == name) { 80 if (control_elements[i].nameForAutofill() == name) {
81 return IsWebNodeVisible(control_elements[i]); 81 return IsWebNodeVisible(control_elements[i]);
82 } 82 }
83 } 83 }
84 return false; 84 return false;
85 } 85 }
86 86
87 // Returns true if password form has username and password fields with either
88 // same or no name and id attributes supplied.
89 bool PasswordFormWithAmbiguousOrNoNameAndIdAttribute(
90 const PasswordFormFillData& fill_data) {
91 // TODO(pritam.nikam): Extend below logic once patch lands for storing the
92 // password forms having name or id attributes missing from the fields.
93 // http://crbug.com/497630
dvadym 2015/09/07 15:26:00 Please rebase on the top of save patch and impleme
Pritam Nikam 2015/09/08 15:56:19 Done.
94 return fill_data.username_field.name == fill_data.password_field.name;
95 }
96
97 bool IsPasswordField(const FormFieldData& field) {
98 return (field.form_control_type == "password");
99 }
100
101 // Returns the |field|'s autofillable name. If no name or id attribute is
102 // specified returns a dummy name.
103 base::string16 FieldName(const FormFieldData& field,
104 bool ambiguous_or_no_name_and_id_attribute) {
105 return ambiguous_or_no_name_and_id_attribute
dvadym 2015/09/07 15:26:00 Is the case of no-username field considered here?
Pritam Nikam 2015/09/08 15:56:19 For the case of no-username field, |ambiguous_or_n
dvadym 2015/09/11 14:15:34 Acknowledged
106 ? IsPasswordField(field) ? base::ASCIIToUTF16("anonymous_password")
107 : base::ASCIIToUTF16("anonymous_username")
108 : field.name;
109 }
110
87 // Utility function to find the unique entry of |control_elements| for the 111 // Utility function to find the unique entry of |control_elements| for the
88 // specified input |field|. On successful find, adds it to |result| and returns 112 // specified input |field|. On successful find, adds it to |result| and returns
89 // |true|. Otherwise clears the references from each |HTMLInputElement| from 113 // |true|. Otherwise clears the references from each |HTMLInputElement| from
90 // |result| and returns |false|. 114 // |result| and returns |false|.
91 bool FindFormInputElement( 115 bool FindFormInputElement(
92 const std::vector<blink::WebFormControlElement>& control_elements, 116 const std::vector<blink::WebFormControlElement>& control_elements,
93 const FormFieldData& field, 117 const FormFieldData& field,
118 bool ambiguous_or_no_name_and_id_attribute,
94 FormInputElementMap* result) { 119 FormInputElementMap* result) {
95 // Match the first input element, if any. 120 // Match the first input element, if any.
96 // If more than one match is made, then we have ambiguity (due to misuse
97 // of "name" attribute) so is it considered not found.
98 bool found_input = false; 121 bool found_input = false;
122 base::string16 field_name =
123 FieldName(field, ambiguous_or_no_name_and_id_attribute);
99 for (size_t i = 0; i < control_elements.size(); ++i) { 124 for (size_t i = 0; i < control_elements.size(); ++i) {
100 if (control_elements[i].nameForAutofill() != field.name) 125 DCHECK(control_elements[i].hasHTMLTagName("input"));
101 continue;
102 126
103 if (!control_elements[i].hasHTMLTagName("input")) 127 // Only fill saved passwords into password fields and usernames into text
128 // fields.
129 const blink::WebInputElement input_element =
130 control_elements[i].toConst<blink::WebInputElement>();
131 if (input_element.isPasswordField() != IsPasswordField(field))
104 continue; 132 continue;
105 133
106 // Check for a non-unique match. 134 // Check for a non-unique match.
107 if (found_input) { 135 if (found_input) {
dvadym 2015/09/07 15:26:00 Could you please allow case when there are more th
Pritam Nikam 2015/09/08 15:56:19 Done.
108 found_input = false; 136 found_input = false;
109 break; 137 break;
110 } 138 }
111 139
112 // Only fill saved passwords into password fields and usernames into 140 (*result)[field_name] = input_element;
113 // text fields.
114 const blink::WebInputElement input_element =
115 control_elements[i].toConst<blink::WebInputElement>();
116 if (input_element.isPasswordField() !=
117 (field.form_control_type == "password"))
118 continue;
119
120 (*result)[field.name] = input_element;
121 found_input = true; 141 found_input = true;
122 } 142 }
123 143
124 // A required element was not found. This is not the right form. 144 // A required element was not found. This is not the right form.
125 // Make sure no input elements from a partially matched form in this 145 // Make sure no input elements from a partially matched form in this
126 // iteration remain in the result set. 146 // iteration remain in the result set.
127 // Note: clear will remove a reference from each InputElement. 147 // Note: clear will remove a reference from each InputElement.
128 if (!found_input) { 148 if (!found_input) {
129 result->clear(); 149 result->clear();
130 return false; 150 return false;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 192
173 return group_name != 193 return group_name !=
174 kFillOnAccountSelectFieldTrialEnabledWithNoHighlightGroup; 194 kFillOnAccountSelectFieldTrialEnabledWithNoHighlightGroup;
175 } 195 }
176 196
177 // Helper to search through |control_elements| for the specified input elements 197 // Helper to search through |control_elements| for the specified input elements
178 // in |data|, and add results to |result|. 198 // in |data|, and add results to |result|.
179 bool FindFormInputElements( 199 bool FindFormInputElements(
180 const std::vector<blink::WebFormControlElement>& control_elements, 200 const std::vector<blink::WebFormControlElement>& control_elements,
181 const PasswordFormFillData& data, 201 const PasswordFormFillData& data,
202 bool ambiguous_or_no_name_and_id_attribute,
182 FormInputElementMap* result) { 203 FormInputElementMap* result) {
183 return FindFormInputElement(control_elements, data.password_field, result) && 204 return FindFormInputElement(control_elements, data.password_field,
184 (!FillDataContainsFillableUsername(data) || 205 ambiguous_or_no_name_and_id_attribute, result) &&
185 FindFormInputElement(control_elements, data.username_field, result)); 206 (!(ambiguous_or_no_name_and_id_attribute ||
207 FillDataContainsFillableUsername(data)) ||
208 FindFormInputElement(control_elements, data.username_field,
209 ambiguous_or_no_name_and_id_attribute, result));
186 } 210 }
187 211
188 // Helper to locate form elements identified by |data|. 212 // Helper to locate form elements identified by |data|.
189 void FindFormElements(content::RenderFrame* render_frame, 213 void FindFormElements(content::RenderFrame* render_frame,
190 const PasswordFormFillData& data, 214 const PasswordFormFillData& data,
215 bool ambiguous_or_no_name_and_id_attribute,
191 FormElementsList* results) { 216 FormElementsList* results) {
192 DCHECK(results); 217 DCHECK(results);
193 218
194 blink::WebDocument doc = render_frame->GetWebFrame()->document(); 219 blink::WebDocument doc = render_frame->GetWebFrame()->document();
195 if (!doc.isHTMLDocument()) 220 if (!doc.isHTMLDocument())
196 return; 221 return;
197 222
198 if (data.origin != GetCanonicalOriginForDocument(doc)) 223 if (data.origin != GetCanonicalOriginForDocument(doc))
199 return; 224 return;
200 225
201 blink::WebVector<blink::WebFormElement> forms; 226 blink::WebVector<blink::WebFormElement> forms;
202 doc.forms(forms); 227 doc.forms(forms);
203 228
204 for (size_t i = 0; i < forms.size(); ++i) { 229 for (size_t i = 0; i < forms.size(); ++i) {
205 blink::WebFormElement fe = forms[i]; 230 blink::WebFormElement fe = forms[i];
206 231
207 // Action URL must match. 232 // Action URL must match.
208 if (data.action != GetCanonicalActionForForm(fe)) 233 if (data.action != GetCanonicalActionForForm(fe))
209 continue; 234 continue;
210 235
211 std::vector<blink::WebFormControlElement> control_elements = 236 std::vector<blink::WebFormControlElement> control_elements =
212 ExtractAutofillableElementsInForm(fe); 237 ExtractAutofillableElementsInForm(fe);
213 FormInputElementMap cur_map; 238 FormInputElementMap cur_map;
214 if (FindFormInputElements(control_elements, data, &cur_map)) 239 if (FindFormInputElements(control_elements, data,
240 ambiguous_or_no_name_and_id_attribute, &cur_map))
215 results->push_back(cur_map); 241 results->push_back(cur_map);
216 } 242 }
217 // If the element to be filled are not in a <form> element, the "action" and 243 // If the element to be filled are not in a <form> element, the "action" and
218 // origin should be the same. 244 // origin should be the same.
219 if (data.action != data.origin) 245 if (data.action != data.origin)
220 return; 246 return;
221 247
222 std::vector<blink::WebFormControlElement> control_elements = 248 std::vector<blink::WebFormControlElement> control_elements =
223 GetUnownedAutofillableFormFieldElements(doc.all(), nullptr); 249 GetUnownedAutofillableFormFieldElements(doc.all(), nullptr);
224 FormInputElementMap unowned_elements_map; 250 FormInputElementMap unowned_elements_map;
225 if (FindFormInputElements(control_elements, data, &unowned_elements_map)) 251 if (FindFormInputElements(control_elements, data,
252 ambiguous_or_no_name_and_id_attribute,
253 &unowned_elements_map))
226 results->push_back(unowned_elements_map); 254 results->push_back(unowned_elements_map);
227 } 255 }
228 256
229 bool IsElementEditable(const blink::WebInputElement& element) { 257 bool IsElementEditable(const blink::WebInputElement& element) {
230 return element.isEnabled() && !element.isReadOnly(); 258 return element.isEnabled() && !element.isReadOnly();
231 } 259 }
232 260
233 bool DoUsernamesMatch(const base::string16& username1, 261 bool DoUsernamesMatch(const base::string16& username1,
234 const base::string16& username2, 262 const base::string16& username2,
235 bool exact_match) { 263 bool exact_match) {
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after
444 while (cur_frame->parent()) { 472 while (cur_frame->parent()) {
445 cur_frame = cur_frame->parent(); 473 cur_frame = cur_frame->parent();
446 if (!bottom_frame_origin.equals(cur_frame->securityOrigin().toString())) 474 if (!bottom_frame_origin.equals(cur_frame->securityOrigin().toString()))
447 return false; 475 return false;
448 } 476 }
449 477
450 // If we can't modify the password, don't try to set the username 478 // If we can't modify the password, don't try to set the username
451 if (!IsElementAutocompletable(password_element)) 479 if (!IsElementAutocompletable(password_element))
452 return false; 480 return false;
453 481
454 bool form_contains_fillable_username_field = 482 base::string16 username_field_name =
455 FillDataContainsFillableUsername(fill_data); 483 FieldName(fill_data.username_field,
484 PasswordFormWithAmbiguousOrNoNameAndIdAttribute(fill_data));
456 // If the form contains an autocompletable username field, try to set the 485 // If the form contains an autocompletable username field, try to set the
457 // username to the preferred name, but only if: 486 // username to the preferred name, but only if:
458 // (a) The fill-on-account-select flag is not set, and 487 // (a) The fill-on-account-select flag is not set, and
459 // (b) The username element isn't prefilled 488 // (b) The username element isn't prefilled
460 // 489 //
461 // If (a) is false, then just mark the username element as autofilled if the 490 // If (a) is false, then just mark the username element as autofilled if the
462 // user is not in the "no highlighting" group and return so the fill step is 491 // user is not in the "no highlighting" group and return so the fill step is
463 // skipped. 492 // skipped.
464 // 493 //
465 // If there is no autocompletable username field, and (a) is false, then the 494 // If there is no autocompletable username field, and (a) is false, then the
466 // username element cannot be autofilled, but the user should still be able to 495 // username element cannot be autofilled, but the user should still be able to
467 // select to fill the password element, so the password element must be marked 496 // select to fill the password element, so the password element must be marked
468 // as autofilled and the fill step should also be skipped if the user is not 497 // as autofilled and the fill step should also be skipped if the user is not
469 // in the "no highlighting" group. 498 // in the "no highlighting" group.
470 // 499 //
471 // In all other cases, do nothing. 500 // In all other cases, do nothing.
472 bool form_has_fillable_username = form_contains_fillable_username_field && 501 bool form_has_fillable_username = !username_field_name.empty() &&
473 IsElementAutocompletable(username_element); 502 IsElementAutocompletable(username_element);
474 503
475 if (ShouldFillOnAccountSelect()) { 504 if (ShouldFillOnAccountSelect()) {
476 if (!ShouldHighlightFields()) { 505 if (!ShouldHighlightFields()) {
477 return false; 506 return false;
478 } 507 }
479 508
480 if (form_has_fillable_username) { 509 if (form_has_fillable_username) {
481 username_element.setAutofilled(true); 510 username_element.setAutofilled(true);
482 } else if (username_element.isNull() || 511 } else if (username_element.isNull() ||
(...skipping 713 matching lines...) Expand 10 before | Expand all | Expand 10 after
1196 } 1225 }
1197 1226
1198 // This is a new navigation, so require a new user gesture before filling in 1227 // This is a new navigation, so require a new user gesture before filling in
1199 // passwords. 1228 // passwords.
1200 gatekeeper_.Reset(); 1229 gatekeeper_.Reset();
1201 } 1230 }
1202 1231
1203 void PasswordAutofillAgent::OnFillPasswordForm( 1232 void PasswordAutofillAgent::OnFillPasswordForm(
1204 int key, 1233 int key,
1205 const PasswordFormFillData& form_data) { 1234 const PasswordFormFillData& form_data) {
1206 1235 bool ambiguous_or_no_name_and_id_attribute =
1236 PasswordFormWithAmbiguousOrNoNameAndIdAttribute(form_data);
1207 FormElementsList forms; 1237 FormElementsList forms;
1208 FindFormElements(render_frame(), form_data, &forms); 1238 FindFormElements(render_frame(), form_data,
1239 ambiguous_or_no_name_and_id_attribute, &forms);
1209 FormElementsList::iterator iter; 1240 FormElementsList::iterator iter;
1210 for (iter = forms.begin(); iter != forms.end(); ++iter) { 1241 for (iter = forms.begin(); iter != forms.end(); ++iter) {
1211 // Attach autocomplete listener to enable selecting alternate logins. 1242 // Attach autocomplete listener to enable selecting alternate logins.
1212 blink::WebInputElement username_element, password_element; 1243 blink::WebInputElement username_element, password_element;
1213 1244
1245 base::string16 username_field_name = FieldName(
1246 form_data.username_field, ambiguous_or_no_name_and_id_attribute);
1247 base::string16 password_field_name = FieldName(
1248 form_data.password_field, ambiguous_or_no_name_and_id_attribute);
1249
1214 // Check whether the password form has a username input field. 1250 // Check whether the password form has a username input field.
1215 bool form_contains_fillable_username_field = 1251 if (!username_field_name.empty()) {
1216 FillDataContainsFillableUsername(form_data); 1252 username_element = (*iter)[username_field_name];
1217 if (form_contains_fillable_username_field) {
1218 username_element =
1219 (*iter)[form_data.username_field.name];
1220 } 1253 }
1221 1254
1222 // No password field, bail out. 1255 // No password field, bail out.
1223 if (form_data.password_field.name.empty()) 1256 if (password_field_name.empty())
1224 break; 1257 break;
1225 1258
1226 // We might have already filled this form if there are two <form> elements 1259 // We might have already filled this form if there are two <form> elements
1227 // with identical markup. 1260 // with identical markup.
1228 if (login_to_password_info_.find(username_element) != 1261 if (login_to_password_info_.find(username_element) !=
1229 login_to_password_info_.end()) 1262 login_to_password_info_.end())
1230 continue; 1263 continue;
1231 1264
1232 // Get pointer to password element. (We currently only support single 1265 // Get pointer to password element. (We currently only support single
1233 // password forms). 1266 // password forms).
1234 password_element = (*iter)[form_data.password_field.name]; 1267 password_element = (*iter)[password_field_name];
1235 1268
1236 // If wait_for_username is true, we don't want to initially fill the form 1269 // If wait_for_username is true, we don't want to initially fill the form
1237 // until the user types in a valid username. 1270 // until the user types in a valid username.
1238 if (!form_data.wait_for_username) { 1271 if (!form_data.wait_for_username) {
1239 FillFormOnPasswordReceived( 1272 FillFormOnPasswordReceived(
1240 form_data, 1273 form_data,
1241 username_element, 1274 username_element,
1242 password_element, 1275 password_element,
1243 &nonscript_modified_values_, 1276 &nonscript_modified_values_,
1244 base::Bind(&PasswordValueGatekeeper::RegisterElement, 1277 base::Bind(&PasswordValueGatekeeper::RegisterElement,
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
1439 void PasswordAutofillAgent::LegacyPasswordAutofillAgent::DidStopLoading() { 1472 void PasswordAutofillAgent::LegacyPasswordAutofillAgent::DidStopLoading() {
1440 agent_->DidStopLoading(); 1473 agent_->DidStopLoading();
1441 } 1474 }
1442 1475
1443 void PasswordAutofillAgent::LegacyPasswordAutofillAgent:: 1476 void PasswordAutofillAgent::LegacyPasswordAutofillAgent::
1444 DidStartProvisionalLoad(blink::WebLocalFrame* navigated_frame) { 1477 DidStartProvisionalLoad(blink::WebLocalFrame* navigated_frame) {
1445 agent_->LegacyDidStartProvisionalLoad(navigated_frame); 1478 agent_->LegacyDidStartProvisionalLoad(navigated_frame);
1446 } 1479 }
1447 1480
1448 } // namespace autofill 1481 } // namespace autofill
OLDNEW
« no previous file with comments | « chrome/test/data/password/ambiguous_password_form.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698