Make |track_| in MediaStreamTrack const. and a couple of other cosmetic changes.
[chromium-blink-merge.git] / components / autofill / content / renderer / password_generation_agent.cc
blob48acba0b5ba6ca995b69557f008316e441444f8d
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/WebVector.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebFormElement.h"
22 #include "third_party/WebKit/public/web/WebInputElement.h"
23 #include "third_party/WebKit/public/web/WebLocalFrame.h"
24 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
25 #include "third_party/WebKit/public/web/WebView.h"
26 #include "ui/gfx/rect.h"
28 namespace autofill {
30 namespace {
32 // Returns true if we think that this form is for account creation. |passwords|
33 // is filled with the password field(s) in the form.
34 bool GetAccountCreationPasswordFields(
35 const blink::WebFormElement& form,
36 std::vector<blink::WebInputElement>* passwords) {
37 // Grab all of the passwords for the form.
38 blink::WebVector<blink::WebFormControlElement> control_elements;
39 form.getFormControlElements(control_elements);
41 size_t num_input_elements = 0;
42 for (size_t i = 0; i < control_elements.size(); i++) {
43 blink::WebInputElement* input_element =
44 toWebInputElement(&control_elements[i]);
45 // Only pay attention to visible password fields.
46 if (input_element &&
47 input_element->isTextField() &&
48 input_element->hasNonEmptyBoundingBox()) {
49 num_input_elements++;
50 if (input_element->isPasswordField())
51 passwords->push_back(*input_element);
55 // This may be too lenient, but we assume that any form with at least three
56 // input elements where at least one of them is a password is an account
57 // creation form.
58 if (!passwords->empty() && num_input_elements >= 3) {
59 // We trim |passwords| because occasionally there are forms where the
60 // security question answers are put in password fields and we don't want
61 // to fill those.
62 if (passwords->size() > 2)
63 passwords->resize(2);
65 return true;
68 return false;
71 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
72 return std::find(urls.begin(), urls.end(), url) != urls.end();
75 // Returns true if the |form1| is essentially equal to |form2|.
76 bool FormsAreEqual(const autofill::FormData& form1,
77 const PasswordForm& form2) {
78 // TODO(zysxqn): use more signals than just origin to compare.
79 // Note that FormData strips the fragement from the url while PasswordForm
80 // strips both the fragement and the path, so we can't just compare these
81 // two directly.
82 return form1.origin.GetOrigin() == form2.origin.GetOrigin();
85 bool ContainsForm(const std::vector<autofill::FormData>& forms,
86 const PasswordForm& form) {
87 for (std::vector<autofill::FormData>::const_iterator it =
88 forms.begin(); it != forms.end(); ++it) {
89 if (FormsAreEqual(*it, form))
90 return true;
92 return false;
95 void CopyValueToAllInputElements(
96 const blink::WebString value,
97 std::vector<blink::WebInputElement>* elements) {
98 for (std::vector<blink::WebInputElement>::iterator it = elements->begin();
99 it != elements->end(); ++it) {
100 it->setValue(value);
104 } // namespace
106 PasswordGenerationAgent::PasswordGenerationAgent(
107 content::RenderView* render_view)
108 : content::RenderViewObserver(render_view),
109 render_view_(render_view),
110 password_is_generated_(false),
111 password_edited_(false),
112 generation_popup_shown_(false),
113 editing_popup_shown_(false),
114 enabled_(password_generation::IsPasswordGenerationEnabled()) {
115 DVLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled");
117 PasswordGenerationAgent::~PasswordGenerationAgent() {}
119 void PasswordGenerationAgent::DidFinishDocumentLoad(
120 blink::WebLocalFrame* frame) {
121 // In every navigation, the IPC message sent by the password autofill manager
122 // to query whether the current form is blacklisted or not happens when the
123 // document load finishes, so we need to clear previous states here before we
124 // hear back from the browser. We only clear this state on main frame load
125 // as we don't want subframe loads to clear state that we have received from
126 // the main frame. Note that we assume there is only one account creation
127 // form, but there could be multiple password forms in each frame.
128 if (!frame->parent()) {
129 not_blacklisted_password_form_origins_.clear();
130 generation_enabled_forms_.clear();
131 generation_element_.reset();
132 possible_account_creation_form_.reset(new PasswordForm());
134 // Log statistics after navigation so that we only log once per page.
135 if (password_elements_.empty()) {
136 password_generation::LogPasswordGenerationEvent(
137 password_generation::NO_SIGN_UP_DETECTED);
138 } else {
139 password_generation::LogPasswordGenerationEvent(
140 password_generation::SIGN_UP_DETECTED);
142 password_elements_.clear();
143 password_is_generated_ = false;
144 if (password_edited_) {
145 password_generation::LogPasswordGenerationEvent(
146 password_generation::PASSWORD_EDITED);
148 password_edited_ = false;
150 if (generation_popup_shown_) {
151 password_generation::LogPasswordGenerationEvent(
152 password_generation::GENERATION_POPUP_SHOWN);
154 generation_popup_shown_ = false;
156 if (editing_popup_shown_) {
157 password_generation::LogPasswordGenerationEvent(
158 password_generation::EDITING_POPUP_SHOWN);
160 editing_popup_shown_ = false;
164 void PasswordGenerationAgent::OnDynamicFormsSeen(blink::WebLocalFrame* frame) {
165 FindPossibleGenerationForm(frame);
168 void PasswordGenerationAgent::DidFinishLoad(blink::WebLocalFrame* frame) {
169 FindPossibleGenerationForm(frame);
172 void PasswordGenerationAgent::FindPossibleGenerationForm(
173 blink::WebLocalFrame* frame) {
174 if (!enabled_)
175 return;
177 // We don't want to generate passwords if the browser won't store or sync
178 // them.
179 if (!ShouldAnalyzeDocument(frame->document()))
180 return;
182 // If we have already found a signup form for this page, no need to continue.
183 if (!password_elements_.empty())
184 return;
186 blink::WebVector<blink::WebFormElement> forms;
187 frame->document().forms(forms);
188 for (size_t i = 0; i < forms.size(); ++i) {
189 if (forms[i].isNull())
190 continue;
192 // If we can't get a valid PasswordForm, we skip this form because the
193 // the password won't get saved even if we generate it.
194 scoped_ptr<PasswordForm> password_form(
195 CreatePasswordForm(forms[i]));
196 if (!password_form.get()) {
197 DVLOG(2) << "Skipping form as it would not be saved";
198 continue;
201 // Do not generate password for GAIA since it is used to retrieve the
202 // generated paswords.
203 GURL realm(password_form->signon_realm);
204 if (realm == GaiaUrls::GetInstance()->gaia_login_form_realm())
205 continue;
207 std::vector<blink::WebInputElement> passwords;
208 if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
209 DVLOG(2) << "Account creation form detected";
210 password_elements_ = passwords;
211 possible_account_creation_form_.swap(password_form);
212 DetermineGenerationElement();
213 // We assume that there is only one account creation field per URL.
214 return;
219 bool PasswordGenerationAgent::ShouldAnalyzeDocument(
220 const blink::WebDocument& document) const {
221 // Make sure that this security origin is allowed to use password manager.
222 // Generating a password that can't be saved is a bad idea.
223 blink::WebSecurityOrigin origin = document.securityOrigin();
224 if (!origin.canAccessPasswordManager()) {
225 DVLOG(1) << "No PasswordManager access";
226 return false;
229 return true;
232 bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) {
233 bool handled = true;
234 IPC_BEGIN_MESSAGE_MAP(PasswordGenerationAgent, message)
235 IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted,
236 OnFormNotBlacklisted)
237 IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted,
238 OnPasswordAccepted)
239 IPC_MESSAGE_HANDLER(AutofillMsg_AccountCreationFormsDetected,
240 OnAccountCreationFormsDetected)
241 IPC_MESSAGE_UNHANDLED(handled = false)
242 IPC_END_MESSAGE_MAP()
243 return handled;
246 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
247 not_blacklisted_password_form_origins_.push_back(form.origin);
248 DetermineGenerationElement();
251 void PasswordGenerationAgent::OnPasswordAccepted(
252 const base::string16& password) {
253 password_is_generated_ = true;
254 password_generation::LogPasswordGenerationEvent(
255 password_generation::PASSWORD_ACCEPTED);
256 for (std::vector<blink::WebInputElement>::iterator it =
257 password_elements_.begin();
258 it != password_elements_.end(); ++it) {
259 it->setValue(password);
260 it->setAutofilled(true);
261 // Advance focus to the next input field. We assume password fields in
262 // an account creation form are always adjacent.
263 render_view_->GetWebView()->advanceFocus(false);
267 void PasswordGenerationAgent::OnAccountCreationFormsDetected(
268 const std::vector<autofill::FormData>& forms) {
269 generation_enabled_forms_.insert(
270 generation_enabled_forms_.end(), forms.begin(), forms.end());
271 DetermineGenerationElement();
274 void PasswordGenerationAgent::DetermineGenerationElement() {
275 // Make sure local heuristics have identified a possible account creation
276 // form.
277 if (!possible_account_creation_form_.get() || password_elements_.empty()) {
278 DVLOG(2) << "Local hueristics have not detected a possible account "
279 << "creation form";
280 return;
283 if (CommandLine::ForCurrentProcess()->HasSwitch(
284 switches::kLocalHeuristicsOnlyForPasswordGeneration)) {
285 DVLOG(2) << "Bypassing additional checks.";
286 } else if (not_blacklisted_password_form_origins_.empty() ||
287 !ContainsURL(not_blacklisted_password_form_origins_,
288 possible_account_creation_form_->origin)) {
289 DVLOG(2) << "Have not received confirmation that password form isn't "
290 << "blacklisted";
291 return;
292 } else if (generation_enabled_forms_.empty() ||
293 !ContainsForm(generation_enabled_forms_,
294 *possible_account_creation_form_)) {
295 // Note that this message will never be sent if this feature is disabled
296 // (e.g. Password saving is disabled).
297 DVLOG(2) << "Have not received confirmation from Autofill that form is "
298 << "used for account creation";
299 return;
302 DVLOG(2) << "Password generation eligible form found";
303 generation_element_ = password_elements_[0];
304 password_generation::LogPasswordGenerationEvent(
305 password_generation::GENERATION_AVAILABLE);
308 bool PasswordGenerationAgent::FocusedNodeHasChanged(
309 const blink::WebNode& node) {
310 if (!generation_element_.isNull())
311 generation_element_.setShouldRevealPassword(false);
313 if (node.isNull() || !node.isElementNode())
314 return false;
316 const blink::WebElement web_element = node.toConst<blink::WebElement>();
317 if (!web_element.document().frame())
318 return false;
320 const blink::WebInputElement* element = toWebInputElement(&web_element);
321 if (!element || *element != generation_element_)
322 return false;
324 if (password_is_generated_) {
325 generation_element_.setShouldRevealPassword(true);
326 ShowEditingPopup();
327 return true;
330 // Assume that if the password field has less than kMaximumOfferSize
331 // characters then the user is not finished typing their password and display
332 // the password suggestion.
333 if (!element->isReadOnly() &&
334 element->isEnabled() &&
335 element->value().length() <= kMaximumOfferSize) {
336 ShowGenerationPopup();
337 return true;
340 return false;
343 bool PasswordGenerationAgent::TextDidChangeInTextField(
344 const blink::WebInputElement& element) {
345 if (element != generation_element_)
346 return false;
348 if (element.value().isEmpty()) {
349 if (password_is_generated_) {
350 // User generated a password and then deleted it.
351 password_generation::LogPasswordGenerationEvent(
352 password_generation::PASSWORD_DELETED);
353 CopyValueToAllInputElements(element.value(), &password_elements_);
356 // Do not treat the password as generated.
357 // TODO(gcasto): Set PasswordForm::type in the browser to TYPE_NORMAL.
358 password_is_generated_ = false;
359 generation_element_.setShouldRevealPassword(false);
361 // Offer generation again.
362 ShowGenerationPopup();
363 } else if (password_is_generated_) {
364 password_edited_ = true;
365 // Mirror edits to any confirmation password fields.
366 CopyValueToAllInputElements(element.value(), &password_elements_);
367 } else if (element.value().length() > kMaximumOfferSize) {
368 // User has rejected the feature and has started typing a password.
369 HidePopup();
370 } else {
371 // Password isn't generated and there are fewer than kMaximumOfferSize
372 // characters typed, so keep offering the password. Note this function
373 // will just keep the previous popup if one is already showing.
374 ShowGenerationPopup();
377 return true;
380 void PasswordGenerationAgent::ShowGenerationPopup() {
381 gfx::RectF bounding_box_scaled =
382 GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
383 &generation_element_);
385 Send(new AutofillHostMsg_ShowPasswordGenerationPopup(
386 routing_id(),
387 bounding_box_scaled,
388 generation_element_.maxLength(),
389 *possible_account_creation_form_));
391 generation_popup_shown_ = true;
394 void PasswordGenerationAgent::ShowEditingPopup() {
395 gfx::RectF bounding_box_scaled =
396 GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
397 &generation_element_);
399 Send(new AutofillHostMsg_ShowPasswordEditingPopup(
400 routing_id(),
401 bounding_box_scaled,
402 *possible_account_creation_form_));
404 editing_popup_shown_ = true;
407 void PasswordGenerationAgent::HidePopup() {
408 Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
411 } // namespace autofill