Add diagnostics_writer.cc to the list of files allowed to printf.
[chromium-blink-merge.git] / components / autofill / content / renderer / password_form_conversion_utils.cc
blob538b602ba21eb6ef48a1e916c0e9d55b6b4c72a8
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
3 // found in the LICENSE file.
5 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
7 #include "base/strings/string_util.h"
8 #include "components/autofill/content/renderer/form_autofill_util.h"
9 #include "components/autofill/core/common/password_form.h"
10 #include "third_party/WebKit/public/platform/WebString.h"
11 #include "third_party/WebKit/public/web/WebDocument.h"
12 #include "third_party/WebKit/public/web/WebFormControlElement.h"
13 #include "third_party/WebKit/public/web/WebInputElement.h"
15 using blink::WebDocument;
16 using blink::WebFormControlElement;
17 using blink::WebFormElement;
18 using blink::WebInputElement;
19 using blink::WebString;
20 using blink::WebVector;
22 namespace autofill {
23 namespace {
25 // Checks in a case-insensitive way if the autocomplete attribute for the given
26 // |element| is present and has the specified |value_in_lowercase|.
27 bool HasAutocompleteAttributeValue(const WebInputElement& element,
28 const char* value_in_lowercase) {
29 return LowerCaseEqualsASCII(element.getAttribute("autocomplete"),
30 value_in_lowercase);
33 // Helper to determine which password is the main (current) one, and which is
34 // the new password (e.g., on a sign-up or change password form), if any.
35 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
36 WebInputElement* current_password,
37 WebInputElement* new_password) {
38 DCHECK(current_password && current_password->isNull());
39 DCHECK(new_password && new_password->isNull());
41 // First, look for elements marked with either autocomplete='current-password'
42 // or 'new-password' -- if we find any, take the hint, and treat the first of
43 // each kind as the element we are looking for.
44 for (std::vector<WebInputElement>::const_iterator it = passwords.begin();
45 it != passwords.end(); it++) {
46 if (HasAutocompleteAttributeValue(*it, "current-password") &&
47 current_password->isNull()) {
48 *current_password = *it;
49 } else if (HasAutocompleteAttributeValue(*it, "new-password") &&
50 new_password->isNull()) {
51 *new_password = *it;
55 // If we have seen an element with either of autocomplete attributes above,
56 // take that as a signal that the page author must have intentionally left the
57 // rest of the password fields unmarked. Perhaps they are used for other
58 // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we
59 // normally do, and ignore the rest of the password fields.
60 if (!current_password->isNull() || !new_password->isNull())
61 return true;
63 switch (passwords.size()) {
64 case 1:
65 // Single password, easy.
66 *current_password = passwords[0];
67 break;
68 case 2:
69 if (passwords[0].value() == passwords[1].value()) {
70 // Two identical passwords: assume we are seeing a new password with a
71 // confirmation. This can be either a sign-up form or a password change
72 // form that does not ask for the old password.
73 *new_password = passwords[0];
74 } else {
75 // Assume first is old password, second is new (no choice but to guess).
76 *current_password = passwords[0];
77 *new_password = passwords[1];
79 break;
80 case 3:
81 if (!passwords[0].value().isEmpty() &&
82 passwords[0].value() == passwords[1].value() &&
83 passwords[0].value() == passwords[2].value()) {
84 // All three passwords are the same and non-empty? This does not make
85 // any sense, give up.
86 return false;
87 } else if (passwords[1].value() == passwords[2].value()) {
88 // New password is the duplicated one, and comes second; or empty form
89 // with 3 password fields, in which case we will assume this layout.
90 *current_password = passwords[0];
91 *new_password = passwords[1];
92 } else if (passwords[0].value() == passwords[1].value()) {
93 // It is strange that the new password comes first, but trust more which
94 // fields are duplicated than the ordering of fields.
95 *current_password = passwords[2];
96 *new_password = passwords[0];
97 } else {
98 // Three different passwords, or first and last match with middle
99 // different. No idea which is which, so no luck.
100 return false;
102 break;
103 default:
104 return false;
106 return true;
109 // Get information about a login form encapsulated in a PasswordForm struct.
110 void GetPasswordForm(const WebFormElement& form, PasswordForm* password_form) {
111 WebInputElement latest_input_element;
112 WebInputElement username_element;
113 // Caches whether |username_element| is marked with autocomplete='username'.
114 // Needed for performance reasons to avoid recalculating this multiple times.
115 bool has_seen_element_with_autocomplete_username_before = false;
116 std::vector<WebInputElement> passwords;
117 std::vector<base::string16> other_possible_usernames;
119 WebVector<WebFormControlElement> control_elements;
120 form.getFormControlElements(control_elements);
122 for (size_t i = 0; i < control_elements.size(); ++i) {
123 WebFormControlElement control_element = control_elements[i];
124 if (control_element.isActivatedSubmit())
125 password_form->submit_element = control_element.formControlName();
127 WebInputElement* input_element = toWebInputElement(&control_element);
128 if (!input_element || !input_element->isEnabled())
129 continue;
131 if (input_element->isPasswordField()) {
132 passwords.push_back(*input_element);
133 // If we have not yet considered any element to be the username so far,
134 // provisionally select the input element just before the first password
135 // element to be the username. This choice will be overruled if we later
136 // find an element with autocomplete='username'.
137 if (username_element.isNull() && !latest_input_element.isNull()) {
138 username_element = latest_input_element;
139 // Remove the selected username from other_possible_usernames.
140 if (!latest_input_element.value().isEmpty()) {
141 DCHECK(!other_possible_usernames.empty());
142 DCHECK_EQ(base::string16(latest_input_element.value()),
143 other_possible_usernames.back());
144 other_possible_usernames.pop_back();
149 // Various input types such as text, url, email can be a username field.
150 if (input_element->isTextField() && !input_element->isPasswordField()) {
151 if (HasAutocompleteAttributeValue(*input_element, "username")) {
152 if (has_seen_element_with_autocomplete_username_before) {
153 // A second or subsequent element marked with autocomplete='username'.
154 // This makes us less confident that we have understood the form. We
155 // will stick to our choice that the first such element was the real
156 // username, but will start collecting other_possible_usernames from
157 // the extra elements marked with autocomplete='username'. Note that
158 // unlike username_element, other_possible_usernames is used only for
159 // autofill, not for form identification, and blank autofill entries
160 // are not useful, so we do not collect empty strings.
161 if (!input_element->value().isEmpty())
162 other_possible_usernames.push_back(input_element->value());
163 } else {
164 // The first element marked with autocomplete='username'. Take the
165 // hint and treat it as the username (overruling the tentative choice
166 // we might have made before). Furthermore, drop all other possible
167 // usernames we have accrued so far: they come from fields not marked
168 // with the autocomplete attribute, making them unlikely alternatives.
169 username_element = *input_element;
170 has_seen_element_with_autocomplete_username_before = true;
171 other_possible_usernames.clear();
173 } else {
174 if (has_seen_element_with_autocomplete_username_before) {
175 // Having seen elements with autocomplete='username', elements without
176 // this attribute are no longer interesting. No-op.
177 } else {
178 // No elements marked with autocomplete='username' so far whatsoever.
179 // If we have not yet selected a username element even provisionally,
180 // then remember this element for the case when the next field turns
181 // out to be a password. Save a non-empty username as a possible
182 // alternative, at least for now.
183 if (username_element.isNull())
184 latest_input_element = *input_element;
185 if (!input_element->value().isEmpty())
186 other_possible_usernames.push_back(input_element->value());
192 if (!username_element.isNull()) {
193 password_form->username_element = username_element.nameForAutofill();
194 password_form->username_value = username_element.value();
197 // Get the document URL
198 GURL full_origin(form.document().url());
200 // Calculate the canonical action URL
201 WebString action = form.action();
202 if (action.isNull())
203 action = WebString(""); // missing 'action' attribute implies current URL
204 GURL full_action(form.document().completeURL(action));
205 if (!full_action.is_valid())
206 return;
208 WebInputElement password;
209 WebInputElement new_password;
210 if (!LocateSpecificPasswords(passwords, &password, &new_password))
211 return;
213 // We want to keep the path but strip any authentication data, as well as
214 // query and ref portions of URL, for the form action and form origin.
215 GURL::Replacements rep;
216 rep.ClearUsername();
217 rep.ClearPassword();
218 rep.ClearQuery();
219 rep.ClearRef();
220 password_form->action = full_action.ReplaceComponents(rep);
221 password_form->origin = full_origin.ReplaceComponents(rep);
223 rep.SetPathStr("");
224 password_form->signon_realm = full_origin.ReplaceComponents(rep).spec();
226 password_form->other_possible_usernames.swap(other_possible_usernames);
228 if (!password.isNull()) {
229 password_form->password_element = password.nameForAutofill();
230 password_form->password_value = password.value();
231 password_form->password_autocomplete_set = password.autoComplete();
233 if (!new_password.isNull()) {
234 password_form->new_password_element = new_password.nameForAutofill();
235 password_form->new_password_value = new_password.value();
238 password_form->scheme = PasswordForm::SCHEME_HTML;
239 password_form->ssl_valid = false;
240 password_form->preferred = false;
241 password_form->blacklisted_by_user = false;
242 password_form->type = PasswordForm::TYPE_MANUAL;
243 password_form->use_additional_authentication = false;
246 } // namespace
248 scoped_ptr<PasswordForm> CreatePasswordForm(const WebFormElement& web_form) {
249 if (web_form.isNull())
250 return scoped_ptr<PasswordForm>();
252 scoped_ptr<PasswordForm> password_form(new PasswordForm());
253 GetPasswordForm(web_form, password_form.get());
255 if (!password_form->action.is_valid())
256 return scoped_ptr<PasswordForm>();
258 WebFormElementToFormData(web_form,
259 blink::WebFormControlElement(),
260 REQUIRE_NONE,
261 EXTRACT_NONE,
262 &password_form->form_data,
263 NULL /* FormFieldData */);
265 return password_form.Pass();
268 } // namespace autofill