Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / autofill / content / renderer / password_generation_agent.cc
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/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"
27
28 namespace autofill {
29
30 namespace {
31
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);
40
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);
52     }
53   }
54
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);
64
65     return true;
66   }
67
68   return false;
69 }
70
71 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
72   return std::find(urls.begin(), urls.end(), url) != urls.end();
73 }
74
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();
83 }
84
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;
91   }
92   return false;
93 }
94
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, true /* sendEvents */);
101   }
102 }
103
104 }  // namespace
105
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");
116 }
117 PasswordGenerationAgent::~PasswordGenerationAgent() {}
118
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());
133
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);
141     }
142     password_elements_.clear();
143     password_is_generated_ = false;
144     if (password_edited_) {
145       password_generation::LogPasswordGenerationEvent(
146           password_generation::PASSWORD_EDITED);
147     }
148     password_edited_ = false;
149
150     if (generation_popup_shown_) {
151       password_generation::LogPasswordGenerationEvent(
152           password_generation::GENERATION_POPUP_SHOWN);
153     }
154     generation_popup_shown_ = false;
155
156     if (editing_popup_shown_) {
157       password_generation::LogPasswordGenerationEvent(
158           password_generation::EDITING_POPUP_SHOWN);
159     }
160     editing_popup_shown_ = false;
161   }
162 }
163
164 void PasswordGenerationAgent::OnDynamicFormsSeen(blink::WebLocalFrame* frame) {
165   FindPossibleGenerationForm(frame);
166 }
167
168 void PasswordGenerationAgent::DidFinishLoad(blink::WebLocalFrame* frame) {
169   FindPossibleGenerationForm(frame);
170 }
171
172 void PasswordGenerationAgent::FindPossibleGenerationForm(
173     blink::WebLocalFrame* frame) {
174   if (!enabled_)
175     return;
176
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;
181
182   // If we have already found a signup form for this page, no need to continue.
183   if (!password_elements_.empty())
184     return;
185
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;
191
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;
199     }
200
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;
206
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;
215     }
216   }
217 }
218
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;
227   }
228
229   return true;
230 }
231
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;
244 }
245
246 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
247   not_blacklisted_password_form_origins_.push_back(form.origin);
248   DetermineGenerationElement();
249 }
250
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, true /* sendEvents */);
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);
264   }
265 }
266
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();
272 }
273
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;
281   }
282
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;
300   }
301
302   DVLOG(2) << "Password generation eligible form found";
303   generation_element_ = password_elements_[0];
304   password_generation::LogPasswordGenerationEvent(
305       password_generation::GENERATION_AVAILABLE);
306 }
307
308 bool PasswordGenerationAgent::FocusedNodeHasChanged(
309     const blink::WebNode& node) {
310   if (!generation_element_.isNull())
311     generation_element_.setShouldRevealPassword(false);
312
313   if (node.isNull() || !node.isElementNode())
314     return false;
315
316   const blink::WebElement web_element = node.toConst<blink::WebElement>();
317   if (!web_element.document().frame())
318     return false;
319
320   const blink::WebInputElement* element = toWebInputElement(&web_element);
321   if (!element || *element != generation_element_)
322     return false;
323
324   if (password_is_generated_) {
325     generation_element_.setShouldRevealPassword(true);
326     ShowEditingPopup();
327     return true;
328   }
329
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;
338   }
339
340   return false;
341 }
342
343 bool PasswordGenerationAgent::TextDidChangeInTextField(
344     const blink::WebInputElement& element) {
345   if (element != generation_element_)
346     return false;
347
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_);
354     }
355
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);
360
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();
375   }
376
377   return true;
378 }
379
380 void PasswordGenerationAgent::ShowGenerationPopup() {
381   gfx::RectF bounding_box_scaled =
382       GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
383                            &generation_element_);
384
385   Send(new AutofillHostMsg_ShowPasswordGenerationPopup(
386       routing_id(),
387       bounding_box_scaled,
388       generation_element_.maxLength(),
389       *possible_account_creation_form_));
390
391   generation_popup_shown_ = true;
392 }
393
394 void PasswordGenerationAgent::ShowEditingPopup() {
395   gfx::RectF bounding_box_scaled =
396       GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
397                            &generation_element_);
398
399   Send(new AutofillHostMsg_ShowPasswordEditingPopup(
400       routing_id(),
401       bounding_box_scaled,
402       *possible_account_creation_form_));
403
404   editing_popup_shown_ = true;
405 }
406
407 void PasswordGenerationAgent::HidePopup() {
408   Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
409 }
410
411 }  // namespace autofill