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_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 // First, look for elements marked with either autocomplete='current-password' | |
| 43 // and/or 'new-password', and treat the first of each kind as that element. | |
| 44 for (std::vector<WebInputElement>::const_iterator it = passwords.begin(); | |
| 45 it != passwords.end(); it++) { | |
| 46 // If some password elements are marked with autocomplete='current-password' | |
| 47 // or with 'new-password', take the hint, and treat the first of each kind | |
| 48 // the element we are looking for. | |
|
vabr (Chromium)
2014/07/05 09:21:39
typo: the element -> as the element
engedy
2014/07/07 08:00:39
Done. I have actually removed this block of commen
| |
| 49 if (HasAutocompleteAttributeValue(*it, "current-password") && | |
| 50 current_password->isNull()) { | |
| 51 *current_password = *it; | |
|
vabr (Chromium)
2014/07/05 09:21:39
optional nit: I wonder if we should document that
engedy
2014/07/07 08:00:39
Done.
| |
| 52 } else if (HasAutocompleteAttributeValue(*it, "new-password") && | |
| 53 new_password->isNull()) { | |
| 54 *new_password = *it; | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 // If we have found any marked-up elements, take the hint and skip all the | |
| 59 // heuristics we normally do, i.e., ignore the rest of the password fields. | |
| 60 // We do this under the assumption they are not intentionally not marked, | |
|
vabr (Chromium)
2014/07/05 09:21:39
typo: remove the first "not"
engedy
2014/07/07 08:00:39
Done, also rephrased.
| |
| 61 // because they are used for other purposes, e.g., PINs, OTPs, and the like. | |
| 62 if (!current_password->isNull() || !new_password->isNull()) | |
| 63 return true; | |
| 64 | |
| 42 switch (passwords.size()) { | 65 switch (passwords.size()) { |
| 43 case 1: | 66 case 1: |
| 44 // Single password, easy. | 67 // Single password, easy. |
| 45 *password = passwords[0]; | 68 *current_password = passwords[0]; |
| 46 break; | 69 break; |
| 47 case 2: | 70 case 2: |
| 48 if (passwords[0].value() == passwords[1].value()) { | 71 if (passwords[0].value() == passwords[1].value()) { |
| 49 // Two identical passwords: assume we are seeing a new password with a | 72 // 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 | 73 // confirmation. This can be either a sign-up form or a password change |
| 51 // form that does not ask for the old password. | 74 // form that does not ask for the old password. |
| 52 *new_password = passwords[0]; | 75 *new_password = passwords[0]; |
| 53 } else { | 76 } else { |
| 54 // Assume first is old password, second is new (no choice but to guess). | 77 // Assume first is old password, second is new (no choice but to guess). |
| 55 *password = passwords[0]; | 78 *current_password = passwords[0]; |
| 56 *new_password = passwords[1]; | 79 *new_password = passwords[1]; |
| 57 } | 80 } |
| 58 break; | 81 break; |
| 59 case 3: | 82 case 3: |
| 60 if (!passwords[0].value().isEmpty() && | 83 if (!passwords[0].value().isEmpty() && |
| 61 (passwords[0].value() == passwords[1].value() && | 84 (passwords[0].value() == passwords[1].value() && |
| 62 passwords[0].value() == passwords[2].value())) { | 85 passwords[0].value() == passwords[2].value())) { |
| 63 // All three passwords are the same and non-empty? This does not make | 86 // All three passwords are the same and non-empty? This does not make |
| 64 // any sense, give up. | 87 // any sense, give up. |
| 65 return false; | 88 return false; |
| 66 } else if (passwords[1].value() == passwords[2].value()) { | 89 } else if (passwords[1].value() == passwords[2].value()) { |
| 67 // New password is the duplicated one, and comes second; or empty form | 90 // 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. | 91 // with 3 password fields, in which case we will assume this layout. |
| 69 *password = passwords[0]; | 92 *current_password = passwords[0]; |
| 70 *new_password = passwords[1]; | 93 *new_password = passwords[1]; |
| 71 } else if (passwords[0].value() == passwords[1].value()) { | 94 } else if (passwords[0].value() == passwords[1].value()) { |
| 72 // It is strange that the new password comes first, but trust more which | 95 // It is strange that the new password comes first, but trust more which |
| 73 // fields are duplicated than the ordering of fields. | 96 // fields are duplicated than the ordering of fields. |
| 74 *password = passwords[2]; | 97 *current_password = passwords[2]; |
| 75 *new_password = passwords[0]; | 98 *new_password = passwords[0]; |
| 76 } else { | 99 } else { |
| 77 // Three different passwords, or first and last match with middle | 100 // Three different passwords, or first and last match with middle |
| 78 // different. No idea which is which, so no luck. | 101 // different. No idea which is which, so no luck. |
| 79 return false; | 102 return false; |
| 80 } | 103 } |
| 81 break; | 104 break; |
| 82 default: | 105 default: |
| 83 return false; | 106 return false; |
| 84 } | 107 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 121 DCHECK(!other_possible_usernames.empty()); | 144 DCHECK(!other_possible_usernames.empty()); |
| 122 DCHECK_EQ(base::string16(latest_input_element.value()), | 145 DCHECK_EQ(base::string16(latest_input_element.value()), |
| 123 other_possible_usernames.back()); | 146 other_possible_usernames.back()); |
| 124 other_possible_usernames.pop_back(); | 147 other_possible_usernames.pop_back(); |
| 125 } | 148 } |
| 126 } | 149 } |
| 127 } | 150 } |
| 128 | 151 |
| 129 // Various input types such as text, url, email can be a username field. | 152 // Various input types such as text, url, email can be a username field. |
| 130 if (input_element->isTextField() && !input_element->isPasswordField()) { | 153 if (input_element->isTextField() && !input_element->isPasswordField()) { |
| 131 if (HasAutocompleteAttributeValue(input_element, "username")) { | 154 if (HasAutocompleteAttributeValue(*input_element, "username")) { |
| 132 if (has_seen_element_with_autocomplete_username_before) { | 155 if (has_seen_element_with_autocomplete_username_before) { |
| 133 // A second or subsequent element marked with autocomplete='username'. | 156 // A second or subsequent element marked with autocomplete='username'. |
| 134 // This makes us less confident that we have understood the form. We | 157 // 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 | 158 // will stick to our choice that the first such element was the real |
| 136 // username, but will start collecting other_possible_usernames from | 159 // username, but will start collecting other_possible_usernames from |
| 137 // the extra elements marked with autocomplete='username'. Note that | 160 // the extra elements marked with autocomplete='username'. Note that |
| 138 // unlike username_element, other_possible_usernames is used only for | 161 // unlike username_element, other_possible_usernames is used only for |
| 139 // autofill, not for form identification, and blank autofill entries | 162 // autofill, not for form identification, and blank autofill entries |
| 140 // are not useful, so we do not collect empty strings. | 163 // are not useful, so we do not collect empty strings. |
| 141 if (!input_element->value().isEmpty()) | 164 if (!input_element->value().isEmpty()) |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 239 blink::WebFormControlElement(), | 262 blink::WebFormControlElement(), |
| 240 REQUIRE_NONE, | 263 REQUIRE_NONE, |
| 241 EXTRACT_NONE, | 264 EXTRACT_NONE, |
| 242 &password_form->form_data, | 265 &password_form->form_data, |
| 243 NULL /* FormFieldData */); | 266 NULL /* FormFieldData */); |
| 244 | 267 |
| 245 return password_form.Pass(); | 268 return password_form.Pass(); |
| 246 } | 269 } |
| 247 | 270 |
| 248 } // namespace autofill | 271 } // namespace autofill |
| OLD | NEW |