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.
4
5 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
6
7 #include "components/autofill/content/renderer/form_autofill_util.h"
8 #include "components/autofill/core/common/password_form.h"
9 #include "third_party/WebKit/public/platform/WebString.h"
10 #include "third_party/WebKit/public/web/WebDocument.h"
11 #include "third_party/WebKit/public/web/WebFormControlElement.h"
12 #include "third_party/WebKit/public/web/WebInputElement.h"
13
14 using blink::WebDocument;
15 using blink::WebFormControlElement;
16 using blink::WebFormElement;
17 using blink::WebInputElement;
18 using blink::WebString;
19 using blink::WebVector;
20
21 namespace autofill {
22 namespace {
23
24 // Maximum number of password fields we will observe before throwing our
25 // hands in the air and giving up with a given form.
26 static const size_t kMaxPasswords = 3;
27
28 // Helper to determine which password is the main one, and which is
29 // an old password (e.g on a "make new password" form), if any.
LocateSpecificPasswords(std::vector<WebInputElement> passwords,WebInputElement * password,WebInputElement * old_password)30 bool LocateSpecificPasswords(std::vector<WebInputElement> passwords,
31 WebInputElement* password,
32 WebInputElement* old_password) {
33 switch (passwords.size()) {
34 case 1:
35 // Single password, easy.
36 *password = passwords[0];
37 break;
38 case 2:
39 if (passwords[0].value() == passwords[1].value()) {
40 // Treat two identical passwords as a single password.
41 *password = passwords[0];
42 } else {
43 // Assume first is old password, second is new (no choice but to guess).
44 *old_password = passwords[0];
45 *password = passwords[1];
46 }
47 break;
48 case 3:
49 if (passwords[0].value() == passwords[1].value() &&
50 passwords[0].value() == passwords[2].value()) {
51 // All three passwords the same? Just treat as one and hope.
52 *password = passwords[0];
53 } else if (passwords[0].value() == passwords[1].value()) {
54 // Two the same and one different -> old password is duplicated one.
55 *old_password = passwords[0];
56 *password = passwords[2];
57 } else if (passwords[1].value() == passwords[2].value()) {
58 *old_password = passwords[0];
59 *password = passwords[1];
60 } else {
61 // Three different passwords, or first and last match with middle
62 // different. No idea which is which, so no luck.
63 return false;
64 }
65 break;
66 default:
67 return false;
68 }
69 return true;
70 }
71
72 // Get information about a login form that encapsulated in the
73 // PasswordForm struct.
GetPasswordForm(const WebFormElement & form,PasswordForm * password_form)74 void GetPasswordForm(const WebFormElement& form, PasswordForm* password_form) {
75 WebInputElement latest_input_element;
76 std::vector<WebInputElement> passwords;
77 std::vector<base::string16> other_possible_usernames;
78
79 WebVector<WebFormControlElement> control_elements;
80 form.getFormControlElements(control_elements);
81
82 for (size_t i = 0; i < control_elements.size(); ++i) {
83 WebFormControlElement control_element = control_elements[i];
84 if (control_element.isActivatedSubmit())
85 password_form->submit_element = control_element.formControlName();
86
87 WebInputElement* input_element = toWebInputElement(&control_element);
88 if (!input_element || !input_element->isEnabled())
89 continue;
90
91 if ((passwords.size() < kMaxPasswords) &&
92 input_element->isPasswordField()) {
93 // We assume that the username element is the input element before the
94 // first password element.
95 if (passwords.empty() && !latest_input_element.isNull()) {
96 password_form->username_element =
97 latest_input_element.nameForAutofill();
98 password_form->username_value = latest_input_element.value();
99 // Remove the selected username from other_possible_usernames.
100 if (!other_possible_usernames.empty() &&
101 !latest_input_element.value().isEmpty())
102 other_possible_usernames.resize(other_possible_usernames.size() - 1);
103 }
104 passwords.push_back(*input_element);
105 }
106
107 // Various input types such as text, url, email can be a username field.
108 if (input_element->isTextField() && !input_element->isPasswordField()) {
109 latest_input_element = *input_element;
110 // We ignore elements that have no value. Unlike username_element,
111 // other_possible_usernames is used only for autofill, not for form
112 // identification, and blank autofill entries are not useful.
113 if (!input_element->value().isEmpty())
114 other_possible_usernames.push_back(input_element->value());
115 }
116 }
117
118 // Get the document URL
119 GURL full_origin(form.document().url());
120
121 // Calculate the canonical action URL
122 WebString action = form.action();
123 if (action.isNull())
124 action = WebString(""); // missing 'action' attribute implies current URL
125 GURL full_action(form.document().completeURL(action));
126 if (!full_action.is_valid())
127 return;
128
129 WebInputElement password;
130 WebInputElement old_password;
131 if (!LocateSpecificPasswords(passwords, &password, &old_password))
132 return;
133
134 // We want to keep the path but strip any authentication data, as well as
135 // query and ref portions of URL, for the form action and form origin.
136 GURL::Replacements rep;
137 rep.ClearUsername();
138 rep.ClearPassword();
139 rep.ClearQuery();
140 rep.ClearRef();
141 password_form->action = full_action.ReplaceComponents(rep);
142 password_form->origin = full_origin.ReplaceComponents(rep);
143
144 rep.SetPathStr("");
145 password_form->signon_realm = full_origin.ReplaceComponents(rep).spec();
146
147 password_form->other_possible_usernames.swap(other_possible_usernames);
148
149 if (!password.isNull()) {
150 password_form->password_element = password.nameForAutofill();
151 password_form->password_value = password.value();
152 password_form->password_autocomplete_set = password.autoComplete();
153 }
154 if (!old_password.isNull()) {
155 password_form->old_password_element = old_password.nameForAutofill();
156 password_form->old_password_value = old_password.value();
157 }
158
159 password_form->scheme = PasswordForm::SCHEME_HTML;
160 password_form->ssl_valid = false;
161 password_form->preferred = false;
162 password_form->blacklisted_by_user = false;
163 password_form->type = PasswordForm::TYPE_MANUAL;
164 password_form->use_additional_authentication = false;
165 }
166
167 } // namespace
168
CreatePasswordForm(const WebFormElement & web_form)169 scoped_ptr<PasswordForm> CreatePasswordForm(const WebFormElement& web_form) {
170 if (web_form.isNull())
171 return scoped_ptr<PasswordForm>();
172
173 scoped_ptr<PasswordForm> password_form(new PasswordForm());
174 GetPasswordForm(web_form, password_form.get());
175
176 if (!password_form->action.is_valid())
177 return scoped_ptr<PasswordForm>();
178
179 WebFormElementToFormData(web_form,
180 blink::WebFormControlElement(),
181 REQUIRE_NONE,
182 EXTRACT_NONE,
183 &password_form->form_data,
184 NULL /* FormFieldData */);
185
186 return password_form.Pass();
187 }
188
189 } // namespace autofill
190