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

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

Issue 2637823002: [Password Generation] Send votes about confirmation fields (Closed)
Patch Set: Fixed comment to |confirmation_password_element| Created 3 years, 11 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
« 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 <stddef.h> 7 #include <stddef.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <string> 10 #include <string>
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 const WebInputElement& username_element, 147 const WebInputElement& username_element,
148 std::vector<base::string16>* other_possible_usernames) { 148 std::vector<base::string16>* other_possible_usernames) {
149 other_possible_usernames->erase( 149 other_possible_usernames->erase(
150 std::remove(other_possible_usernames->begin(), 150 std::remove(other_possible_usernames->begin(),
151 other_possible_usernames->end(), 151 other_possible_usernames->end(),
152 username_element.value()), 152 username_element.value()),
153 other_possible_usernames->end()); 153 other_possible_usernames->end());
154 } 154 }
155 155
156 // Helper to determine which password is the main (current) one, and which is 156 // Helper to determine which password is the main (current) one, and which is
157 // the new password (e.g., on a sign-up or change password form), if any. 157 // the new password (e.g., on a sign-up or change password form), if any. If the
158 // new password is found and there is another password field with the same user
159 // input, the function also sets |confirmation_password| to this field.
158 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords, 160 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
159 WebInputElement* current_password, 161 WebInputElement* current_password,
160 WebInputElement* new_password) { 162 WebInputElement* new_password,
163 WebInputElement* confirmation_password) {
161 DCHECK(current_password && current_password->isNull()); 164 DCHECK(current_password && current_password->isNull());
162 DCHECK(new_password && new_password->isNull()); 165 DCHECK(new_password && new_password->isNull());
166 DCHECK(confirmation_password && confirmation_password->isNull());
163 167
164 // First, look for elements marked with either autocomplete='current-password' 168 // First, look for elements marked with either autocomplete='current-password'
165 // or 'new-password' -- if we find any, take the hint, and treat the first of 169 // or 'new-password' -- if we find any, take the hint, and treat the first of
166 // each kind as the element we are looking for. 170 // each kind as the element we are looking for.
167 for (const WebInputElement& it : passwords) { 171 for (const WebInputElement& it : passwords) {
168 if (HasAutocompleteAttributeValue(it, kAutocompleteCurrentPassword) && 172 if (HasAutocompleteAttributeValue(it, kAutocompleteCurrentPassword) &&
169 current_password->isNull()) { 173 current_password->isNull()) {
170 *current_password = it; 174 *current_password = it;
171 } else if (HasAutocompleteAttributeValue(it, kAutocompleteNewPassword) && 175 } else if (HasAutocompleteAttributeValue(it, kAutocompleteNewPassword) &&
172 new_password->isNull()) { 176 new_password->isNull()) {
173 *new_password = it; 177 *new_password = it;
178 } else if (!new_password->isNull() &&
179 (new_password->value() == it.value())) {
180 *confirmation_password = it;
174 } 181 }
175 } 182 }
176 183
177 // If we have seen an element with either of autocomplete attributes above, 184 // If we have seen an element with either of autocomplete attributes above,
178 // take that as a signal that the page author must have intentionally left the 185 // take that as a signal that the page author must have intentionally left the
179 // rest of the password fields unmarked. Perhaps they are used for other 186 // rest of the password fields unmarked. Perhaps they are used for other
180 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we 187 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we
181 // normally do, and ignore the rest of the password fields. 188 // normally do, and ignore the rest of the password fields.
182 if (!current_password->isNull() || !new_password->isNull()) 189 if (!current_password->isNull() || !new_password->isNull())
183 return true; 190 return true;
184 191
185 if (passwords.empty()) 192 if (passwords.empty())
186 return false; 193 return false;
187 194
188 switch (passwords.size()) { 195 switch (passwords.size()) {
189 case 1: 196 case 1:
190 // Single password, easy. 197 // Single password, easy.
191 *current_password = passwords[0]; 198 *current_password = passwords[0];
192 break; 199 break;
193 case 2: 200 case 2:
194 if (!passwords[0].value().isEmpty() && 201 if (!passwords[0].value().isEmpty() &&
195 passwords[0].value() == passwords[1].value()) { 202 passwords[0].value() == passwords[1].value()) {
196 // Two identical non-empty passwords: assume we are seeing a new 203 // Two identical non-empty passwords: assume we are seeing a new
197 // password with a confirmation. This can be either a sign-up form or a 204 // password with a confirmation. This can be either a sign-up form or a
198 // password change form that does not ask for the old password. 205 // password change form that does not ask for the old password.
199 *new_password = passwords[0]; 206 *new_password = passwords[0];
207 *confirmation_password = passwords[1];
200 } else { 208 } else {
201 // Assume first is old password, second is new (no choice but to guess). 209 // Assume first is old password, second is new (no choice but to guess).
202 // This case also includes empty passwords in order to allow filling of 210 // This case also includes empty passwords in order to allow filling of
203 // password change forms (that also could autofill for sign up form, but 211 // password change forms (that also could autofill for sign up form, but
204 // we can't do anything with this using only client side information). 212 // we can't do anything with this using only client side information).
205 *current_password = passwords[0]; 213 *current_password = passwords[0];
206 *new_password = passwords[1]; 214 *new_password = passwords[1];
207 } 215 }
208 break; 216 break;
209 default: 217 default:
210 if (!passwords[0].value().isEmpty() && 218 if (!passwords[0].value().isEmpty() &&
211 passwords[0].value() == passwords[1].value() && 219 passwords[0].value() == passwords[1].value() &&
212 passwords[0].value() == passwords[2].value()) { 220 passwords[0].value() == passwords[2].value()) {
213 // All three passwords are the same and non-empty? This does not make 221 // All three passwords are the same and non-empty? This does not make
214 // any sense, give up. 222 // any sense, give up.
215 return false; 223 return false;
216 } else if (passwords[1].value() == passwords[2].value()) { 224 } else if (passwords[1].value() == passwords[2].value()) {
217 // New password is the duplicated one, and comes second; or empty form 225 // New password is the duplicated one, and comes second; or empty form
218 // with 3 password fields, in which case we will assume this layout. 226 // with 3 password fields, in which case we will assume this layout.
219 *current_password = passwords[0]; 227 *current_password = passwords[0];
220 *new_password = passwords[1]; 228 *new_password = passwords[1];
229 *confirmation_password = passwords[2];
221 } else if (passwords[0].value() == passwords[1].value()) { 230 } else if (passwords[0].value() == passwords[1].value()) {
222 // It is strange that the new password comes first, but trust more which 231 // It is strange that the new password comes first, but trust more which
223 // fields are duplicated than the ordering of fields. Assume that 232 // fields are duplicated than the ordering of fields. Assume that
224 // any password fields after the new password contain sensitive 233 // any password fields after the new password contain sensitive
225 // information that isn't actually a password (security hint, SSN, etc.) 234 // information that isn't actually a password (security hint, SSN, etc.)
226 *new_password = passwords[0]; 235 *new_password = passwords[0];
236 *confirmation_password = passwords[1];
227 } else { 237 } else {
228 // Three different passwords, or first and last match with middle 238 // Three different passwords, or first and last match with middle
229 // different. No idea which is which, so no luck. 239 // different. No idea which is which, so no luck.
230 return false; 240 return false;
231 } 241 }
232 } 242 }
233 return true; 243 return true;
234 } 244 }
235 245
236 // Checks the |form_predictions| map to see if there is a key associated with 246 // Checks the |form_predictions| map to see if there is a key associated with
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 latest_input_element = *input_element; 508 latest_input_element = *input_element;
499 if (!input_element->value().isEmpty()) 509 if (!input_element->value().isEmpty())
500 other_possible_usernames.push_back(input_element->value()); 510 other_possible_usernames.push_back(input_element->value());
501 } 511 }
502 } 512 }
503 } 513 }
504 } 514 }
505 515
506 WebInputElement password; 516 WebInputElement password;
507 WebInputElement new_password; 517 WebInputElement new_password;
508 if (!LocateSpecificPasswords(passwords, &password, &new_password)) 518 WebInputElement confirmation_password;
519 if (!LocateSpecificPasswords(passwords, &password, &new_password,
520 &confirmation_password))
509 return false; 521 return false;
510 522
511 DCHECK_EQ(passwords.size(), last_text_input_before_password.size()); 523 DCHECK_EQ(passwords.size(), last_text_input_before_password.size());
512 if (username_element.isNull()) { 524 if (username_element.isNull()) {
513 if (!password.isNull()) 525 if (!password.isNull())
514 username_element = last_text_input_before_password[password]; 526 username_element = last_text_input_before_password[password];
515 if (username_element.isNull() && !new_password.isNull()) 527 if (username_element.isNull() && !new_password.isNull())
516 username_element = last_text_input_before_password[new_password]; 528 username_element = last_text_input_before_password[new_password];
517 if (!username_element.isNull()) 529 if (!username_element.isNull())
518 ExcludeUsernameFromOtherUsernamesList(username_element, 530 ExcludeUsernameFromOtherUsernamesList(username_element,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 password_form->password_value = password_value; 589 password_form->password_value = password_value;
578 } 590 }
579 if (!new_password.isNull()) { 591 if (!new_password.isNull()) {
580 password_form->new_password_element = 592 password_form->new_password_element =
581 FieldName(new_password, "anonymous_new_password"); 593 FieldName(new_password, "anonymous_new_password");
582 password_form->new_password_value = new_password.value(); 594 password_form->new_password_value = new_password.value();
583 password_form->new_password_value_is_default = 595 password_form->new_password_value_is_default =
584 new_password.getAttribute("value") == new_password.value(); 596 new_password.getAttribute("value") == new_password.value();
585 if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword)) 597 if (HasAutocompleteAttributeValue(new_password, kAutocompleteNewPassword))
586 password_form->new_password_marked_by_site = true; 598 password_form->new_password_marked_by_site = true;
599 if (!confirmation_password.isNull()) {
600 password_form->confirmation_password_element =
601 FieldName(confirmation_password, "anonymous_confirmation_password");
602 }
587 } 603 }
588 604
589 if (username_element.isNull()) { 605 if (username_element.isNull()) {
590 // To get a better idea on how password forms without a username field 606 // To get a better idea on how password forms without a username field
591 // look like, report the total number of text and password fields. 607 // look like, report the total number of text and password fields.
592 UMA_HISTOGRAM_COUNTS_100( 608 UMA_HISTOGRAM_COUNTS_100(
593 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount", 609 "PasswordManager.EmptyUsernames.TextAndPasswordFieldCount",
594 layout_sequence.size()); 610 layout_sequence.size());
595 // For comparison, also report the number of password fields. 611 // For comparison, also report the number of password fields.
596 UMA_HISTOGRAM_COUNTS_100( 612 UMA_HISTOGRAM_COUNTS_100(
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
706 bool HasAutocompleteAttributeValue(const blink::WebInputElement& element, 722 bool HasAutocompleteAttributeValue(const blink::WebInputElement& element,
707 const char* value_in_lowercase) { 723 const char* value_in_lowercase) {
708 base::string16 autocomplete_attribute(element.getAttribute("autocomplete")); 724 base::string16 autocomplete_attribute(element.getAttribute("autocomplete"));
709 std::vector<std::string> tokens = LowercaseAndTokenizeAttributeString( 725 std::vector<std::string> tokens = LowercaseAndTokenizeAttributeString(
710 base::UTF16ToUTF8(autocomplete_attribute)); 726 base::UTF16ToUTF8(autocomplete_attribute));
711 727
712 return base::ContainsValue(tokens, value_in_lowercase); 728 return base::ContainsValue(tokens, value_in_lowercase);
713 } 729 }
714 730
715 } // namespace autofill 731 } // 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