Add diagnostics_writer.cc to the list of files allowed to printf.
[chromium-blink-merge.git] / components / autofill / content / renderer / password_generation_agent.cc
blobe90f1c9c9b79e7f5b34dff22b7846f83b03942bf
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_generation_agent.h"
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/form_autofill_util.h"
12 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
13 #include "components/autofill/core/common/autofill_switches.h"
14 #include "components/autofill/core/common/form_data.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/autofill/core/common/password_generation_util.h"
17 #include "content/public/renderer/render_view.h"
18 #include "google_apis/gaia/gaia_urls.h"
19 #include "third_party/WebKit/public/platform/WebCString.h"
20 #include "third_party/WebKit/public/platform/WebRect.h"
21 #include "third_party/WebKit/public/platform/WebVector.h"
22 #include "third_party/WebKit/public/web/WebDocument.h"
23 #include "third_party/WebKit/public/web/WebFormElement.h"
24 #include "third_party/WebKit/public/web/WebInputElement.h"
25 #include "third_party/WebKit/public/web/WebLocalFrame.h"
26 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
27 #include "third_party/WebKit/public/web/WebView.h"
28 #include "ui/gfx/rect.h"
30 namespace autofill {
32 namespace {
34 // Returns true if we think that this form is for account creation. |passwords|
35 // is filled with the password field(s) in the form.
36 bool GetAccountCreationPasswordFields(
37 const blink::WebFormElement& form,
38 std::vector<blink::WebInputElement>* passwords) {
39 // Grab all of the passwords for the form.
40 blink::WebVector<blink::WebFormControlElement> control_elements;
41 form.getFormControlElements(control_elements);
43 size_t num_input_elements = 0;
44 for (size_t i = 0; i < control_elements.size(); i++) {
45 blink::WebInputElement* input_element =
46 toWebInputElement(&control_elements[i]);
47 // Only pay attention to visible password fields.
48 if (input_element &&
49 input_element->isTextField() &&
50 input_element->hasNonEmptyBoundingBox()) {
51 num_input_elements++;
52 if (input_element->isPasswordField())
53 passwords->push_back(*input_element);
57 // This may be too lenient, but we assume that any form with at least three
58 // input elements where at least one of them is a password is an account
59 // creation form.
60 if (!passwords->empty() && num_input_elements >= 3) {
61 // We trim |passwords| because occasionally there are forms where the
62 // security question answers are put in password fields and we don't want
63 // to fill those.
64 if (passwords->size() > 2)
65 passwords->resize(2);
67 return true;
70 return false;
73 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
74 return std::find(urls.begin(), urls.end(), url) != urls.end();
77 // Returns true if the |form1| is essentially equal to |form2|.
78 bool FormsAreEqual(const autofill::FormData& form1,
79 const PasswordForm& form2) {
80 // TODO(zysxqn): use more signals than just origin to compare.
81 // Note that FormData strips the fragement from the url while PasswordForm
82 // strips both the fragement and the path, so we can't just compare these
83 // two directly.
84 return form1.origin.GetOrigin() == form2.origin.GetOrigin();
87 bool ContainsForm(const std::vector<autofill::FormData>& forms,
88 const PasswordForm& form) {
89 for (std::vector<autofill::FormData>::const_iterator it =
90 forms.begin(); it != forms.end(); ++it) {
91 if (FormsAreEqual(*it, form))
92 return true;
94 return false;
97 } // namespace
99 PasswordGenerationAgent::PasswordGenerationAgent(
100 content::RenderView* render_view)
101 : content::RenderViewObserver(render_view),
102 render_view_(render_view),
103 password_is_generated_(false),
104 password_edited_(false),
105 enabled_(password_generation::IsPasswordGenerationEnabled()) {
106 DVLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled");
108 PasswordGenerationAgent::~PasswordGenerationAgent() {}
110 void PasswordGenerationAgent::DidFinishDocumentLoad(
111 blink::WebLocalFrame* frame) {
112 // In every navigation, the IPC message sent by the password autofill manager
113 // to query whether the current form is blacklisted or not happens when the
114 // document load finishes, so we need to clear previous states here before we
115 // hear back from the browser. We only clear this state on main frame load
116 // as we don't want subframe loads to clear state that we have received from
117 // the main frame. Note that we assume there is only one account creation
118 // form, but there could be multiple password forms in each frame.
119 if (!frame->parent()) {
120 not_blacklisted_password_form_origins_.clear();
121 generation_enabled_forms_.clear();
122 generation_element_.reset();
123 possible_account_creation_form_.reset(new PasswordForm());
124 password_elements_.clear();
125 password_is_generated_ = false;
126 if (password_edited_) {
127 password_generation::LogPasswordGenerationEvent(
128 password_generation::PASSWORD_EDITED);
130 password_edited_ = false;
134 void PasswordGenerationAgent::DidFinishLoad(blink::WebLocalFrame* frame) {
135 if (!enabled_)
136 return;
138 // We don't want to generate passwords if the browser won't store or sync
139 // them.
140 if (!ShouldAnalyzeDocument(frame->document()))
141 return;
143 blink::WebVector<blink::WebFormElement> forms;
144 frame->document().forms(forms);
145 for (size_t i = 0; i < forms.size(); ++i) {
146 if (forms[i].isNull())
147 continue;
149 // If we can't get a valid PasswordForm, we skip this form because the
150 // the password won't get saved even if we generate it.
151 scoped_ptr<PasswordForm> password_form(
152 CreatePasswordForm(forms[i]));
153 if (!password_form.get()) {
154 DVLOG(2) << "Skipping form as it would not be saved";
155 continue;
158 // Do not generate password for GAIA since it is used to retrieve the
159 // generated paswords.
160 GURL realm(password_form->signon_realm);
161 if (realm == GaiaUrls::GetInstance()->gaia_login_form_realm())
162 continue;
164 std::vector<blink::WebInputElement> passwords;
165 if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
166 DVLOG(2) << "Account creation form detected";
167 password_generation::LogPasswordGenerationEvent(
168 password_generation::SIGN_UP_DETECTED);
169 password_elements_ = passwords;
170 possible_account_creation_form_.swap(password_form);
171 DetermineGenerationElement();
172 // We assume that there is only one account creation field per URL.
173 return;
176 password_generation::LogPasswordGenerationEvent(
177 password_generation::NO_SIGN_UP_DETECTED);
180 bool PasswordGenerationAgent::ShouldAnalyzeDocument(
181 const blink::WebDocument& document) const {
182 // Make sure that this security origin is allowed to use password manager.
183 // Generating a password that can't be saved is a bad idea.
184 blink::WebSecurityOrigin origin = document.securityOrigin();
185 if (!origin.canAccessPasswordManager()) {
186 DVLOG(1) << "No PasswordManager access";
187 return false;
190 return true;
193 bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) {
194 bool handled = true;
195 IPC_BEGIN_MESSAGE_MAP(PasswordGenerationAgent, message)
196 IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted,
197 OnFormNotBlacklisted)
198 IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted,
199 OnPasswordAccepted)
200 IPC_MESSAGE_HANDLER(AutofillMsg_AccountCreationFormsDetected,
201 OnAccountCreationFormsDetected)
202 IPC_MESSAGE_UNHANDLED(handled = false)
203 IPC_END_MESSAGE_MAP()
204 return handled;
207 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
208 not_blacklisted_password_form_origins_.push_back(form.origin);
209 DetermineGenerationElement();
212 void PasswordGenerationAgent::OnPasswordAccepted(
213 const base::string16& password) {
214 password_is_generated_ = true;
215 password_generation::LogPasswordGenerationEvent(
216 password_generation::PASSWORD_ACCEPTED);
217 for (std::vector<blink::WebInputElement>::iterator it =
218 password_elements_.begin();
219 it != password_elements_.end(); ++it) {
220 it->setValue(password);
221 it->setAutofilled(true);
222 // Advance focus to the next input field. We assume password fields in
223 // an account creation form are always adjacent.
224 render_view_->GetWebView()->advanceFocus(false);
228 void PasswordGenerationAgent::OnAccountCreationFormsDetected(
229 const std::vector<autofill::FormData>& forms) {
230 generation_enabled_forms_.insert(
231 generation_enabled_forms_.end(), forms.begin(), forms.end());
232 DetermineGenerationElement();
235 void PasswordGenerationAgent::DetermineGenerationElement() {
236 // Make sure local heuristics have identified a possible account creation
237 // form.
238 if (!possible_account_creation_form_.get() || password_elements_.empty()) {
239 DVLOG(2) << "Local hueristics have not detected a possible account "
240 << "creation form";
241 return;
244 if (CommandLine::ForCurrentProcess()->HasSwitch(
245 switches::kLocalHeuristicsOnlyForPasswordGeneration)) {
246 DVLOG(2) << "Bypassing additional checks.";
247 } else if (not_blacklisted_password_form_origins_.empty() ||
248 !ContainsURL(not_blacklisted_password_form_origins_,
249 possible_account_creation_form_->origin)) {
250 DVLOG(2) << "Have not received confirmation that password form isn't "
251 << "blacklisted";
252 return;
253 } else if (generation_enabled_forms_.empty() ||
254 !ContainsForm(generation_enabled_forms_,
255 *possible_account_creation_form_)) {
256 // Note that this message will never be sent if this feature is disabled
257 // (e.g. Password saving is disabled).
258 DVLOG(2) << "Have not received confirmation from Autofill that form is "
259 << "used for account creation";
260 return;
263 DVLOG(2) << "Password generation eligible form found";
264 generation_element_ = password_elements_[0];
265 password_generation::LogPasswordGenerationEvent(
266 password_generation::GENERATION_AVAILABLE);
269 bool PasswordGenerationAgent::FocusedNodeHasChanged(
270 const blink::WebNode& node) {
271 if (!generation_element_.isNull())
272 generation_element_.setShouldRevealPassword(false);
274 if (node.isNull() || !node.isElementNode())
275 return false;
277 const blink::WebElement web_element = node.toConst<blink::WebElement>();
278 if (!web_element.document().frame())
279 return false;
281 const blink::WebInputElement* element = toWebInputElement(&web_element);
282 if (!element || *element != generation_element_)
283 return false;
285 if (password_is_generated_) {
286 generation_element_.setShouldRevealPassword(true);
287 ShowEditingPopup();
288 return true;
291 // Only trigger if the password field is empty.
292 if (!element->isReadOnly() &&
293 element->isEnabled() &&
294 element->value().isEmpty()) {
295 ShowGenerationPopup();
296 return true;
299 return false;
302 bool PasswordGenerationAgent::TextDidChangeInTextField(
303 const blink::WebInputElement& element) {
304 if (element != generation_element_)
305 return false;
307 if (element.value().isEmpty()) {
308 if (password_is_generated_) {
309 // User generated a password and then deleted it.
310 password_generation::LogPasswordGenerationEvent(
311 password_generation::PASSWORD_DELETED);
314 // Do not treat the password as generated.
315 // TODO(gcasto): Set PasswordForm::type in the browser to TYPE_NORMAL.
316 password_is_generated_ = false;
317 generation_element_.setShouldRevealPassword(false);
319 // Offer generation again.
320 ShowGenerationPopup();
321 } else if (!password_is_generated_) {
322 // User has rejected the feature and has started typing a password.
323 HidePopup();
324 } else {
325 password_edited_ = true;
326 // Mirror edits to any confirmation password fields.
327 for (std::vector<blink::WebInputElement>::iterator it =
328 password_elements_.begin();
329 it != password_elements_.end(); ++it) {
330 it->setValue(element.value());
334 return true;
337 void PasswordGenerationAgent::ShowGenerationPopup() {
338 gfx::RectF bounding_box_scaled =
339 GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
340 &generation_element_);
342 Send(new AutofillHostMsg_ShowPasswordGenerationPopup(
343 routing_id(),
344 bounding_box_scaled,
345 generation_element_.maxLength(),
346 *possible_account_creation_form_));
348 password_generation::LogPasswordGenerationEvent(
349 password_generation::GENERATION_POPUP_SHOWN);
352 void PasswordGenerationAgent::ShowEditingPopup() {
353 gfx::RectF bounding_box_scaled =
354 GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
355 &generation_element_);
357 Send(new AutofillHostMsg_ShowPasswordEditingPopup(
358 routing_id(),
359 bounding_box_scaled,
360 *possible_account_creation_form_));
362 password_generation::LogPasswordGenerationEvent(
363 password_generation::EDITING_POPUP_SHOWN);
366 void PasswordGenerationAgent::HidePopup() {
367 Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
370 } // namespace autofill