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 "base/strings/string_util.h" | 7 #include "base/strings/string_util.h" |
8 #include "components/autofill/content/renderer/form_autofill_util.h" | 8 #include "components/autofill/content/renderer/form_autofill_util.h" |
9 #include "components/autofill/core/common/password_form.h" | 9 #include "components/autofill/core/common/password_form.h" |
10 #include "third_party/WebKit/public/platform/WebString.h" | 10 #include "third_party/WebKit/public/platform/WebString.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 | 21 |
22 namespace autofill { | 22 namespace autofill { |
23 namespace { | 23 namespace { |
24 | 24 |
25 // Maximum number of password fields we will observe before throwing our | 25 // Maximum number of password fields we will observe before throwing our |
26 // hands in the air and giving up with a given form. | 26 // hands in the air and giving up with a given form. |
27 static const size_t kMaxPasswords = 3; | 27 static const size_t kMaxPasswords = 3; |
28 | 28 |
29 // Checks in a case-insensitive way if the autocomplete attribute for the given | 29 // Checks in a case-insensitive way if the autocomplete attribute for the given |
30 // |element| is present and has the specified |value_in_lowercase|. | 30 // |element| is present and has the specified |value_in_lowercase|. |
31 bool HasAutocompleteAttributeValue(const WebInputElement* element, | 31 bool HasAutocompleteAttributeValue(const WebInputElement& element, |
32 const char* value_in_lowercase) { | 32 const char* value_in_lowercase) { |
33 return LowerCaseEqualsASCII(element->getAttribute("autocomplete"), | 33 return LowerCaseEqualsASCII(element.getAttribute("autocomplete"), |
34 value_in_lowercase); | 34 value_in_lowercase); |
35 } | 35 } |
36 | 36 |
37 // Helper to determine which password is the main (current) one, and which is | 37 // Helper to determine which password is the main (current) one, and which is |
38 // the new password (e.g., on a sign-up or change password form), if any. | 38 // the new password (e.g., on a sign-up or change password form), if any. |
39 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, | 39 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, |
40 WebInputElement* password, | 40 WebInputElement* current_password, |
41 WebInputElement* new_password) { | 41 WebInputElement* new_password) { |
| 42 DCHECK(current_password && current_password->isNull()); |
| 43 DCHECK(new_password && new_password->isNull()); |
| 44 |
| 45 // First, look for elements marked with either autocomplete='current-password' |
| 46 // or 'new-password' -- if we find any, take the hint, and treat the first of |
| 47 // each kind as the element we are looking for. |
| 48 for (std::vector<WebInputElement>::const_iterator it = passwords.begin(); |
| 49 it != passwords.end(); it++) { |
| 50 if (HasAutocompleteAttributeValue(*it, "current-password") && |
| 51 current_password->isNull()) { |
| 52 *current_password = *it; |
| 53 } else if (HasAutocompleteAttributeValue(*it, "new-password") && |
| 54 new_password->isNull()) { |
| 55 *new_password = *it; |
| 56 } |
| 57 } |
| 58 |
| 59 // If we have seen an element with either of autocomplete attributes above, |
| 60 // take that as a signal that the page author must have intentionally left the |
| 61 // rest of the password fields unmarked. Perhaps they are used for other |
| 62 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we |
| 63 // normally do, and ignore the rest of the password fields. |
| 64 if (!current_password->isNull() || !new_password->isNull()) |
| 65 return true; |
| 66 |
42 switch (passwords.size()) { | 67 switch (passwords.size()) { |
43 case 1: | 68 case 1: |
44 // Single password, easy. | 69 // Single password, easy. |
45 *password = passwords[0]; | 70 *current_password = passwords[0]; |
46 break; | 71 break; |
47 case 2: | 72 case 2: |
48 if (passwords[0].value() == passwords[1].value()) { | 73 if (passwords[0].value() == passwords[1].value()) { |
49 // Two identical passwords: assume we are seeing a new password with a | 74 // Two identical passwords: assume we are seeing a new password with a |
50 // confirmation. This can be either a sign-up form or a password change | 75 // confirmation. This can be either a sign-up form or a password change |
51 // form that does not ask for the old password. | 76 // form that does not ask for the old password. |
52 *new_password = passwords[0]; | 77 *new_password = passwords[0]; |
53 } else { | 78 } else { |
54 // Assume first is old password, second is new (no choice but to guess). | 79 // Assume first is old password, second is new (no choice but to guess). |
55 *password = passwords[0]; | 80 *current_password = passwords[0]; |
56 *new_password = passwords[1]; | 81 *new_password = passwords[1]; |
57 } | 82 } |
58 break; | 83 break; |
59 case 3: | 84 case 3: |
60 if (!passwords[0].value().isEmpty() && | 85 if (!passwords[0].value().isEmpty() && |
61 passwords[0].value() == passwords[1].value() && | 86 passwords[0].value() == passwords[1].value() && |
62 passwords[0].value() == passwords[2].value()) { | 87 passwords[0].value() == passwords[2].value()) { |
63 // All three passwords are the same and non-empty? This does not make | 88 // All three passwords are the same and non-empty? This does not make |
64 // any sense, give up. | 89 // any sense, give up. |
65 return false; | 90 return false; |
66 } else if (passwords[1].value() == passwords[2].value()) { | 91 } else if (passwords[1].value() == passwords[2].value()) { |
67 // New password is the duplicated one, and comes second; or empty form | 92 // New password is the duplicated one, and comes second; or empty form |
68 // with 3 password fields, in which case we will assume this layout. | 93 // with 3 password fields, in which case we will assume this layout. |
69 *password = passwords[0]; | 94 *current_password = passwords[0]; |
70 *new_password = passwords[1]; | 95 *new_password = passwords[1]; |
71 } else if (passwords[0].value() == passwords[1].value()) { | 96 } else if (passwords[0].value() == passwords[1].value()) { |
72 // It is strange that the new password comes first, but trust more which | 97 // It is strange that the new password comes first, but trust more which |
73 // fields are duplicated than the ordering of fields. | 98 // fields are duplicated than the ordering of fields. |
74 *password = passwords[2]; | 99 *current_password = passwords[2]; |
75 *new_password = passwords[0]; | 100 *new_password = passwords[0]; |
76 } else { | 101 } else { |
77 // Three different passwords, or first and last match with middle | 102 // Three different passwords, or first and last match with middle |
78 // different. No idea which is which, so no luck. | 103 // different. No idea which is which, so no luck. |
79 return false; | 104 return false; |
80 } | 105 } |
81 break; | 106 break; |
82 default: | 107 default: |
83 return false; | 108 return false; |
84 } | 109 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 DCHECK(!other_possible_usernames.empty()); | 146 DCHECK(!other_possible_usernames.empty()); |
122 DCHECK_EQ(base::string16(latest_input_element.value()), | 147 DCHECK_EQ(base::string16(latest_input_element.value()), |
123 other_possible_usernames.back()); | 148 other_possible_usernames.back()); |
124 other_possible_usernames.pop_back(); | 149 other_possible_usernames.pop_back(); |
125 } | 150 } |
126 } | 151 } |
127 } | 152 } |
128 | 153 |
129 // Various input types such as text, url, email can be a username field. | 154 // Various input types such as text, url, email can be a username field. |
130 if (input_element->isTextField() && !input_element->isPasswordField()) { | 155 if (input_element->isTextField() && !input_element->isPasswordField()) { |
131 if (HasAutocompleteAttributeValue(input_element, "username")) { | 156 if (HasAutocompleteAttributeValue(*input_element, "username")) { |
132 if (has_seen_element_with_autocomplete_username_before) { | 157 if (has_seen_element_with_autocomplete_username_before) { |
133 // A second or subsequent element marked with autocomplete='username'. | 158 // A second or subsequent element marked with autocomplete='username'. |
134 // This makes us less confident that we have understood the form. We | 159 // This makes us less confident that we have understood the form. We |
135 // will stick to our choice that the first such element was the real | 160 // will stick to our choice that the first such element was the real |
136 // username, but will start collecting other_possible_usernames from | 161 // username, but will start collecting other_possible_usernames from |
137 // the extra elements marked with autocomplete='username'. Note that | 162 // the extra elements marked with autocomplete='username'. Note that |
138 // unlike username_element, other_possible_usernames is used only for | 163 // unlike username_element, other_possible_usernames is used only for |
139 // autofill, not for form identification, and blank autofill entries | 164 // autofill, not for form identification, and blank autofill entries |
140 // are not useful, so we do not collect empty strings. | 165 // are not useful, so we do not collect empty strings. |
141 if (!input_element->value().isEmpty()) | 166 if (!input_element->value().isEmpty()) |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 blink::WebFormControlElement(), | 264 blink::WebFormControlElement(), |
240 REQUIRE_NONE, | 265 REQUIRE_NONE, |
241 EXTRACT_NONE, | 266 EXTRACT_NONE, |
242 &password_form->form_data, | 267 &password_form->form_data, |
243 NULL /* FormFieldData */); | 268 NULL /* FormFieldData */); |
244 | 269 |
245 return password_form.Pass(); | 270 return password_form.Pass(); |
246 } | 271 } |
247 | 272 |
248 } // namespace autofill | 273 } // namespace autofill |
OLD | NEW |