| 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 <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/i18n/case_conversion.h" | 9 #include "base/i18n/case_conversion.h" |
| 10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 // LAYOUT_LOGIN_AND_SIGNUP is classified by NPN+P.*. This corresponds to a form | 51 // LAYOUT_LOGIN_AND_SIGNUP is classified by NPN+P.*. This corresponds to a form |
| 52 // which starts with a login section (NP) and continues with a sign-up section | 52 // which starts with a login section (NP) and continues with a sign-up section |
| 53 // (N+P.*). The aim is to distinguish such forms from change password-forms | 53 // (N+P.*). The aim is to distinguish such forms from change password-forms |
| 54 // (N*PPP?.*) and forms which use password fields to store private but | 54 // (N*PPP?.*) and forms which use password fields to store private but |
| 55 // non-password data (could look like, e.g., PN+P.*). | 55 // non-password data (could look like, e.g., PN+P.*). |
| 56 const char kLoginAndSignupRegex[] = | 56 const char kLoginAndSignupRegex[] = |
| 57 "NP" // Login section. | 57 "NP" // Login section. |
| 58 "N+P" // Sign-up section. | 58 "N+P" // Sign-up section. |
| 59 ".*"; // Anything beyond that. | 59 ".*"; // Anything beyond that. |
| 60 | 60 |
| 61 const char kAutocompleteUsername[] = "username"; |
| 62 const char kAutocompleteCurrentPassword[] = "current-password"; |
| 63 const char kAutocompleteNewPassword[] = "new-password"; |
| 64 |
| 61 struct LoginAndSignupLazyInstanceTraits | 65 struct LoginAndSignupLazyInstanceTraits |
| 62 : public base::DefaultLazyInstanceTraits<icu::RegexMatcher> { | 66 : public base::DefaultLazyInstanceTraits<icu::RegexMatcher> { |
| 63 static icu::RegexMatcher* New(void* instance) { | 67 static icu::RegexMatcher* New(void* instance) { |
| 64 const icu::UnicodeString icu_pattern(kLoginAndSignupRegex); | 68 const icu::UnicodeString icu_pattern(kLoginAndSignupRegex); |
| 65 | 69 |
| 66 UErrorCode status = U_ZERO_ERROR; | 70 UErrorCode status = U_ZERO_ERROR; |
| 67 // Use placement new to initialize the instance in the preallocated space. | 71 // Use placement new to initialize the instance in the preallocated space. |
| 68 // The "(instance)" is very important to force POD type initialization. | 72 // The "(instance)" is very important to force POD type initialization. |
| 69 scoped_ptr<icu::RegexMatcher> matcher(new (instance) icu::RegexMatcher( | 73 scoped_ptr<icu::RegexMatcher> matcher(new (instance) icu::RegexMatcher( |
| 70 icu_pattern, UREGEX_CASE_INSENSITIVE, status)); | 74 icu_pattern, UREGEX_CASE_INSENSITIVE, status)); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 WebInputElement* current_password, | 116 WebInputElement* current_password, |
| 113 WebInputElement* new_password) { | 117 WebInputElement* new_password) { |
| 114 DCHECK(current_password && current_password->isNull()); | 118 DCHECK(current_password && current_password->isNull()); |
| 115 DCHECK(new_password && new_password->isNull()); | 119 DCHECK(new_password && new_password->isNull()); |
| 116 | 120 |
| 117 // First, look for elements marked with either autocomplete='current-password' | 121 // First, look for elements marked with either autocomplete='current-password' |
| 118 // or 'new-password' -- if we find any, take the hint, and treat the first of | 122 // or 'new-password' -- if we find any, take the hint, and treat the first of |
| 119 // each kind as the element we are looking for. | 123 // each kind as the element we are looking for. |
| 120 for (std::vector<WebInputElement>::const_iterator it = passwords.begin(); | 124 for (std::vector<WebInputElement>::const_iterator it = passwords.begin(); |
| 121 it != passwords.end(); it++) { | 125 it != passwords.end(); it++) { |
| 122 if (HasAutocompleteAttributeValue(*it, "current-password") && | 126 if (HasAutocompleteAttributeValue(*it, kAutocompleteCurrentPassword) && |
| 123 current_password->isNull()) { | 127 current_password->isNull()) { |
| 124 *current_password = *it; | 128 *current_password = *it; |
| 125 } else if (HasAutocompleteAttributeValue(*it, "new-password") && | 129 } else if (HasAutocompleteAttributeValue(*it, kAutocompleteNewPassword) && |
| 126 new_password->isNull()) { | 130 new_password->isNull()) { |
| 127 *new_password = *it; | 131 *new_password = *it; |
| 128 } | 132 } |
| 129 } | 133 } |
| 130 | 134 |
| 131 // If we have seen an element with either of autocomplete attributes above, | 135 // If we have seen an element with either of autocomplete attributes above, |
| 132 // take that as a signal that the page author must have intentionally left the | 136 // take that as a signal that the page author must have intentionally left the |
| 133 // rest of the password fields unmarked. Perhaps they are used for other | 137 // rest of the password fields unmarked. Perhaps they are used for other |
| 134 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we | 138 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we |
| 135 // normally do, and ignore the rest of the password fields. | 139 // normally do, and ignore the rest of the password fields. |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 282 // keyboard and bypassing the password field value (see | 286 // keyboard and bypassing the password field value (see |
| 283 // http://crbug.com/475488). There is nothing Chrome can do to fill | 287 // http://crbug.com/475488). There is nothing Chrome can do to fill |
| 284 // passwords for now. Continue processing in case when the password field | 288 // passwords for now. Continue processing in case when the password field |
| 285 // was made readonly by JavaScript before submission. We can do this by | 289 // was made readonly by JavaScript before submission. We can do this by |
| 286 // checking whether password element was updated not from JavaScript. | 290 // checking whether password element was updated not from JavaScript. |
| 287 if (input_element->isPasswordField() && | 291 if (input_element->isPasswordField() && |
| 288 (!input_element->isReadOnly() || | 292 (!input_element->isReadOnly() || |
| 289 (nonscript_modified_values && | 293 (nonscript_modified_values && |
| 290 nonscript_modified_values->find(*input_element) != | 294 nonscript_modified_values->find(*input_element) != |
| 291 nonscript_modified_values->end()) || | 295 nonscript_modified_values->end()) || |
| 292 HasAutocompleteAttributeValue(*input_element, "current-password") || | 296 HasAutocompleteAttributeValue(*input_element, |
| 293 HasAutocompleteAttributeValue(*input_element, "new-password"))) { | 297 kAutocompleteCurrentPassword) || |
| 298 HasAutocompleteAttributeValue(*input_element, |
| 299 kAutocompleteNewPassword))) { |
| 294 passwords.push_back(*input_element); | 300 passwords.push_back(*input_element); |
| 295 // If we have not yet considered any element to be the username so far, | 301 // If we have not yet considered any element to be the username so far, |
| 296 // provisionally select the input element just before the first password | 302 // provisionally select the input element just before the first password |
| 297 // element to be the username. This choice will be overruled if we later | 303 // element to be the username. This choice will be overruled if we later |
| 298 // find an element with autocomplete='username'. | 304 // find an element with autocomplete='username'. |
| 299 if (username_element.isNull() && !latest_input_element.isNull()) { | 305 if (username_element.isNull() && !latest_input_element.isNull()) { |
| 300 username_element = latest_input_element; | 306 username_element = latest_input_element; |
| 301 // Remove the selected username from other_possible_usernames. | 307 // Remove the selected username from other_possible_usernames. |
| 302 if (!latest_input_element.value().isEmpty()) { | 308 if (!latest_input_element.value().isEmpty()) { |
| 303 DCHECK(!other_possible_usernames.empty()); | 309 DCHECK(!other_possible_usernames.empty()); |
| 304 DCHECK_EQ(base::string16(latest_input_element.value()), | 310 DCHECK_EQ(base::string16(latest_input_element.value()), |
| 305 other_possible_usernames.back()); | 311 other_possible_usernames.back()); |
| 306 other_possible_usernames.pop_back(); | 312 other_possible_usernames.pop_back(); |
| 307 } | 313 } |
| 308 } | 314 } |
| 309 } | 315 } |
| 310 | 316 |
| 311 // Various input types such as text, url, email can be a username field. | 317 // Various input types such as text, url, email can be a username field. |
| 312 if (input_element->isTextField() && !input_element->isPasswordField()) { | 318 if (input_element->isTextField() && !input_element->isPasswordField()) { |
| 313 if (HasAutocompleteAttributeValue(*input_element, "username")) { | 319 if (HasAutocompleteAttributeValue(*input_element, |
| 320 kAutocompleteUsername)) { |
| 314 if (password_form->username_marked_by_site) { | 321 if (password_form->username_marked_by_site) { |
| 315 // A second or subsequent element marked with autocomplete='username'. | 322 // A second or subsequent element marked with autocomplete='username'. |
| 316 // This makes us less confident that we have understood the form. We | 323 // This makes us less confident that we have understood the form. We |
| 317 // will stick to our choice that the first such element was the real | 324 // will stick to our choice that the first such element was the real |
| 318 // username, but will start collecting other_possible_usernames from | 325 // username, but will start collecting other_possible_usernames from |
| 319 // the extra elements marked with autocomplete='username'. Note that | 326 // the extra elements marked with autocomplete='username'. Note that |
| 320 // unlike username_element, other_possible_usernames is used only for | 327 // unlike username_element, other_possible_usernames is used only for |
| 321 // autofill, not for form identification, and blank autofill entries | 328 // autofill, not for form identification, and blank autofill entries |
| 322 // are not useful, so we do not collect empty strings. | 329 // are not useful, so we do not collect empty strings. |
| 323 if (!input_element->value().isEmpty()) | 330 if (!input_element->value().isEmpty()) |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 418 if (nonscript_modified_values != nullptr) { | 425 if (nonscript_modified_values != nullptr) { |
| 419 auto password_iterator = nonscript_modified_values->find(password); | 426 auto password_iterator = nonscript_modified_values->find(password); |
| 420 if (password_iterator != nonscript_modified_values->end()) | 427 if (password_iterator != nonscript_modified_values->end()) |
| 421 password_value = password_iterator->second; | 428 password_value = password_iterator->second; |
| 422 } | 429 } |
| 423 password_form->password_value = password_value; | 430 password_form->password_value = password_value; |
| 424 } | 431 } |
| 425 if (!new_password.isNull()) { | 432 if (!new_password.isNull()) { |
| 426 password_form->new_password_element = new_password.nameForAutofill(); | 433 password_form->new_password_element = new_password.nameForAutofill(); |
| 427 password_form->new_password_value = new_password.value(); | 434 password_form->new_password_value = new_password.value(); |
| 428 if (HasAutocompleteAttributeValue(new_password, "new-password")) | 435 if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword)) |
| 429 password_form->new_password_marked_by_site = true; | 436 password_form->new_password_marked_by_site = true; |
| 430 } | 437 } |
| 431 | 438 |
| 432 if (username_element.isNull()) { | 439 if (username_element.isNull()) { |
| 433 // To get a better idea on how password forms without a username field | 440 // To get a better idea on how password forms without a username field |
| 434 // look like, report the total number of text and password fields. | 441 // look like, report the total number of text and password fields. |
| 435 UMA_HISTOGRAM_COUNTS_100( | 442 UMA_HISTOGRAM_COUNTS_100( |
| 436 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount", | 443 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount", |
| 437 layout_sequence.size()); | 444 layout_sequence.size()); |
| 438 // For comparison, also report the number of password fields. | 445 // For comparison, also report the number of password fields. |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 494 WebFormElementToFormData(web_form, | 501 WebFormElementToFormData(web_form, |
| 495 blink::WebFormControlElement(), | 502 blink::WebFormControlElement(), |
| 496 EXTRACT_NONE, | 503 EXTRACT_NONE, |
| 497 &password_form->form_data, | 504 &password_form->form_data, |
| 498 NULL /* FormFieldData */); | 505 NULL /* FormFieldData */); |
| 499 | 506 |
| 500 return password_form.Pass(); | 507 return password_form.Pass(); |
| 501 } | 508 } |
| 502 | 509 |
| 503 } // namespace autofill | 510 } // namespace autofill |
| OLD | NEW |