Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: components/autofill/content/renderer/password_form_conversion_utils.cc

Issue 369323003: Implement autocomplete='current-password' and 'new-password'. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase/ Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698