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

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: Code restructured. Created 5 years, 4 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/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 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 // The size above which we stop triggering autocomplete. 48 // The size above which we stop triggering autocomplete.
49 static const size_t kMaximumTextSizeForAutocomplete = 1000; 49 static const size_t kMaximumTextSizeForAutocomplete = 1000;
50 50
51 // Experiment information 51 // Experiment information
52 const char kFillOnAccountSelectFieldTrialName[] = "FillOnAccountSelect"; 52 const char kFillOnAccountSelectFieldTrialName[] = "FillOnAccountSelect";
53 const char kFillOnAccountSelectFieldTrialEnabledWithHighlightGroup[] = 53 const char kFillOnAccountSelectFieldTrialEnabledWithHighlightGroup[] =
54 "EnableWithHighlight"; 54 "EnableWithHighlight";
55 const char kFillOnAccountSelectFieldTrialEnabledWithNoHighlightGroup[] = 55 const char kFillOnAccountSelectFieldTrialEnabledWithNoHighlightGroup[] =
56 "EnableWithNoHighlight"; 56 "EnableWithNoHighlight";
57 57
58 const char kUsernameField[] = "anonymous_username";
59 const char kPasswordField[] = "anonymous_password";
60 const char kInputTypePassword[] = "password";
vabr (Chromium) 2015/08/26 09:36:01 nit: You can just inline this constant in the help
Pritam Nikam 2015/08/26 13:25:41 Done.
61
58 // Maps element names to the actual elements to simplify form filling. 62 // Maps element names to the actual elements to simplify form filling.
59 typedef std::map<base::string16, blink::WebInputElement> FormInputElementMap; 63 typedef std::map<base::string16, blink::WebInputElement> FormInputElementMap;
60 64
61 // Use the shorter name when referencing SavePasswordProgressLogger::StringID 65 // Use the shorter name when referencing SavePasswordProgressLogger::StringID
62 // values to spare line breaks. The code provides enough context for that 66 // values to spare line breaks. The code provides enough context for that
63 // already. 67 // already.
64 typedef SavePasswordProgressLogger Logger; 68 typedef SavePasswordProgressLogger Logger;
65 69
66 // Utility struct for form lookup and autofill. When we parse the DOM to look up 70 // Utility struct for form lookup and autofill. When we parse the DOM to look up
67 // a form, in addition to action and origin URL's we have to compare all 71 // a form, in addition to action and origin URL's we have to compare all
68 // necessary form elements. To avoid having to look these up again when we want 72 // necessary form elements. To avoid having to look these up again when we want
69 // to fill the form, the FindFormElements function stores the pointers 73 // to fill the form, the FindFormElements function stores the pointers
70 // in a FormElements* result, referenced to ensure they are safe to use. 74 // in a FormElements* result, referenced to ensure they are safe to use.
71 struct FormElements { 75 struct FormElements {
72 blink::WebFormElement form_element; 76 blink::WebFormElement form_element;
73 FormInputElementMap input_elements; 77 FormInputElementMap input_elements;
74 }; 78 };
75 79
76 typedef std::vector<FormElements*> FormElementsList; 80 typedef std::vector<FormElements*> FormElementsList;
77 81
78 bool FillDataContainsFillableUsername(const PasswordFormFillData& fill_data) { 82 bool FillDataContainsFillableUsername(const PasswordFormFillData& fill_data) {
79 return !fill_data.username_field.name.empty() && 83 return !fill_data.username_field.name.empty() &&
80 (!fill_data.additional_logins.empty() || 84 (!fill_data.additional_logins.empty() ||
81 !fill_data.username_field.value.empty()); 85 !fill_data.username_field.value.empty());
82 } 86 }
83 87
88 // Returns true if password form has username and password fields with either
89 // same or no name and id attributes supplied.
90 bool PasswordFormWithAmbiguousOrNoNameAndIdAttributes(
91 const PasswordFormFillData& fill_data) {
92 return fill_data.username_field.name == fill_data.password_field.name;
93 }
94
95 bool IsPasswordField(const FormFieldData& field) {
96 return (field.form_control_type == kInputTypePassword);
97 }
98
99 // Returns the |field|'s autofillable name. If no name or id attribute is
100 // specified returns a dummy name.
101 base::string16 FieldName(const FormFieldData& field,
102 bool ambiguousOrNoNameAndIdAttribute) {
vabr (Chromium) 2015/08/26 09:36:00 style: Please use lowercase_with_underscore, not c
Pritam Nikam 2015/08/26 13:25:41 Done.
103 return ambiguousOrNoNameAndIdAttribute
104 ? IsPasswordField(field) ? base::ASCIIToUTF16(kPasswordField)
105 : base::ASCIIToUTF16(kUsernameField)
106 : field.name;
107 }
108
109 // Helper function to check whether the |element| qualifies for autofilling.
110 bool IsFillableInputElement(const blink::WebInputElement& element,
111 const FormFieldData& field) {
112 return element.hasHTMLTagName("input") && element.isTextField() &&
113 element.isPasswordField() == IsPasswordField(field);
114 }
115
116 // Helper function to get the password form control elements. On supplied with
vabr (Chromium) 2015/08/26 09:36:00 Please reformulate the second sentence. You probab
Pritam Nikam 2015/08/26 13:25:40 Done. valid -> non-empty and non-ambiguous.
117 // valid |field_name| gets the elements matching it.
vabr (Chromium) 2015/08/26 09:36:00 You should comment on what the bool argument is us
Pritam Nikam 2015/08/26 13:25:41 Done.
118 void GetFormControlElements(
119 blink::WebFormElement* form_element,
120 const base::string16& field_name,
121 bool ambiguousOrNoNameAndIdAttribute,
122 blink::WebVector<blink::WebFormControlElement>* elements) {
123 ambiguousOrNoNameAndIdAttribute
vabr (Chromium) 2015/08/26 09:36:00 Please use if-else here, do not create a value jus
Pritam Nikam 2015/08/26 13:25:41 Done.
124 ? form_element->getFormControlElements(*elements)
125 : form_element->getNamedElements(
126 field_name, (blink::WebVector<blink::WebNode>&)*elements);
127 }
128
84 // Utility function to find the unique entry of the |form_element| for the 129 // Utility function to find the unique entry of the |form_element| for the
85 // specified input |field|. On successful find, adds it to |result| and returns 130 // specified input |field|. On successful find, adds it to |result| and returns
86 // |true|. Otherwise clears the references from each |HTMLInputElement| from 131 // |true|. Otherwise clears the references from each |HTMLInputElement| from
87 // |result| and returns |false|. 132 // |result| and returns |false|.
88 bool FindFormInputElement(blink::WebFormElement* form_element, 133 bool FindFormInputElement(blink::WebFormElement* form_element,
89 const FormFieldData& field, 134 const FormFieldData& field,
135 bool ambiguousOrNoNameAndIdAttribute,
90 FormElements* result) { 136 FormElements* result) {
91 blink::WebVector<blink::WebNode> temp_elements; 137 blink::WebVector<blink::WebFormControlElement> input_elements;
92 form_element->getNamedElements(field.name, temp_elements); 138 GetFormControlElements(form_element, field.name,
139 ambiguousOrNoNameAndIdAttribute, &input_elements);
93 140
94 // Match the first input element, if any. 141 // Match the first input element, if any.
95 // |getNamedElements| may return non-input elements where the names match, 142 // |GetFormControlElements| may return non-input elements where the names
96 // so the results are filtered for input elements. 143 // match, so the results are filtered for input elements.
97 // If more than one match is made, then we have ambiguity (due to misuse
98 // of "name" attribute) so is it considered not found.
99 bool found_input = false; 144 bool found_input = false;
100 for (size_t i = 0; i < temp_elements.size(); ++i) { 145 base::string16 field_name = FieldName(field, ambiguousOrNoNameAndIdAttribute);
101 if (temp_elements[i].to<blink::WebElement>().hasHTMLTagName("input")) { 146 for (size_t i = 0; i < input_elements.size(); ++i) {
147 blink::WebInputElement input_element =
148 input_elements[i].to<blink::WebInputElement>();
149 if (IsFillableInputElement(input_element, field)) {
102 // Check for a non-unique match. 150 // Check for a non-unique match.
103 if (found_input) { 151 if (found_input) {
104 found_input = false; 152 found_input = false;
105 break; 153 break;
106 } 154 }
107 155
108 // Only fill saved passwords into password fields and usernames into
109 // text fields.
110 blink::WebInputElement input_element =
111 temp_elements[i].to<blink::WebInputElement>();
112 if (input_element.isPasswordField() !=
113 (field.form_control_type == "password"))
114 continue;
115
116 // This element matched, add it to our temporary result. It's possible 156 // This element matched, add it to our temporary result. It's possible
117 // there are multiple matches, but for purposes of identifying the form 157 // there are multiple matches, but for purposes of identifying the form
118 // one suffices and if some function needs to deal with multiple 158 // one suffices and if some function needs to deal with multiple
119 // matching elements it can get at them through the FormElement*. 159 // matching elements it can get at them through the FormElement*.
120 // Note: This assignment adds a reference to the InputElement. 160 // Note: This assignment adds a reference to the InputElement.
121 result->input_elements[field.name] = input_element; 161 result->input_elements[field_name] = input_element;
122 found_input = true; 162 found_input = true;
123 } 163 }
124 } 164 }
125 165
126 // A required element was not found. This is not the right form. 166 // A required element was not found. This is not the right form.
127 // Make sure no input elements from a partially matched form in this 167 // Make sure no input elements from a partially matched form in this
128 // iteration remain in the result set. 168 // iteration remain in the result set.
129 // Note: clear will remove a reference from each InputElement. 169 // Note: clear will remove a reference from each InputElement.
130 if (!found_input) { 170 if (!found_input) {
131 result->input_elements.clear(); 171 result->input_elements.clear();
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 } 213 }
174 214
175 return group_name != 215 return group_name !=
176 kFillOnAccountSelectFieldTrialEnabledWithNoHighlightGroup; 216 kFillOnAccountSelectFieldTrialEnabledWithNoHighlightGroup;
177 } 217 }
178 218
179 // Helper to search the given form element for the specified input elements in 219 // Helper to search the given form element for the specified input elements in
180 // |data|, and add results to |result|. 220 // |data|, and add results to |result|.
181 bool FindFormInputElements(blink::WebFormElement* form_element, 221 bool FindFormInputElements(blink::WebFormElement* form_element,
182 const PasswordFormFillData& data, 222 const PasswordFormFillData& data,
223 bool ambiguousOrNoNameAndIdAttribute,
183 FormElements* result) { 224 FormElements* result) {
184 return FindFormInputElement(form_element, data.password_field, result) && 225 return FindFormInputElement(form_element, data.password_field,
185 (!FillDataContainsFillableUsername(data) || 226 ambiguousOrNoNameAndIdAttribute, result) &&
186 FindFormInputElement(form_element, data.username_field, result)); 227 (!(ambiguousOrNoNameAndIdAttribute ||
228 FillDataContainsFillableUsername(data)) ||
229 FindFormInputElement(form_element, data.username_field,
230 ambiguousOrNoNameAndIdAttribute, result));
187 } 231 }
188 232
189 // Helper to locate form elements identified by |data|. 233 // Helper to locate form elements identified by |data|.
190 void FindFormElements(content::RenderFrame* render_frame, 234 void FindFormElements(content::RenderFrame* render_frame,
191 const PasswordFormFillData& data, 235 const PasswordFormFillData& data,
236 bool ambiguousOrNoNameAndIdAttribute,
192 FormElementsList* results) { 237 FormElementsList* results) {
193 DCHECK(results); 238 DCHECK(results);
194 239
195 blink::WebDocument doc = render_frame->GetWebFrame()->document(); 240 blink::WebDocument doc = render_frame->GetWebFrame()->document();
196 if (!doc.isHTMLDocument()) 241 if (!doc.isHTMLDocument())
197 return; 242 return;
198 243
199 if (data.origin != GetCanonicalOriginForDocument(doc)) 244 if (data.origin != GetCanonicalOriginForDocument(doc))
200 return; 245 return;
201 246
202 blink::WebVector<blink::WebFormElement> forms; 247 blink::WebVector<blink::WebFormElement> forms;
203 doc.forms(forms); 248 doc.forms(forms);
204 249
205 for (size_t i = 0; i < forms.size(); ++i) { 250 for (size_t i = 0; i < forms.size(); ++i) {
206 blink::WebFormElement fe = forms[i]; 251 blink::WebFormElement fe = forms[i];
207 252
208 // Action URL must match. 253 // Action URL must match.
209 if (data.action != GetCanonicalActionForForm(fe)) 254 if (data.action != GetCanonicalActionForForm(fe))
210 continue; 255 continue;
211 256
212 scoped_ptr<FormElements> curr_elements(new FormElements); 257 scoped_ptr<FormElements> curr_elements(new FormElements);
213 if (!FindFormInputElements(&fe, data, curr_elements.get())) 258 if (!FindFormInputElements(&fe, data, ambiguousOrNoNameAndIdAttribute,
259 curr_elements.get())) {
214 continue; 260 continue;
261 }
215 262
216 // We found the right element. 263 // We found the right element.
217 // Note: this assignment adds a reference to |fe|. 264 // Note: this assignment adds a reference to |fe|.
218 curr_elements->form_element = fe; 265 curr_elements->form_element = fe;
219 results->push_back(curr_elements.release()); 266 results->push_back(curr_elements.release());
220 } 267 }
221 } 268 }
222 269
223 bool IsElementEditable(const blink::WebInputElement& element) { 270 bool IsElementEditable(const blink::WebInputElement& element) {
224 return element.isEnabled() && !element.isReadOnly(); 271 return element.isEnabled() && !element.isReadOnly();
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 while (cur_frame->parent()) { 508 while (cur_frame->parent()) {
462 cur_frame = cur_frame->parent(); 509 cur_frame = cur_frame->parent();
463 if (!bottom_frame_origin.equals(cur_frame->securityOrigin().toString())) 510 if (!bottom_frame_origin.equals(cur_frame->securityOrigin().toString()))
464 return false; 511 return false;
465 } 512 }
466 513
467 // If we can't modify the password, don't try to set the username 514 // If we can't modify the password, don't try to set the username
468 if (!IsElementAutocompletable(password_element)) 515 if (!IsElementAutocompletable(password_element))
469 return false; 516 return false;
470 517
471 bool form_contains_fillable_username_field = 518 bool ambiguousOrNoNameAndIdAttribute =
472 FillDataContainsFillableUsername(fill_data); 519 PasswordFormWithAmbiguousOrNoNameAndIdAttributes(fill_data);
520 base::string16 username_field_name =
521 FieldName(fill_data.username_field, ambiguousOrNoNameAndIdAttribute);
473 // If the form contains an autocompletable username field, try to set the 522 // If the form contains an autocompletable username field, try to set the
474 // username to the preferred name, but only if: 523 // username to the preferred name, but only if:
475 // (a) The fill-on-account-select flag is not set, and 524 // (a) The fill-on-account-select flag is not set, and
476 // (b) The username element isn't prefilled 525 // (b) The username element isn't prefilled
477 // 526 //
478 // If (a) is false, then just mark the username element as autofilled if the 527 // If (a) is false, then just mark the username element as autofilled if the
479 // user is not in the "no highlighting" group and return so the fill step is 528 // user is not in the "no highlighting" group and return so the fill step is
480 // skipped. 529 // skipped.
481 // 530 //
482 // If there is no autocompletable username field, and (a) is false, then the 531 // If there is no autocompletable username field, and (a) is false, then the
483 // username element cannot be autofilled, but the user should still be able to 532 // username element cannot be autofilled, but the user should still be able to
484 // select to fill the password element, so the password element must be marked 533 // select to fill the password element, so the password element must be marked
485 // as autofilled and the fill step should also be skipped if the user is not 534 // as autofilled and the fill step should also be skipped if the user is not
486 // in the "no highlighting" group. 535 // in the "no highlighting" group.
487 // 536 //
488 // In all other cases, do nothing. 537 // In all other cases, do nothing.
489 bool form_has_fillable_username = form_contains_fillable_username_field && 538 bool form_has_fillable_username = !username_field_name.empty() &&
490 IsElementAutocompletable(username_element); 539 IsElementAutocompletable(username_element);
491 540
492 if (ShouldFillOnAccountSelect()) { 541 if (ShouldFillOnAccountSelect()) {
493 if (!ShouldHighlightFields()) { 542 if (!ShouldHighlightFields()) {
494 return false; 543 return false;
495 } 544 }
496 545
497 if (form_has_fillable_username) { 546 if (form_has_fillable_username) {
498 username_element.setAutofilled(true); 547 username_element.setAutofilled(true);
499 } else if (username_element.isNull() || 548 } else if (username_element.isNull() ||
(...skipping 657 matching lines...) Expand 10 before | Expand all | Expand 10 after
1157 } 1206 }
1158 1207
1159 // This is a new navigation, so require a new user gesture before filling in 1208 // This is a new navigation, so require a new user gesture before filling in
1160 // passwords. 1209 // passwords.
1161 gatekeeper_.Reset(); 1210 gatekeeper_.Reset();
1162 } 1211 }
1163 1212
1164 void PasswordAutofillAgent::OnFillPasswordForm( 1213 void PasswordAutofillAgent::OnFillPasswordForm(
1165 int key, 1214 int key,
1166 const PasswordFormFillData& form_data) { 1215 const PasswordFormFillData& form_data) {
1167 1216 bool ambiguousOrNoNameAndIdAttribute =
1217 PasswordFormWithAmbiguousOrNoNameAndIdAttributes(form_data);
1168 FormElementsList forms; 1218 FormElementsList forms;
1169 // We own the FormElements* in forms. 1219 // We own the FormElements* in forms.
1170 FindFormElements(render_frame(), form_data, &forms); 1220 FindFormElements(render_frame(), form_data, ambiguousOrNoNameAndIdAttribute,
1221 &forms);
1171 FormElementsList::iterator iter; 1222 FormElementsList::iterator iter;
1172 for (iter = forms.begin(); iter != forms.end(); ++iter) { 1223 for (iter = forms.begin(); iter != forms.end(); ++iter) {
1173 scoped_ptr<FormElements> form_elements(*iter); 1224 scoped_ptr<FormElements> form_elements(*iter);
1174 1225
1226 base::string16 username_field_name =
1227 FieldName(form_data.username_field, ambiguousOrNoNameAndIdAttribute);
1228 base::string16 password_field_name =
1229 FieldName(form_data.password_field, ambiguousOrNoNameAndIdAttribute);
1230
1175 // Attach autocomplete listener to enable selecting alternate logins. 1231 // Attach autocomplete listener to enable selecting alternate logins.
1176 blink::WebInputElement username_element, password_element; 1232 blink::WebInputElement username_element, password_element;
1177 1233
1178 // Check whether the password form has a username input field. 1234 // Check whether the password form has a username input field.
1179 bool form_contains_fillable_username_field = 1235 if (!username_field_name.empty()) {
1180 FillDataContainsFillableUsername(form_data); 1236 username_element = form_elements->input_elements[username_field_name];
1181 if (form_contains_fillable_username_field) {
1182 username_element =
1183 form_elements->input_elements[form_data.username_field.name];
1184 } 1237 }
1185 1238
1186 // No password field, bail out. 1239 // No password field, bail out.
1187 if (form_data.password_field.name.empty()) 1240 if (password_field_name.empty())
1188 break; 1241 break;
1189 1242
1190 // We might have already filled this form if there are two <form> elements 1243 // We might have already filled this form if there are two <form> elements
1191 // with identical markup. 1244 // with identical markup.
1192 if (login_to_password_info_.find(username_element) != 1245 if (login_to_password_info_.find(username_element) !=
1193 login_to_password_info_.end()) 1246 login_to_password_info_.end())
1194 continue; 1247 continue;
1195 1248
1196 // Get pointer to password element. (We currently only support single 1249 // Get pointer to password element. (We currently only support single
1197 // password forms). 1250 // password forms).
1198 password_element = 1251 password_element = form_elements->input_elements[password_field_name];
1199 form_elements->input_elements[form_data.password_field.name];
1200 1252
1201 // If wait_for_username is true, we don't want to initially fill the form 1253 // If wait_for_username is true, we don't want to initially fill the form
1202 // until the user types in a valid username. 1254 // until the user types in a valid username.
1203 if (!form_data.wait_for_username) { 1255 if (!form_data.wait_for_username) {
1204 FillFormOnPasswordReceived( 1256 FillFormOnPasswordReceived(
1205 form_data, 1257 form_data,
1206 username_element, 1258 username_element,
1207 password_element, 1259 password_element,
1208 &nonscript_modified_values_, 1260 &nonscript_modified_values_,
1209 base::Bind(&PasswordValueGatekeeper::RegisterElement, 1261 base::Bind(&PasswordValueGatekeeper::RegisterElement,
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
1395 void PasswordAutofillAgent::LegacyPasswordAutofillAgent::DidStopLoading() { 1447 void PasswordAutofillAgent::LegacyPasswordAutofillAgent::DidStopLoading() {
1396 agent_->DidStopLoading(); 1448 agent_->DidStopLoading();
1397 } 1449 }
1398 1450
1399 void PasswordAutofillAgent::LegacyPasswordAutofillAgent:: 1451 void PasswordAutofillAgent::LegacyPasswordAutofillAgent::
1400 DidStartProvisionalLoad(blink::WebLocalFrame* navigated_frame) { 1452 DidStartProvisionalLoad(blink::WebLocalFrame* navigated_frame) {
1401 agent_->LegacyDidStartProvisionalLoad(navigated_frame); 1453 agent_->LegacyDidStartProvisionalLoad(navigated_frame);
1402 } 1454 }
1403 1455
1404 } // namespace autofill 1456 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698