Chromium Code Reviews| 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 |