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_generation_agent.h"
6
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "components/autofill/content/common/autofill_messages.h"
11 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
12 #include "components/autofill/core/common/autofill_switches.h"
13 #include "components/autofill/core/common/form_data.h"
14 #include "components/autofill/core/common/password_form.h"
15 #include "components/autofill/core/common/password_generation_util.h"
16 #include "content/public/renderer/render_view.h"
17 #include "google_apis/gaia/gaia_urls.h"
18 #include "third_party/WebKit/public/platform/WebCString.h"
19 #include "third_party/WebKit/public/platform/WebRect.h"
20 #include "third_party/WebKit/public/platform/WebVector.h"
21 #include "third_party/WebKit/public/web/WebDocument.h"
22 #include "third_party/WebKit/public/web/WebFormElement.h"
23 #include "third_party/WebKit/public/web/WebFrame.h"
24 #include "third_party/WebKit/public/web/WebInputElement.h"
25 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
26 #include "third_party/WebKit/public/web/WebView.h"
27 #include "ui/gfx/rect.h"
28
29 namespace autofill {
30
31 namespace {
32
33 // Returns true if we think that this form is for account creation. |passwords|
34 // is filled with the password field(s) in the form.
GetAccountCreationPasswordFields(const blink::WebFormElement & form,std::vector<blink::WebInputElement> * passwords)35 bool GetAccountCreationPasswordFields(
36 const blink::WebFormElement& form,
37 std::vector<blink::WebInputElement>* passwords) {
38 // Grab all of the passwords for the form.
39 blink::WebVector<blink::WebFormControlElement> control_elements;
40 form.getFormControlElements(control_elements);
41
42 size_t num_input_elements = 0;
43 for (size_t i = 0; i < control_elements.size(); i++) {
44 blink::WebInputElement* input_element =
45 toWebInputElement(&control_elements[i]);
46 // Only pay attention to visible password fields.
47 if (input_element &&
48 input_element->isTextField() &&
49 input_element->hasNonEmptyBoundingBox()) {
50 num_input_elements++;
51 if (input_element->isPasswordField())
52 passwords->push_back(*input_element);
53 }
54 }
55
56 // This may be too lenient, but we assume that any form with at least three
57 // input elements where at least one of them is a password is an account
58 // creation form.
59 if (!passwords->empty() && num_input_elements >= 3) {
60 // We trim |passwords| because occasionally there are forms where the
61 // security question answers are put in password fields and we don't want
62 // to fill those.
63 if (passwords->size() > 2)
64 passwords->resize(2);
65
66 return true;
67 }
68
69 return false;
70 }
71
ContainsURL(const std::vector<GURL> & urls,const GURL & url)72 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
73 return std::find(urls.begin(), urls.end(), url) != urls.end();
74 }
75
76 // Returns true if the |form1| is essentially equal to |form2|.
FormsAreEqual(const autofill::FormData & form1,const PasswordForm & form2)77 bool FormsAreEqual(const autofill::FormData& form1,
78 const PasswordForm& form2) {
79 // TODO(zysxqn): use more signals than just origin to compare.
80 // Note that FormData strips the fragement from the url while PasswordForm
81 // strips both the fragement and the path, so we can't just compare these
82 // two directly.
83 return form1.origin.GetOrigin() == form2.origin.GetOrigin();
84 }
85
ContainsForm(const std::vector<autofill::FormData> & forms,const PasswordForm & form)86 bool ContainsForm(const std::vector<autofill::FormData>& forms,
87 const PasswordForm& form) {
88 for (std::vector<autofill::FormData>::const_iterator it =
89 forms.begin(); it != forms.end(); ++it) {
90 if (FormsAreEqual(*it, form))
91 return true;
92 }
93 return false;
94 }
95
96 } // namespace
97
PasswordGenerationAgent(content::RenderView * render_view)98 PasswordGenerationAgent::PasswordGenerationAgent(
99 content::RenderView* render_view)
100 : content::RenderViewObserver(render_view),
101 render_view_(render_view) {
102 render_view_->GetWebView()->setPasswordGeneratorClient(this);
103 }
~PasswordGenerationAgent()104 PasswordGenerationAgent::~PasswordGenerationAgent() {}
105
DidFinishDocumentLoad(blink::WebFrame * frame)106 void PasswordGenerationAgent::DidFinishDocumentLoad(blink::WebFrame* frame) {
107 // In every navigation, the IPC message sent by the password autofill manager
108 // to query whether the current form is blacklisted or not happens when the
109 // document load finishes, so we need to clear previous states here before we
110 // hear back from the browser. We only clear this state on main frame load
111 // as we don't want subframe loads to clear state that we have recieved from
112 // the main frame. Note that we assume there is only one account creation
113 // form, but there could be multiple password forms in each frame.
114 //
115 // TODO(zysxqn): Add stat when local heuristic fires but we don't show the
116 // password generation icon.
117 if (!frame->parent()) {
118 not_blacklisted_password_form_origins_.clear();
119 generation_enabled_forms_.clear();
120 possible_account_creation_form_.reset(new PasswordForm());
121 passwords_.clear();
122 }
123 }
124
DidFinishLoad(blink::WebFrame * frame)125 void PasswordGenerationAgent::DidFinishLoad(blink::WebFrame* frame) {
126 // We don't want to generate passwords if the browser won't store or sync
127 // them.
128 if (!ShouldAnalyzeDocument(frame->document()))
129 return;
130
131 blink::WebVector<blink::WebFormElement> forms;
132 frame->document().forms(forms);
133 for (size_t i = 0; i < forms.size(); ++i) {
134 if (forms[i].isNull())
135 continue;
136
137 // If we can't get a valid PasswordForm, we skip this form because the
138 // the password won't get saved even if we generate it.
139 scoped_ptr<PasswordForm> password_form(
140 CreatePasswordForm(forms[i]));
141 if (!password_form.get()) {
142 DVLOG(2) << "Skipping form as it would not be saved";
143 continue;
144 }
145
146 // Do not generate password for GAIA since it is used to retrieve the
147 // generated paswords.
148 GURL realm(password_form->signon_realm);
149 if (realm == GaiaUrls::GetInstance()->gaia_login_form_realm())
150 continue;
151
152 std::vector<blink::WebInputElement> passwords;
153 if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
154 DVLOG(2) << "Account creation form detected";
155 password_generation::LogPasswordGenerationEvent(
156 password_generation::SIGN_UP_DETECTED);
157 passwords_ = passwords;
158 possible_account_creation_form_.swap(password_form);
159 MaybeShowIcon();
160 // We assume that there is only one account creation field per URL.
161 return;
162 }
163 }
164 password_generation::LogPasswordGenerationEvent(
165 password_generation::NO_SIGN_UP_DETECTED);
166 }
167
ShouldAnalyzeDocument(const blink::WebDocument & document) const168 bool PasswordGenerationAgent::ShouldAnalyzeDocument(
169 const blink::WebDocument& document) const {
170 // Make sure that this security origin is allowed to use password manager.
171 // Generating a password that can't be saved is a bad idea.
172 blink::WebSecurityOrigin origin = document.securityOrigin();
173 if (!origin.canAccessPasswordManager()) {
174 DVLOG(1) << "No PasswordManager access";
175 return false;
176 }
177
178 return true;
179 }
180
openPasswordGenerator(blink::WebInputElement & element)181 void PasswordGenerationAgent::openPasswordGenerator(
182 blink::WebInputElement& element) {
183 blink::WebElement button(element.passwordGeneratorButtonElement());
184 gfx::Rect rect(button.boundsInViewportSpace());
185 scoped_ptr<PasswordForm> password_form(
186 CreatePasswordForm(element.form()));
187 // We should not have shown the icon we can't create a valid PasswordForm.
188 DCHECK(password_form.get());
189
190 Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(),
191 rect,
192 element.maxLength(),
193 *password_form));
194 password_generation::LogPasswordGenerationEvent(
195 password_generation::BUBBLE_SHOWN);
196 }
197
OnMessageReceived(const IPC::Message & message)198 bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) {
199 bool handled = true;
200 IPC_BEGIN_MESSAGE_MAP(PasswordGenerationAgent, message)
201 IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted,
202 OnFormNotBlacklisted)
203 IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted,
204 OnPasswordAccepted)
205 IPC_MESSAGE_HANDLER(AutofillMsg_AccountCreationFormsDetected,
206 OnAccountCreationFormsDetected)
207 IPC_MESSAGE_UNHANDLED(handled = false)
208 IPC_END_MESSAGE_MAP()
209 return handled;
210 }
211
OnFormNotBlacklisted(const PasswordForm & form)212 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
213 not_blacklisted_password_form_origins_.push_back(form.origin);
214 MaybeShowIcon();
215 }
216
OnPasswordAccepted(const base::string16 & password)217 void PasswordGenerationAgent::OnPasswordAccepted(
218 const base::string16& password) {
219 for (std::vector<blink::WebInputElement>::iterator it = passwords_.begin();
220 it != passwords_.end(); ++it) {
221 it->setValue(password);
222 it->setAutofilled(true);
223 // Advance focus to the next input field. We assume password fields in
224 // an account creation form are always adjacent.
225 render_view_->GetWebView()->advanceFocus(false);
226 }
227 }
228
OnAccountCreationFormsDetected(const std::vector<autofill::FormData> & forms)229 void PasswordGenerationAgent::OnAccountCreationFormsDetected(
230 const std::vector<autofill::FormData>& forms) {
231 generation_enabled_forms_.insert(
232 generation_enabled_forms_.end(), forms.begin(), forms.end());
233 MaybeShowIcon();
234 }
235
MaybeShowIcon()236 void PasswordGenerationAgent::MaybeShowIcon() {
237 // Make sure local heuristics have identified a possible account creation
238 // form.
239 if (!possible_account_creation_form_.get() || passwords_.empty()) {
240 DVLOG(2) << "Local hueristics have not detected a possible account "
241 << "creation form";
242 return;
243 }
244
245 // Verify that it's not blacklisted.
246 if (not_blacklisted_password_form_origins_.empty() ||
247 !ContainsURL(not_blacklisted_password_form_origins_,
248 possible_account_creation_form_->origin)) {
249 DVLOG(2) << "Have not recieved confirmation that password form isn't "
250 << "blacklisted";
251 return;
252 }
253
254 // Ensure that we get a ping from Autofill saying that this form is used for
255 // account creation. Note that this message will not be set if this feature
256 // is not enabled. If kNoAutofillNecessaryForPasswordGeneration is set,
257 // skip this check. This switch should only be used in testing environments.
258 if (!CommandLine::ForCurrentProcess()->HasSwitch(
259 switches::kNoAutofillNecessaryForPasswordGeneration) &&
260 (generation_enabled_forms_.empty() ||
261 !ContainsForm(generation_enabled_forms_,
262 *possible_account_creation_form_))) {
263 DVLOG(2) << "Have not recieved confirmation from Autofill that form is used"
264 << " for account creation";
265 return;
266 }
267
268 passwords_[0].passwordGeneratorButtonElement().setAttribute("style",
269 "display:block");
270 password_generation::LogPasswordGenerationEvent(
271 password_generation::ICON_SHOWN);
272 }
273
274 } // namespace autofill
275