• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
29 
30 namespace autofill {
31 
32 namespace {
33 
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.
GetAccountCreationPasswordFields(const blink::WebFormElement & form,std::vector<blink::WebInputElement> * passwords)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);
42 
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);
54     }
55   }
56 
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);
66 
67     return true;
68   }
69 
70   return false;
71 }
72 
ContainsURL(const std::vector<GURL> & urls,const GURL & url)73 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
74   return std::find(urls.begin(), urls.end(), url) != urls.end();
75 }
76 
77 // Returns true if the |form1| is essentially equal to |form2|.
FormsAreEqual(const autofill::FormData & form1,const PasswordForm & 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();
85 }
86 
ContainsForm(const std::vector<autofill::FormData> & forms,const PasswordForm & form)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;
93   }
94   return false;
95 }
96 
97 }  // namespace
98 
PasswordGenerationAgent(content::RenderView * render_view)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");
107 }
~PasswordGenerationAgent()108 PasswordGenerationAgent::~PasswordGenerationAgent() {}
109 
DidFinishDocumentLoad(blink::WebLocalFrame * frame)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);
129     }
130     password_edited_ = false;
131   }
132 }
133 
DidFinishLoad(blink::WebLocalFrame * frame)134 void PasswordGenerationAgent::DidFinishLoad(blink::WebLocalFrame* frame) {
135   if (!enabled_)
136     return;
137 
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;
142 
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;
148 
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;
156     }
157 
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;
163 
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;
174     }
175   }
176   password_generation::LogPasswordGenerationEvent(
177       password_generation::NO_SIGN_UP_DETECTED);
178 }
179 
ShouldAnalyzeDocument(const blink::WebDocument & document) const180 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;
188   }
189 
190   return true;
191 }
192 
OnMessageReceived(const IPC::Message & message)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;
205 }
206 
OnFormNotBlacklisted(const PasswordForm & form)207 void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
208   not_blacklisted_password_form_origins_.push_back(form.origin);
209   DetermineGenerationElement();
210 }
211 
OnPasswordAccepted(const base::string16 & password)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);
225   }
226 }
227 
OnAccountCreationFormsDetected(const std::vector<autofill::FormData> & forms)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();
233 }
234 
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;
242   }
243 
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;
261   }
262 
263   DVLOG(2) << "Password generation eligible form found";
264   generation_element_ = password_elements_[0];
265   password_generation::LogPasswordGenerationEvent(
266       password_generation::GENERATION_AVAILABLE);
267 }
268 
FocusedNodeHasChanged(const blink::WebNode & node)269 bool PasswordGenerationAgent::FocusedNodeHasChanged(
270     const blink::WebNode& node) {
271   if (!generation_element_.isNull())
272     generation_element_.setShouldRevealPassword(false);
273 
274   if (node.isNull() || !node.isElementNode())
275     return false;
276 
277   const blink::WebElement web_element = node.toConst<blink::WebElement>();
278   if (!web_element.document().frame())
279     return false;
280 
281   const blink::WebInputElement* element = toWebInputElement(&web_element);
282   if (!element || *element != generation_element_)
283     return false;
284 
285   if (password_is_generated_) {
286     generation_element_.setShouldRevealPassword(true);
287     ShowEditingPopup();
288     return true;
289   }
290 
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;
297   }
298 
299   return false;
300 }
301 
TextDidChangeInTextField(const blink::WebInputElement & element)302 bool PasswordGenerationAgent::TextDidChangeInTextField(
303     const blink::WebInputElement& element) {
304   if (element != generation_element_)
305     return false;
306 
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);
312     }
313 
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);
318 
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());
331     }
332   }
333 
334   return true;
335 }
336 
ShowGenerationPopup()337 void PasswordGenerationAgent::ShowGenerationPopup() {
338   gfx::RectF bounding_box_scaled =
339       GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
340                            &generation_element_);
341 
342   Send(new AutofillHostMsg_ShowPasswordGenerationPopup(
343       routing_id(),
344       bounding_box_scaled,
345       generation_element_.maxLength(),
346       *possible_account_creation_form_));
347 
348   password_generation::LogPasswordGenerationEvent(
349       password_generation::GENERATION_POPUP_SHOWN);
350 }
351 
ShowEditingPopup()352 void PasswordGenerationAgent::ShowEditingPopup() {
353   gfx::RectF bounding_box_scaled =
354       GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
355                            &generation_element_);
356 
357   Send(new AutofillHostMsg_ShowPasswordEditingPopup(
358       routing_id(),
359       bounding_box_scaled,
360       *possible_account_creation_form_));
361 
362   password_generation::LogPasswordGenerationEvent(
363       password_generation::EDITING_POPUP_SHOWN);
364 }
365 
HidePopup()366 void PasswordGenerationAgent::HidePopup() {
367   Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
368 }
369 
370 }  // namespace autofill
371