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

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

Issue 614023002: [Password manager] Relplace the FormFieldData vector from autofill::FormData with named fields… (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Incorporated Vaclav's review comments. Created 6 years, 1 month 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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 // necessary form elements. To avoid having to look these up again when we want 55 // necessary form elements. To avoid having to look these up again when we want
56 // to fill the form, the FindFormElements function stores the pointers 56 // to fill the form, the FindFormElements function stores the pointers
57 // in a FormElements* result, referenced to ensure they are safe to use. 57 // in a FormElements* result, referenced to ensure they are safe to use.
58 struct FormElements { 58 struct FormElements {
59 blink::WebFormElement form_element; 59 blink::WebFormElement form_element;
60 FormInputElementMap input_elements; 60 FormInputElementMap input_elements;
61 }; 61 };
62 62
63 typedef std::vector<FormElements*> FormElementsList; 63 typedef std::vector<FormElements*> FormElementsList;
64 64
65 enum FieldType { PASSWORD_FIELD, TEXT_FIELD };
66
67 // Utility function to find the unique entry of the |form_element| for the
68 // specified input |field_name|. On successful find, adds it to |result|
69 // and returns |true|. Otherwise clears the references from each
70 // |HTMLInputElement| from |result| and returns |false|.
71 bool AddInputElementToResultWithMatchingFieldName(
Ilya Sherman 2014/11/05 20:40:00 When moving the code as I've recommended in my pre
Pritam Nikam 2014/11/06 06:31:59 Done. Added a follow-up CL: https://codereview.ch
72 blink::WebFormElement* form_element,
73 const base::string16& field_name,
74 FormElements* result,
75 FieldType field_type) {
76 blink::WebVector<blink::WebNode> temp_elements;
77 bool found_exactly_one_input = false;
78 // Get the list of input elements from |form_element| for matching
79 // |field_name| and appends uniqe match to |result|.
80 form_element->getNamedElements(field_name, temp_elements);
81 for (size_t i = 0; i < temp_elements.size(); ++i) {
82 if (temp_elements[i].to<blink::WebElement>().hasHTMLTagName("input")) {
83 // Check for a non-unique match.
84 if (found_exactly_one_input) {
85 found_exactly_one_input = false;
86 break;
87 }
88
89 found_exactly_one_input = true;
90
91 // Only fill saved passwords into password fields and usernames into
92 // text fields.
93 blink::WebInputElement input_element =
94 temp_elements[i].to<blink::WebInputElement>();
95 if (input_element.isPasswordField() != (field_type == PASSWORD_FIELD))
96 continue;
97
98 // This |input_element| matched, add it to our temporary |result|. It's
99 // possible there are multiple matches, but for purposes of identifying
100 // the form one suffices and if some function needs to deal with multiple
101 // matching |input_elements| it can get at them through the
102 // |FormElement*|. Note: This assignment adds a reference to the
103 // |HTMLInputElement|.
104 result->input_elements[field_name] = input_element;
105 }
106 }
107
108 // A required |input_element| was not found. This is not the right form. Make
109 // sure no |input_elements| from a partially matched form in this iteration
110 // remain in the |result| set. Note: clear will remove a reference from each
111 // |HTMLInputElement|.
112 if (!found_exactly_one_input || (result->input_elements.size() == 0)) {
vabr (Chromium) 2014/11/05 12:34:55 optional nit: This certainly works, although you c
113 result->input_elements.clear();
114 return false;
115 }
116
117 return true;
118 }
119
65 // Helper to search the given form element for the specified input elements 120 // Helper to search the given form element for the specified input elements
66 // in |data|, and add results to |result|. 121 // in |data|, and add results to |result|.
67 static bool FindFormInputElements(blink::WebFormElement* fe, 122 bool FindFormInputElements(blink::WebFormElement* form_element,
68 const FormData& data, 123 const PasswordFormFillData& data,
69 FormElements* result) { 124 FormElements* result) {
70 const bool username_is_present = !data.fields[0].name.empty(); 125 return AddInputElementToResultWithMatchingFieldName(
71 126 form_element, data.password_field.name, result, PASSWORD_FIELD) &&
72 // Loop through the list of elements we need to find on the form in order to 127 (data.username_field.name.empty() ||
73 // autofill it. If we don't find any one of them, abort processing this 128 AddInputElementToResultWithMatchingFieldName(
74 // form; it can't be the right one. 129 form_element, data.username_field.name, result, TEXT_FIELD));
75 // First field is the username, skip it if not present.
76 for (size_t j = (username_is_present ? 0 : 1); j < data.fields.size(); ++j) {
77 blink::WebVector<blink::WebNode> temp_elements;
78 fe->getNamedElements(data.fields[j].name, temp_elements);
79
80 // Match the first input element, if any.
81 // |getNamedElements| may return non-input elements where the names match,
82 // so the results are filtered for input elements.
83 // If more than one match is made, then we have ambiguity (due to misuse
84 // of "name" attribute) so is it considered not found.
85 bool found_input = false;
86 for (size_t i = 0; i < temp_elements.size(); ++i) {
87 if (temp_elements[i].to<blink::WebElement>().hasHTMLTagName("input")) {
88 // Check for a non-unique match.
89 if (found_input) {
90 found_input = false;
91 break;
92 }
93
94 // Only fill saved passwords into password fields and usernames into
95 // text fields.
96 blink::WebInputElement input_element =
97 temp_elements[i].to<blink::WebInputElement>();
98 if (input_element.isPasswordField() !=
99 (data.fields[j].form_control_type == "password"))
100 continue;
101
102 // This element matched, add it to our temporary result. It's possible
103 // there are multiple matches, but for purposes of identifying the form
104 // one suffices and if some function needs to deal with multiple
105 // matching elements it can get at them through the FormElement*.
106 // Note: This assignment adds a reference to the InputElement.
107 result->input_elements[data.fields[j].name] = input_element;
108 found_input = true;
109 }
110 }
111
112 // A required element was not found. This is not the right form.
113 // Make sure no input elements from a partially matched form in this
114 // iteration remain in the result set.
115 // Note: clear will remove a reference from each InputElement.
116 if (!found_input) {
117 result->input_elements.clear();
118 return false;
119 }
120 }
121 return true;
122 } 130 }
123 131
124 // Helper to locate form elements identified by |data|. 132 // Helper to locate form elements identified by |data|.
125 void FindFormElements(blink::WebView* view, 133 void FindFormElements(blink::WebView* view,
126 const FormData& data, 134 const PasswordFormFillData& data,
127 FormElementsList* results) { 135 FormElementsList* results) {
128 DCHECK(view); 136 DCHECK(view);
129 DCHECK(results); 137 DCHECK(results);
130 blink::WebFrame* main_frame = view->mainFrame(); 138 blink::WebFrame* main_frame = view->mainFrame();
131 if (!main_frame) 139 if (!main_frame)
132 return; 140 return;
133 141
134 GURL::Replacements rep; 142 GURL::Replacements rep;
135 rep.ClearQuery(); 143 rep.ClearQuery();
136 rep.ClearRef(); 144 rep.ClearRef();
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 // Log a message including the name, method and action of |form|. 238 // Log a message including the name, method and action of |form|.
231 void LogHTMLForm(SavePasswordProgressLogger* logger, 239 void LogHTMLForm(SavePasswordProgressLogger* logger,
232 SavePasswordProgressLogger::StringID message_id, 240 SavePasswordProgressLogger::StringID message_id,
233 const blink::WebFormElement& form) { 241 const blink::WebFormElement& form) {
234 logger->LogHTMLForm(message_id, 242 logger->LogHTMLForm(message_id,
235 form.name().utf8(), 243 form.name().utf8(),
236 GURL(form.action().utf8())); 244 GURL(form.action().utf8()));
237 } 245 }
238 246
239 bool FillDataContainsUsername(const PasswordFormFillData& fill_data) { 247 bool FillDataContainsUsername(const PasswordFormFillData& fill_data) {
240 return !fill_data.basic_data.fields[0].name.empty(); 248 return !fill_data.username_field.name.empty();
241 } 249 }
242 250
243 // This function attempts to fill |suggestions| and |realms| form |fill_data| 251 // This function attempts to fill |suggestions| and |realms| form |fill_data|
244 // based on |current_username|. Returns true when |suggestions| gets filled 252 // based on |current_username|. Returns true when |suggestions| gets filled
245 // from |fill_data.other_possible_usernames|, else returns false. 253 // from |fill_data.other_possible_usernames|, else returns false.
246 bool GetSuggestions(const PasswordFormFillData& fill_data, 254 bool GetSuggestions(const PasswordFormFillData& fill_data,
247 const base::string16& current_username, 255 const base::string16& current_username,
248 std::vector<base::string16>* suggestions, 256 std::vector<base::string16>* suggestions,
249 std::vector<base::string16>* realms, 257 std::vector<base::string16>* realms,
250 bool show_all) { 258 bool show_all) {
251 bool other_possible_username_shown = false; 259 bool other_possible_username_shown = false;
252 if (show_all || 260 if (show_all ||
253 StartsWith( 261 StartsWith(fill_data.username_field.value, current_username, false)) {
254 fill_data.basic_data.fields[0].value, current_username, false)) { 262 suggestions->push_back(fill_data.username_field.value);
255 suggestions->push_back(fill_data.basic_data.fields[0].value);
256 realms->push_back(base::UTF8ToUTF16(fill_data.preferred_realm)); 263 realms->push_back(base::UTF8ToUTF16(fill_data.preferred_realm));
257 } 264 }
258 265
259 for (PasswordFormFillData::LoginCollection::const_iterator iter = 266 for (PasswordFormFillData::LoginCollection::const_iterator iter =
260 fill_data.additional_logins.begin(); 267 fill_data.additional_logins.begin();
261 iter != fill_data.additional_logins.end(); 268 iter != fill_data.additional_logins.end();
262 ++iter) { 269 ++iter) {
263 if (show_all || StartsWith(iter->first, current_username, false)) { 270 if (show_all || StartsWith(iter->first, current_username, false)) {
264 suggestions->push_back(iter->first); 271 suggestions->push_back(iter->first);
265 realms->push_back(base::UTF8ToUTF16(iter->second.realm)); 272 realms->push_back(base::UTF8ToUTF16(iter->second.realm));
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 base::string16 current_username; 308 base::string16 current_username;
302 if (!username_element->isNull()) { 309 if (!username_element->isNull()) {
303 current_username = username_element->value(); 310 current_username = username_element->value();
304 } 311 }
305 312
306 // username and password will contain the match found if any. 313 // username and password will contain the match found if any.
307 base::string16 username; 314 base::string16 username;
308 base::string16 password; 315 base::string16 password;
309 316
310 // Look for any suitable matches to current field text. 317 // Look for any suitable matches to current field text.
311 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, 318 if (DoUsernamesMatch(fill_data.username_field.value, current_username,
312 current_username,
313 exact_username_match)) { 319 exact_username_match)) {
314 username = fill_data.basic_data.fields[0].value; 320 username = fill_data.username_field.value;
315 password = fill_data.basic_data.fields[1].value; 321 password = fill_data.password_field.value;
316 } else { 322 } else {
317 // Scan additional logins for a match. 323 // Scan additional logins for a match.
318 PasswordFormFillData::LoginCollection::const_iterator iter; 324 PasswordFormFillData::LoginCollection::const_iterator iter;
319 for (iter = fill_data.additional_logins.begin(); 325 for (iter = fill_data.additional_logins.begin();
320 iter != fill_data.additional_logins.end(); 326 iter != fill_data.additional_logins.end();
321 ++iter) { 327 ++iter) {
322 if (DoUsernamesMatch( 328 if (DoUsernamesMatch(
323 iter->first, current_username, exact_username_match)) { 329 iter->first, current_username, exact_username_match)) {
324 username = iter->first; 330 username = iter->first;
325 password = iter->second.password; 331 password = iter->second.password;
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 // If we can't modify the password, don't try to set the username 411 // If we can't modify the password, don't try to set the username
406 if (!IsElementAutocompletable(password_element)) 412 if (!IsElementAutocompletable(password_element))
407 return false; 413 return false;
408 414
409 // Try to set the username to the preferred name, but only if the field 415 // Try to set the username to the preferred name, but only if the field
410 // can be set and isn't prefilled. 416 // can be set and isn't prefilled.
411 if (form_contains_username_field && 417 if (form_contains_username_field &&
412 IsElementAutocompletable(username_element) && 418 IsElementAutocompletable(username_element) &&
413 username_element.value().isEmpty()) { 419 username_element.value().isEmpty()) {
414 // TODO(tkent): Check maxlength and pattern. 420 // TODO(tkent): Check maxlength and pattern.
415 username_element.setValue(fill_data.basic_data.fields[0].value, true); 421 username_element.setValue(fill_data.username_field.value, true);
416 } 422 }
417 423
418 // Fill if we have an exact match for the username. Note that this sets 424 // Fill if we have an exact match for the username. Note that this sets
419 // username to autofilled. 425 // username to autofilled.
420 return FillUserNameAndPassword(&username_element, 426 return FillUserNameAndPassword(&username_element,
421 &password_element, 427 &password_element,
422 fill_data, 428 fill_data,
423 true /* exact_username_match */, 429 true /* exact_username_match */,
424 false /* set_selection */, 430 false /* set_selection */,
425 registration_callback); 431 registration_callback);
(...skipping 593 matching lines...) Expand 10 before | Expand all | Expand 10 after
1019 const PasswordFormFillData& form_data) { 1025 const PasswordFormFillData& form_data) {
1020 if (usernames_usage_ == NOTHING_TO_AUTOFILL) { 1026 if (usernames_usage_ == NOTHING_TO_AUTOFILL) {
1021 if (form_data.other_possible_usernames.size()) 1027 if (form_data.other_possible_usernames.size())
1022 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT; 1028 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT;
1023 else if (usernames_usage_ == NOTHING_TO_AUTOFILL) 1029 else if (usernames_usage_ == NOTHING_TO_AUTOFILL)
1024 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; 1030 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT;
1025 } 1031 }
1026 1032
1027 FormElementsList forms; 1033 FormElementsList forms;
1028 // We own the FormElements* in forms. 1034 // We own the FormElements* in forms.
1029 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); 1035 FindFormElements(render_view()->GetWebView(), form_data, &forms);
1030 FormElementsList::iterator iter; 1036 FormElementsList::iterator iter;
1031 for (iter = forms.begin(); iter != forms.end(); ++iter) { 1037 for (iter = forms.begin(); iter != forms.end(); ++iter) {
1032 scoped_ptr<FormElements> form_elements(*iter); 1038 scoped_ptr<FormElements> form_elements(*iter);
1033 1039
1034 // Attach autocomplete listener to enable selecting alternate logins. 1040 // Attach autocomplete listener to enable selecting alternate logins.
1035 blink::WebInputElement username_element, password_element; 1041 blink::WebInputElement username_element, password_element;
1036 1042
1037 // Check whether the password form has a username input field. 1043 // Check whether the password form has a username input field.
1038 bool form_contains_username_field = FillDataContainsUsername(form_data); 1044 bool form_contains_username_field = FillDataContainsUsername(form_data);
1039 if (form_contains_username_field) { 1045 if (form_contains_username_field) {
1040 username_element = 1046 username_element =
1041 form_elements->input_elements[form_data.basic_data.fields[0].name]; 1047 form_elements->input_elements[form_data.username_field.name];
1042 } 1048 }
1043 1049
1044 // No password field, bail out. 1050 // No password field, bail out.
1045 if (form_data.basic_data.fields[1].name.empty()) 1051 if (form_data.password_field.name.empty())
1046 break; 1052 break;
1047 1053
1048 // Get pointer to password element. (We currently only support single 1054 // Get pointer to password element. (We currently only support single
1049 // password forms). 1055 // password forms).
1050 password_element = 1056 password_element =
1051 form_elements->input_elements[form_data.basic_data.fields[1].name]; 1057 form_elements->input_elements[form_data.password_field.name];
1052 1058
1053 // If wait_for_username is true, we don't want to initially fill the form 1059 // If wait_for_username is true, we don't want to initially fill the form
1054 // until the user types in a valid username. 1060 // until the user types in a valid username.
1055 if (!form_data.wait_for_username && 1061 if (!form_data.wait_for_username &&
1056 FillFormOnPasswordRecieved( 1062 FillFormOnPasswordRecieved(
1057 form_data, 1063 form_data,
1058 username_element, 1064 username_element,
1059 password_element, 1065 password_element,
1060 base::Bind(&PasswordValueGatekeeper::RegisterElement, 1066 base::Bind(&PasswordValueGatekeeper::RegisterElement,
1061 base::Unretained(&gatekeeper_)))) { 1067 base::Unretained(&gatekeeper_)))) {
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
1243 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form)); 1249 scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
1244 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD && 1250 if (!password_form || (restriction == RESTRICTION_NON_EMPTY_PASSWORD &&
1245 password_form->password_value.empty() && 1251 password_form->password_value.empty() &&
1246 password_form->new_password_value.empty())) { 1252 password_form->new_password_value.empty())) {
1247 return; 1253 return;
1248 } 1254 }
1249 provisionally_saved_forms_[frame].reset(password_form.release()); 1255 provisionally_saved_forms_[frame].reset(password_form.release());
1250 } 1256 }
1251 1257
1252 } // namespace autofill 1258 } // namespace autofill
OLDNEW
« no previous file with comments | « components/autofill/content/common/autofill_messages.h ('k') | components/autofill/core/common/autofill_data_validation.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698