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 |