OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |