| 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_form_conversion_utils.h" | 5 #include "components/autofill/content/renderer/password_form_conversion_utils.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <string> | 10 #include <string> |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 const WebInputElement& username_element, | 147 const WebInputElement& username_element, |
| 148 std::vector<base::string16>* other_possible_usernames) { | 148 std::vector<base::string16>* other_possible_usernames) { |
| 149 other_possible_usernames->erase( | 149 other_possible_usernames->erase( |
| 150 std::remove(other_possible_usernames->begin(), | 150 std::remove(other_possible_usernames->begin(), |
| 151 other_possible_usernames->end(), | 151 other_possible_usernames->end(), |
| 152 username_element.value()), | 152 username_element.value()), |
| 153 other_possible_usernames->end()); | 153 other_possible_usernames->end()); |
| 154 } | 154 } |
| 155 | 155 |
| 156 // Helper to determine which password is the main (current) one, and which is | 156 // Helper to determine which password is the main (current) one, and which is |
| 157 // the new password (e.g., on a sign-up or change password form), if any. | 157 // the new password (e.g., on a sign-up or change password form), if any. If the |
| 158 // new password is found and there is another password field with the same user |
| 159 // input, the function also sets |confirmation_password| to this field. |
| 158 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, | 160 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, |
| 159 WebInputElement* current_password, | 161 WebInputElement* current_password, |
| 160 WebInputElement* new_password) { | 162 WebInputElement* new_password, |
| 163 WebInputElement* confirmation_password) { |
| 161 DCHECK(current_password && current_password->isNull()); | 164 DCHECK(current_password && current_password->isNull()); |
| 162 DCHECK(new_password && new_password->isNull()); | 165 DCHECK(new_password && new_password->isNull()); |
| 166 DCHECK(confirmation_password && confirmation_password->isNull()); |
| 163 | 167 |
| 164 // First, look for elements marked with either autocomplete='current-password' | 168 // First, look for elements marked with either autocomplete='current-password' |
| 165 // or 'new-password' -- if we find any, take the hint, and treat the first of | 169 // or 'new-password' -- if we find any, take the hint, and treat the first of |
| 166 // each kind as the element we are looking for. | 170 // each kind as the element we are looking for. |
| 167 for (const WebInputElement& it : passwords) { | 171 for (const WebInputElement& it : passwords) { |
| 168 if (HasAutocompleteAttributeValue(it, kAutocompleteCurrentPassword) && | 172 if (HasAutocompleteAttributeValue(it, kAutocompleteCurrentPassword) && |
| 169 current_password->isNull()) { | 173 current_password->isNull()) { |
| 170 *current_password = it; | 174 *current_password = it; |
| 171 } else if (HasAutocompleteAttributeValue(it, kAutocompleteNewPassword) && | 175 } else if (HasAutocompleteAttributeValue(it, kAutocompleteNewPassword) && |
| 172 new_password->isNull()) { | 176 new_password->isNull()) { |
| 173 *new_password = it; | 177 *new_password = it; |
| 178 } else if (!new_password->isNull() && |
| 179 (new_password->value() == it.value())) { |
| 180 *confirmation_password = it; |
| 174 } | 181 } |
| 175 } | 182 } |
| 176 | 183 |
| 177 // If we have seen an element with either of autocomplete attributes above, | 184 // If we have seen an element with either of autocomplete attributes above, |
| 178 // take that as a signal that the page author must have intentionally left the | 185 // take that as a signal that the page author must have intentionally left the |
| 179 // rest of the password fields unmarked. Perhaps they are used for other | 186 // rest of the password fields unmarked. Perhaps they are used for other |
| 180 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we | 187 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we |
| 181 // normally do, and ignore the rest of the password fields. | 188 // normally do, and ignore the rest of the password fields. |
| 182 if (!current_password->isNull() || !new_password->isNull()) | 189 if (!current_password->isNull() || !new_password->isNull()) |
| 183 return true; | 190 return true; |
| 184 | 191 |
| 185 if (passwords.empty()) | 192 if (passwords.empty()) |
| 186 return false; | 193 return false; |
| 187 | 194 |
| 188 switch (passwords.size()) { | 195 switch (passwords.size()) { |
| 189 case 1: | 196 case 1: |
| 190 // Single password, easy. | 197 // Single password, easy. |
| 191 *current_password = passwords[0]; | 198 *current_password = passwords[0]; |
| 192 break; | 199 break; |
| 193 case 2: | 200 case 2: |
| 194 if (!passwords[0].value().isEmpty() && | 201 if (!passwords[0].value().isEmpty() && |
| 195 passwords[0].value() == passwords[1].value()) { | 202 passwords[0].value() == passwords[1].value()) { |
| 196 // Two identical non-empty passwords: assume we are seeing a new | 203 // Two identical non-empty passwords: assume we are seeing a new |
| 197 // password with a confirmation. This can be either a sign-up form or a | 204 // password with a confirmation. This can be either a sign-up form or a |
| 198 // password change form that does not ask for the old password. | 205 // password change form that does not ask for the old password. |
| 199 *new_password = passwords[0]; | 206 *new_password = passwords[0]; |
| 207 *confirmation_password = passwords[1]; |
| 200 } else { | 208 } else { |
| 201 // Assume first is old password, second is new (no choice but to guess). | 209 // Assume first is old password, second is new (no choice but to guess). |
| 202 // This case also includes empty passwords in order to allow filling of | 210 // This case also includes empty passwords in order to allow filling of |
| 203 // password change forms (that also could autofill for sign up form, but | 211 // password change forms (that also could autofill for sign up form, but |
| 204 // we can't do anything with this using only client side information). | 212 // we can't do anything with this using only client side information). |
| 205 *current_password = passwords[0]; | 213 *current_password = passwords[0]; |
| 206 *new_password = passwords[1]; | 214 *new_password = passwords[1]; |
| 207 } | 215 } |
| 208 break; | 216 break; |
| 209 default: | 217 default: |
| 210 if (!passwords[0].value().isEmpty() && | 218 if (!passwords[0].value().isEmpty() && |
| 211 passwords[0].value() == passwords[1].value() && | 219 passwords[0].value() == passwords[1].value() && |
| 212 passwords[0].value() == passwords[2].value()) { | 220 passwords[0].value() == passwords[2].value()) { |
| 213 // All three passwords are the same and non-empty? This does not make | 221 // All three passwords are the same and non-empty? This does not make |
| 214 // any sense, give up. | 222 // any sense, give up. |
| 215 return false; | 223 return false; |
| 216 } else if (passwords[1].value() == passwords[2].value()) { | 224 } else if (passwords[1].value() == passwords[2].value()) { |
| 217 // New password is the duplicated one, and comes second; or empty form | 225 // New password is the duplicated one, and comes second; or empty form |
| 218 // with 3 password fields, in which case we will assume this layout. | 226 // with 3 password fields, in which case we will assume this layout. |
| 219 *current_password = passwords[0]; | 227 *current_password = passwords[0]; |
| 220 *new_password = passwords[1]; | 228 *new_password = passwords[1]; |
| 229 *confirmation_password = passwords[2]; |
| 221 } else if (passwords[0].value() == passwords[1].value()) { | 230 } else if (passwords[0].value() == passwords[1].value()) { |
| 222 // It is strange that the new password comes first, but trust more which | 231 // It is strange that the new password comes first, but trust more which |
| 223 // fields are duplicated than the ordering of fields. Assume that | 232 // fields are duplicated than the ordering of fields. Assume that |
| 224 // any password fields after the new password contain sensitive | 233 // any password fields after the new password contain sensitive |
| 225 // information that isn't actually a password (security hint, SSN, etc.) | 234 // information that isn't actually a password (security hint, SSN, etc.) |
| 226 *new_password = passwords[0]; | 235 *new_password = passwords[0]; |
| 236 *confirmation_password = passwords[1]; |
| 227 } else { | 237 } else { |
| 228 // Three different passwords, or first and last match with middle | 238 // Three different passwords, or first and last match with middle |
| 229 // different. No idea which is which, so no luck. | 239 // different. No idea which is which, so no luck. |
| 230 return false; | 240 return false; |
| 231 } | 241 } |
| 232 } | 242 } |
| 233 return true; | 243 return true; |
| 234 } | 244 } |
| 235 | 245 |
| 236 // Checks the |form_predictions| map to see if there is a key associated with | 246 // Checks the |form_predictions| map to see if there is a key associated with |
| (...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 498 latest_input_element = *input_element; | 508 latest_input_element = *input_element; |
| 499 if (!input_element->value().isEmpty()) | 509 if (!input_element->value().isEmpty()) |
| 500 other_possible_usernames.push_back(input_element->value()); | 510 other_possible_usernames.push_back(input_element->value()); |
| 501 } | 511 } |
| 502 } | 512 } |
| 503 } | 513 } |
| 504 } | 514 } |
| 505 | 515 |
| 506 WebInputElement password; | 516 WebInputElement password; |
| 507 WebInputElement new_password; | 517 WebInputElement new_password; |
| 508 if (!LocateSpecificPasswords(passwords, &password, &new_password)) | 518 WebInputElement confirmation_password; |
| 519 if (!LocateSpecificPasswords(passwords, &password, &new_password, |
| 520 &confirmation_password)) |
| 509 return false; | 521 return false; |
| 510 | 522 |
| 511 DCHECK_EQ(passwords.size(), last_text_input_before_password.size()); | 523 DCHECK_EQ(passwords.size(), last_text_input_before_password.size()); |
| 512 if (username_element.isNull()) { | 524 if (username_element.isNull()) { |
| 513 if (!password.isNull()) | 525 if (!password.isNull()) |
| 514 username_element = last_text_input_before_password[password]; | 526 username_element = last_text_input_before_password[password]; |
| 515 if (username_element.isNull() && !new_password.isNull()) | 527 if (username_element.isNull() && !new_password.isNull()) |
| 516 username_element = last_text_input_before_password[new_password]; | 528 username_element = last_text_input_before_password[new_password]; |
| 517 if (!username_element.isNull()) | 529 if (!username_element.isNull()) |
| 518 ExcludeUsernameFromOtherUsernamesList(username_element, | 530 ExcludeUsernameFromOtherUsernamesList(username_element, |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 577 password_form->password_value = password_value; | 589 password_form->password_value = password_value; |
| 578 } | 590 } |
| 579 if (!new_password.isNull()) { | 591 if (!new_password.isNull()) { |
| 580 password_form->new_password_element = | 592 password_form->new_password_element = |
| 581 FieldName(new_password, "anonymous_new_password"); | 593 FieldName(new_password, "anonymous_new_password"); |
| 582 password_form->new_password_value = new_password.value(); | 594 password_form->new_password_value = new_password.value(); |
| 583 password_form->new_password_value_is_default = | 595 password_form->new_password_value_is_default = |
| 584 new_password.getAttribute("value") == new_password.value(); | 596 new_password.getAttribute("value") == new_password.value(); |
| 585 if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword)) | 597 if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword)) |
| 586 password_form->new_password_marked_by_site = true; | 598 password_form->new_password_marked_by_site = true; |
| 599 if (!confirmation_password.isNull()) { |
| 600 password_form->confirmation_password_element = |
| 601 FieldName(confirmation_password, "anonymous_confirmation_password"); |
| 602 } |
| 587 } | 603 } |
| 588 | 604 |
| 589 if (username_element.isNull()) { | 605 if (username_element.isNull()) { |
| 590 // To get a better idea on how password forms without a username field | 606 // To get a better idea on how password forms without a username field |
| 591 // look like, report the total number of text and password fields. | 607 // look like, report the total number of text and password fields. |
| 592 UMA_HISTOGRAM_COUNTS_100( | 608 UMA_HISTOGRAM_COUNTS_100( |
| 593 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount", | 609 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount", |
| 594 layout_sequence.size()); | 610 layout_sequence.size()); |
| 595 // For comparison, also report the number of password fields. | 611 // For comparison, also report the number of password fields. |
| 596 UMA_HISTOGRAM_COUNTS_100( | 612 UMA_HISTOGRAM_COUNTS_100( |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 706 bool HasAutocompleteAttributeValue(const blink::WebInputElement& element, | 722 bool HasAutocompleteAttributeValue(const blink::WebInputElement& element, |
| 707 const char* value_in_lowercase) { | 723 const char* value_in_lowercase) { |
| 708 base::string16 autocomplete_attribute(element.getAttribute("autocomplete")); | 724 base::string16 autocomplete_attribute(element.getAttribute("autocomplete")); |
| 709 std::vector<std::string> tokens = LowercaseAndTokenizeAttributeString( | 725 std::vector<std::string> tokens = LowercaseAndTokenizeAttributeString( |
| 710 base::UTF16ToUTF8(autocomplete_attribute)); | 726 base::UTF16ToUTF8(autocomplete_attribute)); |
| 711 | 727 |
| 712 return base::ContainsValue(tokens, value_in_lowercase); | 728 return base::ContainsValue(tokens, value_in_lowercase); |
| 713 } | 729 } |
| 714 | 730 |
| 715 } // namespace autofill | 731 } // namespace autofill |
| OLD | NEW |