• 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_autofill_agent.h"
6 
7 #include "base/bind.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "components/autofill/content/common/autofill_messages.h"
13 #include "components/autofill/content/renderer/form_autofill_util.h"
14 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
15 #include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
16 #include "components/autofill/core/common/form_field_data.h"
17 #include "components/autofill/core/common/password_autofill_util.h"
18 #include "components/autofill/core/common/password_form.h"
19 #include "components/autofill/core/common/password_form_fill_data.h"
20 #include "content/public/common/page_transition_types.h"
21 #include "content/public/renderer/document_state.h"
22 #include "content/public/renderer/navigation_state.h"
23 #include "content/public/renderer/render_view.h"
24 #include "third_party/WebKit/public/platform/WebVector.h"
25 #include "third_party/WebKit/public/web/WebAutofillClient.h"
26 #include "third_party/WebKit/public/web/WebDocument.h"
27 #include "third_party/WebKit/public/web/WebElement.h"
28 #include "third_party/WebKit/public/web/WebFormElement.h"
29 #include "third_party/WebKit/public/web/WebInputEvent.h"
30 #include "third_party/WebKit/public/web/WebLocalFrame.h"
31 #include "third_party/WebKit/public/web/WebNode.h"
32 #include "third_party/WebKit/public/web/WebNodeList.h"
33 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
34 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
35 #include "third_party/WebKit/public/web/WebView.h"
36 #include "ui/events/keycodes/keyboard_codes.h"
37 #include "url/gurl.h"
38 
39 namespace autofill {
40 namespace {
41 
42 // The size above which we stop triggering autocomplete.
43 static const size_t kMaximumTextSizeForAutocomplete = 1000;
44 
45 // Maps element names to the actual elements to simplify form filling.
46 typedef std::map<base::string16, blink::WebInputElement> FormInputElementMap;
47 
48 // Use the shorter name when referencing SavePasswordProgressLogger::StringID
49 // values to spare line breaks. The code provides enough context for that
50 // already.
51 typedef SavePasswordProgressLogger Logger;
52 
53 // Utility struct for form lookup and autofill. When we parse the DOM to look up
54 // a form, in addition to action and origin URL's we have to compare all
55 // necessary form elements. To avoid having to look these up again when we want
56 // to fill the form, the FindFormElements function stores the pointers
57 // in a FormElements* result, referenced to ensure they are safe to use.
58 struct FormElements {
59   blink::WebFormElement form_element;
60   FormInputElementMap input_elements;
61 };
62 
63 typedef std::vector<FormElements*> FormElementsList;
64 
65 // Helper to search the given form element for the specified input elements
66 // in |data|, and add results to |result|.
FindFormInputElements(blink::WebFormElement * fe,const FormData & data,FormElements * result)67 static bool FindFormInputElements(blink::WebFormElement* fe,
68                                   const FormData& data,
69                                   FormElements* result) {
70   // Loop through the list of elements we need to find on the form in order to
71   // autofill it. If we don't find any one of them, abort processing this
72   // form; it can't be the right one.
73   for (size_t j = 0; j < data.fields.size(); j++) {
74     blink::WebVector<blink::WebNode> temp_elements;
75     fe->getNamedElements(data.fields[j].name, temp_elements);
76 
77     // Match the first input element, if any.
78     // |getNamedElements| may return non-input elements where the names match,
79     // so the results are filtered for input elements.
80     // If more than one match is made, then we have ambiguity (due to misuse
81     // of "name" attribute) so is it considered not found.
82     bool found_input = false;
83     for (size_t i = 0; i < temp_elements.size(); ++i) {
84       if (temp_elements[i].to<blink::WebElement>().hasHTMLTagName("input")) {
85         // Check for a non-unique match.
86         if (found_input) {
87           found_input = false;
88           break;
89         }
90 
91         // Only fill saved passwords into password fields and usernames into
92         // text fields.
93         blink::WebInputElement input_element =
94             temp_elements[i].to<blink::WebInputElement>();
95         if (input_element.isPasswordField() !=
96             (data.fields[j].form_control_type == "password"))
97           continue;
98 
99         // This element matched, add it to our temporary result. It's possible
100         // there are multiple matches, but for purposes of identifying the form
101         // one suffices and if some function needs to deal with multiple
102         // matching elements it can get at them through the FormElement*.
103         // Note: This assignment adds a reference to the InputElement.
104         result->input_elements[data.fields[j].name] = input_element;
105         found_input = true;
106       }
107     }
108 
109     // A required element was not found. This is not the right form.
110     // Make sure no input elements from a partially matched form in this
111     // iteration remain in the result set.
112     // Note: clear will remove a reference from each InputElement.
113     if (!found_input) {
114       result->input_elements.clear();
115       return false;
116     }
117   }
118   return true;
119 }
120 
121 // Helper to locate form elements identified by |data|.
FindFormElements(blink::WebView * view,const FormData & data,FormElementsList * results)122 void FindFormElements(blink::WebView* view,
123                       const FormData& data,
124                       FormElementsList* results) {
125   DCHECK(view);
126   DCHECK(results);
127   blink::WebFrame* main_frame = view->mainFrame();
128   if (!main_frame)
129     return;
130 
131   GURL::Replacements rep;
132   rep.ClearQuery();
133   rep.ClearRef();
134 
135   // Loop through each frame.
136   for (blink::WebFrame* f = main_frame; f; f = f->traverseNext(false)) {
137     blink::WebDocument doc = f->document();
138     if (!doc.isHTMLDocument())
139       continue;
140 
141     GURL full_origin(doc.url());
142     if (data.origin != full_origin.ReplaceComponents(rep))
143       continue;
144 
145     blink::WebVector<blink::WebFormElement> forms;
146     doc.forms(forms);
147 
148     for (size_t i = 0; i < forms.size(); ++i) {
149       blink::WebFormElement fe = forms[i];
150 
151       GURL full_action(f->document().completeURL(fe.action()));
152       if (full_action.is_empty()) {
153         // The default action URL is the form's origin.
154         full_action = full_origin;
155       }
156 
157       // Action URL must match.
158       if (data.action != full_action.ReplaceComponents(rep))
159         continue;
160 
161       scoped_ptr<FormElements> curr_elements(new FormElements);
162       if (!FindFormInputElements(&fe, data, curr_elements.get()))
163         continue;
164 
165       // We found the right element.
166       // Note: this assignment adds a reference to |fe|.
167       curr_elements->form_element = fe;
168       results->push_back(curr_elements.release());
169     }
170   }
171 }
172 
IsElementEditable(const blink::WebInputElement & element)173 bool IsElementEditable(const blink::WebInputElement& element) {
174   return element.isEnabled() && !element.isReadOnly();
175 }
176 
DoUsernamesMatch(const base::string16 & username1,const base::string16 & username2,bool exact_match)177 bool DoUsernamesMatch(const base::string16& username1,
178                       const base::string16& username2,
179                       bool exact_match) {
180   if (exact_match)
181     return username1 == username2;
182   return StartsWith(username1, username2, true);
183 }
184 
185 // Returns |true| if the given element is both editable and has permission to be
186 // autocompleted. The latter can be either because there is no
187 // autocomplete='off' set for the element, or because the flag is set to ignore
188 // autocomplete='off'. Otherwise, returns |false|.
IsElementAutocompletable(const blink::WebInputElement & element)189 bool IsElementAutocompletable(const blink::WebInputElement& element) {
190   return IsElementEditable(element) &&
191          (ShouldIgnoreAutocompleteOffForPasswordFields() ||
192           element.autoComplete());
193 }
194 
195 // Returns true if the password specified in |form| is a default value.
PasswordValueIsDefault(const PasswordForm & form,blink::WebFormElement form_element)196 bool PasswordValueIsDefault(const PasswordForm& form,
197                             blink::WebFormElement form_element) {
198   blink::WebVector<blink::WebNode> temp_elements;
199   form_element.getNamedElements(form.password_element, temp_elements);
200 
201   // We are loose in our definition here and will return true if any of the
202   // appropriately named elements match the element to be saved. Currently
203   // we ignore filling passwords where naming is ambigious anyway.
204   for (size_t i = 0; i < temp_elements.size(); ++i) {
205     if (temp_elements[i].to<blink::WebElement>().getAttribute("value") ==
206         form.password_value)
207       return true;
208   }
209   return false;
210 }
211 
212 // Log a message including the name, method and action of |form|.
LogHTMLForm(SavePasswordProgressLogger * logger,SavePasswordProgressLogger::StringID message_id,const blink::WebFormElement & form)213 void LogHTMLForm(SavePasswordProgressLogger* logger,
214                  SavePasswordProgressLogger::StringID message_id,
215                  const blink::WebFormElement& form) {
216   logger->LogHTMLForm(message_id,
217                       form.name().utf8(),
218                       form.method().utf8(),
219                       GURL(form.action().utf8()));
220 }
221 
222 }  // namespace
223 
224 ////////////////////////////////////////////////////////////////////////////////
225 // PasswordAutofillAgent, public:
226 
PasswordAutofillAgent(content::RenderView * render_view)227 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view)
228     : content::RenderViewObserver(render_view),
229       usernames_usage_(NOTHING_TO_AUTOFILL),
230       web_view_(render_view->GetWebView()),
231       logging_state_active_(false),
232       was_username_autofilled_(false),
233       was_password_autofilled_(false),
234       username_selection_start_(0),
235       weak_ptr_factory_(this) {
236   Send(new AutofillHostMsg_PasswordAutofillAgentConstructed(routing_id()));
237 }
238 
~PasswordAutofillAgent()239 PasswordAutofillAgent::~PasswordAutofillAgent() {
240 }
241 
PasswordValueGatekeeper()242 PasswordAutofillAgent::PasswordValueGatekeeper::PasswordValueGatekeeper()
243     : was_user_gesture_seen_(false) {
244 }
245 
~PasswordValueGatekeeper()246 PasswordAutofillAgent::PasswordValueGatekeeper::~PasswordValueGatekeeper() {
247 }
248 
RegisterElement(blink::WebInputElement * element)249 void PasswordAutofillAgent::PasswordValueGatekeeper::RegisterElement(
250     blink::WebInputElement* element) {
251   if (was_user_gesture_seen_)
252     ShowValue(element);
253   else
254     elements_.push_back(*element);
255 }
256 
OnUserGesture()257 void PasswordAutofillAgent::PasswordValueGatekeeper::OnUserGesture() {
258   was_user_gesture_seen_ = true;
259 
260   for (std::vector<blink::WebInputElement>::iterator it = elements_.begin();
261        it != elements_.end();
262        ++it) {
263     ShowValue(&(*it));
264   }
265 
266   elements_.clear();
267 }
268 
Reset()269 void PasswordAutofillAgent::PasswordValueGatekeeper::Reset() {
270   was_user_gesture_seen_ = false;
271   elements_.clear();
272 }
273 
ShowValue(blink::WebInputElement * element)274 void PasswordAutofillAgent::PasswordValueGatekeeper::ShowValue(
275     blink::WebInputElement* element) {
276   if (!element->isNull() && !element->suggestedValue().isNull())
277     element->setValue(element->suggestedValue(), true);
278 }
279 
TextFieldDidEndEditing(const blink::WebInputElement & element)280 bool PasswordAutofillAgent::TextFieldDidEndEditing(
281     const blink::WebInputElement& element) {
282   LoginToPasswordInfoMap::const_iterator iter =
283       login_to_password_info_.find(element);
284   if (iter == login_to_password_info_.end())
285     return false;
286 
287   const PasswordFormFillData& fill_data = iter->second.fill_data;
288 
289   // If wait_for_username is false, we should have filled when the text changed.
290   if (!fill_data.wait_for_username)
291     return false;
292 
293   blink::WebInputElement password = iter->second.password_field;
294   if (!IsElementEditable(password))
295     return false;
296 
297   blink::WebInputElement username = element;  // We need a non-const.
298 
299   // Do not set selection when ending an editing session, otherwise it can
300   // mess with focus.
301   FillUserNameAndPassword(&username,
302                           &password,
303                           fill_data,
304                           true /* exact_username_match */,
305                           false /* set_selection */);
306   return true;
307 }
308 
TextDidChangeInTextField(const blink::WebInputElement & element)309 bool PasswordAutofillAgent::TextDidChangeInTextField(
310     const blink::WebInputElement& element) {
311   LoginToPasswordInfoMap::const_iterator iter =
312       login_to_password_info_.find(element);
313   if (iter == login_to_password_info_.end())
314     return false;
315 
316   // The input text is being changed, so any autofilled password is now
317   // outdated.
318   blink::WebInputElement username = element;  // We need a non-const.
319   username.setAutofilled(false);
320 
321   blink::WebInputElement password = iter->second.password_field;
322   if (password.isAutofilled()) {
323     password.setValue(base::string16(), true);
324     password.setAutofilled(false);
325   }
326 
327   // If wait_for_username is true we will fill when the username loses focus.
328   if (iter->second.fill_data.wait_for_username)
329     return false;
330 
331   if (!element.isText() || !IsElementAutocompletable(element) ||
332       !IsElementAutocompletable(password)) {
333     return false;
334   }
335 
336   // Don't inline autocomplete if the user is deleting, that would be confusing.
337   // But refresh the popup.  Note, since this is ours, return true to signal
338   // no further processing is required.
339   if (iter->second.backspace_pressed_last) {
340     ShowSuggestionPopup(iter->second.fill_data, username);
341     return true;
342   }
343 
344   blink::WebString name = element.nameForAutofill();
345   if (name.isEmpty())
346     return false;  // If the field has no name, then we won't have values.
347 
348   // Don't attempt to autofill with values that are too large.
349   if (element.value().length() > kMaximumTextSizeForAutocomplete)
350     return false;
351 
352   // The caret position should have already been updated.
353   PerformInlineAutocomplete(element, password, iter->second.fill_data);
354   return true;
355 }
356 
TextFieldHandlingKeyDown(const blink::WebInputElement & element,const blink::WebKeyboardEvent & event)357 bool PasswordAutofillAgent::TextFieldHandlingKeyDown(
358     const blink::WebInputElement& element,
359     const blink::WebKeyboardEvent& event) {
360   // If using the new Autofill UI that lives in the browser, it will handle
361   // keypresses before this function. This is not currently an issue but if
362   // the keys handled there or here change, this issue may appear.
363 
364   LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element);
365   if (iter == login_to_password_info_.end())
366     return false;
367 
368   int win_key_code = event.windowsKeyCode;
369   iter->second.backspace_pressed_last =
370       (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE);
371   return true;
372 }
373 
FillSuggestion(const blink::WebNode & node,const blink::WebString & username,const blink::WebString & password)374 bool PasswordAutofillAgent::FillSuggestion(
375     const blink::WebNode& node,
376     const blink::WebString& username,
377     const blink::WebString& password) {
378   blink::WebInputElement username_element;
379   PasswordInfo password_info;
380 
381   if (!FindLoginInfo(node, &username_element, &password_info) ||
382       !IsElementAutocompletable(username_element) ||
383       !IsElementAutocompletable(password_info.password_field)) {
384     return false;
385   }
386 
387   base::string16 current_username = username_element.value();
388   username_element.setValue(username, true);
389   username_element.setAutofilled(true);
390   username_element.setSelectionRange(username.length(), username.length());
391 
392   password_info.password_field.setValue(password, true);
393   password_info.password_field.setAutofilled(true);
394 
395   return true;
396 }
397 
PreviewSuggestion(const blink::WebNode & node,const blink::WebString & username,const blink::WebString & password)398 bool PasswordAutofillAgent::PreviewSuggestion(
399     const blink::WebNode& node,
400     const blink::WebString& username,
401     const blink::WebString& password) {
402   blink::WebInputElement username_element;
403   PasswordInfo password_info;
404 
405   if (!FindLoginInfo(node, &username_element, &password_info) ||
406       !IsElementAutocompletable(username_element) ||
407       !IsElementAutocompletable(password_info.password_field)) {
408     return false;
409   }
410 
411   was_username_autofilled_ = username_element.isAutofilled();
412   username_selection_start_ = username_element.selectionStart();
413   username_element.setSuggestedValue(username);
414   username_element.setAutofilled(true);
415   username_element.setSelectionRange(
416       username_selection_start_,
417       username_element.suggestedValue().length());
418 
419   was_password_autofilled_ = password_info.password_field.isAutofilled();
420   password_info.password_field.setSuggestedValue(password);
421   password_info.password_field.setAutofilled(true);
422 
423   return true;
424 }
425 
DidClearAutofillSelection(const blink::WebNode & node)426 bool PasswordAutofillAgent::DidClearAutofillSelection(
427     const blink::WebNode& node) {
428   blink::WebInputElement username_element;
429   PasswordInfo password_info;
430   if (!FindLoginInfo(node, &username_element, &password_info))
431     return false;
432 
433   ClearPreview(&username_element, &password_info.password_field);
434   return true;
435 }
436 
ShowSuggestions(const blink::WebInputElement & element)437 bool PasswordAutofillAgent::ShowSuggestions(
438     const blink::WebInputElement& element) {
439   LoginToPasswordInfoMap::const_iterator iter =
440       login_to_password_info_.find(element);
441   if (iter == login_to_password_info_.end())
442     return false;
443 
444   // If autocomplete='off' is set on the form elements, no suggestion dialog
445   // should be shown. However, return |true| to indicate that this is a known
446   // password form and that the request to show suggestions has been handled (as
447   // a no-op).
448   if (!IsElementAutocompletable(element) ||
449       !IsElementAutocompletable(iter->second.password_field))
450     return true;
451 
452   return ShowSuggestionPopup(iter->second.fill_data, element);
453 }
454 
OriginCanAccessPasswordManager(const blink::WebSecurityOrigin & origin)455 bool PasswordAutofillAgent::OriginCanAccessPasswordManager(
456     const blink::WebSecurityOrigin& origin) {
457   return origin.canAccessPasswordManager();
458 }
459 
OnDynamicFormsSeen(blink::WebFrame * frame)460 void PasswordAutofillAgent::OnDynamicFormsSeen(blink::WebFrame* frame) {
461   SendPasswordForms(frame, false /* only_visible */);
462 }
463 
FirstUserGestureObserved()464 void PasswordAutofillAgent::FirstUserGestureObserved() {
465   gatekeeper_.OnUserGesture();
466 }
467 
SendPasswordForms(blink::WebFrame * frame,bool only_visible)468 void PasswordAutofillAgent::SendPasswordForms(blink::WebFrame* frame,
469                                               bool only_visible) {
470   scoped_ptr<RendererSavePasswordProgressLogger> logger;
471   if (logging_state_active_) {
472     logger.reset(new RendererSavePasswordProgressLogger(this, routing_id()));
473     logger->LogMessage(Logger::STRING_SEND_PASSWORD_FORMS_METHOD);
474     logger->LogBoolean(Logger::STRING_ONLY_VISIBLE, only_visible);
475   }
476 
477   // Make sure that this security origin is allowed to use password manager.
478   blink::WebSecurityOrigin origin = frame->document().securityOrigin();
479   if (logger) {
480     logger->LogURL(Logger::STRING_SECURITY_ORIGIN,
481                    GURL(origin.toString().utf8()));
482   }
483   if (!OriginCanAccessPasswordManager(origin)) {
484     if (logger) {
485       logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE);
486       logger->LogMessage(Logger::STRING_DECISION_DROP);
487     }
488     return;
489   }
490 
491   // Checks whether the webpage is a redirect page or an empty page.
492   if (IsWebpageEmpty(frame)) {
493     if (logger) {
494       logger->LogMessage(Logger::STRING_WEBPAGE_EMPTY);
495       logger->LogMessage(Logger::STRING_DECISION_DROP);
496     }
497     return;
498   }
499 
500   blink::WebVector<blink::WebFormElement> forms;
501   frame->document().forms(forms);
502   if (logger)
503     logger->LogNumber(Logger::STRING_NUMBER_OF_ALL_FORMS, forms.size());
504 
505   std::vector<PasswordForm> password_forms;
506   for (size_t i = 0; i < forms.size(); ++i) {
507     const blink::WebFormElement& form = forms[i];
508     bool is_form_visible = IsWebNodeVisible(form);
509     if (logger) {
510       LogHTMLForm(logger.get(), Logger::STRING_FORM_FOUND_ON_PAGE, form);
511       logger->LogBoolean(Logger::STRING_FORM_IS_VISIBLE, is_form_visible);
512     }
513 
514     // If requested, ignore non-rendered forms, e.g. those styled with
515     // display:none.
516     if (only_visible && !is_form_visible)
517       continue;
518 
519     scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
520     if (password_form.get()) {
521       if (logger) {
522         logger->LogPasswordForm(Logger::STRING_FORM_IS_PASSWORD,
523                                 *password_form);
524       }
525       password_forms.push_back(*password_form);
526     }
527   }
528 
529   if (password_forms.empty() && !only_visible) {
530     // We need to send the PasswordFormsRendered message regardless of whether
531     // there are any forms visible, as this is also the code path that triggers
532     // showing the infobar.
533     return;
534   }
535 
536   if (only_visible) {
537     Send(new AutofillHostMsg_PasswordFormsRendered(routing_id(),
538                                                    password_forms));
539   } else {
540     Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms));
541   }
542 }
543 
OnMessageReceived(const IPC::Message & message)544 bool PasswordAutofillAgent::OnMessageReceived(const IPC::Message& message) {
545   bool handled = true;
546   IPC_BEGIN_MESSAGE_MAP(PasswordAutofillAgent, message)
547     IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm)
548     IPC_MESSAGE_HANDLER(AutofillMsg_SetLoggingState, OnSetLoggingState)
549     IPC_MESSAGE_UNHANDLED(handled = false)
550   IPC_END_MESSAGE_MAP()
551   return handled;
552 }
553 
DidStartLoading()554 void PasswordAutofillAgent::DidStartLoading() {
555   if (usernames_usage_ != NOTHING_TO_AUTOFILL) {
556     UMA_HISTOGRAM_ENUMERATION("PasswordManager.OtherPossibleUsernamesUsage",
557                               usernames_usage_,
558                               OTHER_POSSIBLE_USERNAMES_MAX);
559     usernames_usage_ = NOTHING_TO_AUTOFILL;
560   }
561 }
562 
DidFinishDocumentLoad(blink::WebLocalFrame * frame)563 void PasswordAutofillAgent::DidFinishDocumentLoad(blink::WebLocalFrame* frame) {
564   // The |frame| contents have been parsed, but not yet rendered.  Let the
565   // PasswordManager know that forms are loaded, even though we can't yet tell
566   // whether they're visible.
567   SendPasswordForms(frame, false);
568 }
569 
DidFinishLoad(blink::WebLocalFrame * frame)570 void PasswordAutofillAgent::DidFinishLoad(blink::WebLocalFrame* frame) {
571   // The |frame| contents have been rendered.  Let the PasswordManager know
572   // which of the loaded frames are actually visible to the user.  This also
573   // triggers the "Save password?" infobar if the user just submitted a password
574   // form.
575   SendPasswordForms(frame, true);
576 }
577 
FrameDetached(blink::WebFrame * frame)578 void PasswordAutofillAgent::FrameDetached(blink::WebFrame* frame) {
579   FrameClosing(frame);
580 }
581 
FrameWillClose(blink::WebFrame * frame)582 void PasswordAutofillAgent::FrameWillClose(blink::WebFrame* frame) {
583   FrameClosing(frame);
584 }
585 
WillSendSubmitEvent(blink::WebLocalFrame * frame,const blink::WebFormElement & form)586 void PasswordAutofillAgent::WillSendSubmitEvent(
587     blink::WebLocalFrame* frame,
588     const blink::WebFormElement& form) {
589   // Some login forms have onSubmit handlers that put a hash of the password
590   // into a hidden field and then clear the password (http://crbug.com/28910).
591   // This method gets called before any of those handlers run, so save away
592   // a copy of the password in case it gets lost.
593   scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
594   if (password_form)
595     provisionally_saved_forms_[frame].reset(password_form.release());
596 }
597 
WillSubmitForm(blink::WebLocalFrame * frame,const blink::WebFormElement & form)598 void PasswordAutofillAgent::WillSubmitForm(blink::WebLocalFrame* frame,
599                                            const blink::WebFormElement& form) {
600   scoped_ptr<RendererSavePasswordProgressLogger> logger;
601   if (logging_state_active_) {
602     logger.reset(new RendererSavePasswordProgressLogger(this, routing_id()));
603     logger->LogMessage(Logger::STRING_WILL_SUBMIT_FORM_METHOD);
604     LogHTMLForm(logger.get(), Logger::STRING_HTML_FORM_FOR_SUBMIT, form);
605   }
606 
607   scoped_ptr<PasswordForm> submitted_form = CreatePasswordForm(form);
608 
609   // If there is a provisionally saved password, copy over the previous
610   // password value so we get the user's typed password, not the value that
611   // may have been transformed for submit.
612   // TODO(gcasto): Do we need to have this action equality check? Is it trying
613   // to prevent accidentally copying over passwords from a different form?
614   if (submitted_form) {
615     if (logger) {
616       logger->LogPasswordForm(Logger::STRING_CREATED_PASSWORD_FORM,
617                               *submitted_form);
618     }
619     if (provisionally_saved_forms_[frame].get() &&
620         submitted_form->action == provisionally_saved_forms_[frame]->action) {
621       if (logger)
622         logger->LogMessage(Logger::STRING_SUBMITTED_PASSWORD_REPLACED);
623       submitted_form->password_value =
624           provisionally_saved_forms_[frame]->password_value;
625     }
626 
627     // Some observers depend on sending this information now instead of when
628     // the frame starts loading. If there are redirects that cause a new
629     // RenderView to be instantiated (such as redirects to the WebStore)
630     // we will never get to finish the load.
631     Send(new AutofillHostMsg_PasswordFormSubmitted(routing_id(),
632                                                    *submitted_form));
633     // Remove reference since we have already submitted this form.
634     provisionally_saved_forms_.erase(frame);
635   } else if (logger) {
636     logger->LogMessage(Logger::STRING_DECISION_DROP);
637   }
638 }
639 
CurrentOrChildFrameWithSavedForms(const blink::WebFrame * current_frame)640 blink::WebFrame* PasswordAutofillAgent::CurrentOrChildFrameWithSavedForms(
641     const blink::WebFrame* current_frame) {
642   for (FrameToPasswordFormMap::const_iterator it =
643            provisionally_saved_forms_.begin();
644        it != provisionally_saved_forms_.end();
645        ++it) {
646     blink::WebFrame* form_frame = it->first;
647     // The check that the returned frame is related to |current_frame| is mainly
648     // for double-checking. There should not be any unrelated frames in
649     // |provisionally_saved_forms_|, because the map is cleared after
650     // navigation. If there are reasons to remove this check in the future and
651     // keep just the first frame found, it might be a good idea to add a UMA
652     // statistic or a similar check on how many frames are here to choose from.
653     if (current_frame == form_frame ||
654         current_frame->findChildByName(form_frame->assignedName())) {
655       return form_frame;
656     }
657   }
658   return NULL;
659 }
660 
DidStartProvisionalLoad(blink::WebLocalFrame * frame)661 void PasswordAutofillAgent::DidStartProvisionalLoad(
662     blink::WebLocalFrame* frame) {
663   scoped_ptr<RendererSavePasswordProgressLogger> logger;
664   if (logging_state_active_) {
665     logger.reset(new RendererSavePasswordProgressLogger(this, routing_id()));
666     logger->LogMessage(Logger::STRING_DID_START_PROVISIONAL_LOAD_METHOD);
667   }
668 
669   if (!frame->parent()) {
670     // If the navigation is not triggered by a user gesture, e.g. by some ajax
671     // callback, then inherit the submitted password form from the previous
672     // state. This fixes the no password save issue for ajax login, tracked in
673     // [http://crbug/43219]. Note that this still fails for sites that use
674     // synchonous XHR as isProcessingUserGesture() will return true.
675     blink::WebFrame* form_frame = CurrentOrChildFrameWithSavedForms(frame);
676     if (logger) {
677       logger->LogBoolean(Logger::STRING_FORM_FRAME_EQ_FRAME,
678                          form_frame == frame);
679     }
680     // Bug fix for crbug.com/368690. isProcessingUserGesture() is false when
681     // the user is performing actions outside the page (e.g. typed url,
682     // history navigation). We don't want to trigger saving in these cases.
683     content::DocumentState* document_state =
684         content::DocumentState::FromDataSource(
685             frame->provisionalDataSource());
686     content::NavigationState* navigation_state =
687         document_state->navigation_state();
688     if (content::PageTransitionIsWebTriggerable(
689             navigation_state->transition_type()) &&
690         !blink::WebUserGestureIndicator::isProcessingUserGesture()) {
691       // If onsubmit has been called, try and save that form.
692       if (provisionally_saved_forms_[form_frame].get()) {
693         if (logger) {
694           logger->LogPasswordForm(
695               Logger::STRING_PROVISIONALLY_SAVED_FORM_FOR_FRAME,
696               *provisionally_saved_forms_[form_frame]);
697         }
698         Send(new AutofillHostMsg_PasswordFormSubmitted(
699             routing_id(), *provisionally_saved_forms_[form_frame]));
700         provisionally_saved_forms_.erase(form_frame);
701       } else {
702         // Loop through the forms on the page looking for one that has been
703         // filled out. If one exists, try and save the credentials.
704         blink::WebVector<blink::WebFormElement> forms;
705         frame->document().forms(forms);
706 
707         bool password_forms_found = false;
708         for (size_t i = 0; i < forms.size(); ++i) {
709           blink::WebFormElement form_element = forms[i];
710           if (logger) {
711             LogHTMLForm(
712                 logger.get(), Logger::STRING_FORM_FOUND_ON_PAGE, form_element);
713           }
714           scoped_ptr<PasswordForm> password_form(
715               CreatePasswordForm(form_element));
716           if (password_form.get() && !password_form->username_value.empty() &&
717               !password_form->password_value.empty() &&
718               !PasswordValueIsDefault(*password_form, form_element)) {
719             password_forms_found = true;
720             if (logger) {
721               logger->LogPasswordForm(
722                   Logger::STRING_PASSWORD_FORM_FOUND_ON_PAGE, *password_form);
723             }
724             Send(new AutofillHostMsg_PasswordFormSubmitted(routing_id(),
725                                                            *password_form));
726           }
727         }
728         if (!password_forms_found && logger) {
729           logger->LogMessage(Logger::STRING_DECISION_DROP);
730         }
731       }
732     }
733     // Clear the whole map during main frame navigation.
734     provisionally_saved_forms_.clear();
735 
736     // This is a new navigation, so require a new user gesture before filling in
737     // passwords.
738     gatekeeper_.Reset();
739   } else {
740     if (logger)
741       logger->LogMessage(Logger::STRING_DECISION_DROP);
742   }
743 }
744 
OnFillPasswordForm(const PasswordFormFillData & form_data)745 void PasswordAutofillAgent::OnFillPasswordForm(
746     const PasswordFormFillData& form_data) {
747   if (usernames_usage_ == NOTHING_TO_AUTOFILL) {
748     if (form_data.other_possible_usernames.size())
749       usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT;
750     else if (usernames_usage_ == NOTHING_TO_AUTOFILL)
751       usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT;
752   }
753 
754   FormElementsList forms;
755   // We own the FormElements* in forms.
756   FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms);
757   FormElementsList::iterator iter;
758   for (iter = forms.begin(); iter != forms.end(); ++iter) {
759     scoped_ptr<FormElements> form_elements(*iter);
760 
761     // Attach autocomplete listener to enable selecting alternate logins.
762     // First, get pointers to username element.
763     blink::WebInputElement username_element =
764         form_elements->input_elements[form_data.basic_data.fields[0].name];
765 
766     // Get pointer to password element. (We currently only support single
767     // password forms).
768     blink::WebInputElement password_element =
769         form_elements->input_elements[form_data.basic_data.fields[1].name];
770 
771     // If wait_for_username is true, we don't want to initially fill the form
772     // until the user types in a valid username.
773     if (!form_data.wait_for_username)
774       FillFormOnPasswordRecieved(form_data, username_element, password_element);
775 
776     // We might have already filled this form if there are two <form> elements
777     // with identical markup.
778     if (login_to_password_info_.find(username_element) !=
779         login_to_password_info_.end())
780       continue;
781 
782     PasswordInfo password_info;
783     password_info.fill_data = form_data;
784     password_info.password_field = password_element;
785     login_to_password_info_[username_element] = password_info;
786 
787     FormData form;
788     FormFieldData field;
789     FindFormAndFieldForFormControlElement(
790         username_element, &form, &field, REQUIRE_NONE);
791     Send(new AutofillHostMsg_AddPasswordFormMapping(
792         routing_id(), field, form_data));
793   }
794 }
795 
OnSetLoggingState(bool active)796 void PasswordAutofillAgent::OnSetLoggingState(bool active) {
797   logging_state_active_ = active;
798 }
799 
800 ////////////////////////////////////////////////////////////////////////////////
801 // PasswordAutofillAgent, private:
802 
GetSuggestions(const PasswordFormFillData & fill_data,const base::string16 & input,std::vector<base::string16> * suggestions,std::vector<base::string16> * realms)803 void PasswordAutofillAgent::GetSuggestions(
804     const PasswordFormFillData& fill_data,
805     const base::string16& input,
806     std::vector<base::string16>* suggestions,
807     std::vector<base::string16>* realms) {
808   if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) {
809     suggestions->push_back(fill_data.basic_data.fields[0].value);
810     realms->push_back(base::UTF8ToUTF16(fill_data.preferred_realm));
811   }
812 
813   for (PasswordFormFillData::LoginCollection::const_iterator iter =
814            fill_data.additional_logins.begin();
815        iter != fill_data.additional_logins.end();
816        ++iter) {
817     if (StartsWith(iter->first, input, false)) {
818       suggestions->push_back(iter->first);
819       realms->push_back(base::UTF8ToUTF16(iter->second.realm));
820     }
821   }
822 
823   for (PasswordFormFillData::UsernamesCollection::const_iterator iter =
824            fill_data.other_possible_usernames.begin();
825        iter != fill_data.other_possible_usernames.end();
826        ++iter) {
827     for (size_t i = 0; i < iter->second.size(); ++i) {
828       if (StartsWith(iter->second[i], input, false)) {
829         usernames_usage_ = OTHER_POSSIBLE_USERNAME_SHOWN;
830         suggestions->push_back(iter->second[i]);
831         realms->push_back(base::UTF8ToUTF16(iter->first.realm));
832       }
833     }
834   }
835 }
836 
ShowSuggestionPopup(const PasswordFormFillData & fill_data,const blink::WebInputElement & user_input)837 bool PasswordAutofillAgent::ShowSuggestionPopup(
838     const PasswordFormFillData& fill_data,
839     const blink::WebInputElement& user_input) {
840   blink::WebFrame* frame = user_input.document().frame();
841   if (!frame)
842     return false;
843 
844   blink::WebView* webview = frame->view();
845   if (!webview)
846     return false;
847 
848   std::vector<base::string16> suggestions;
849   std::vector<base::string16> realms;
850   GetSuggestions(fill_data, user_input.value(), &suggestions, &realms);
851   DCHECK_EQ(suggestions.size(), realms.size());
852 
853   FormData form;
854   FormFieldData field;
855   FindFormAndFieldForFormControlElement(
856       user_input, &form, &field, REQUIRE_NONE);
857 
858   blink::WebInputElement selected_element = user_input;
859   gfx::Rect bounding_box(selected_element.boundsInViewportSpace());
860 
861   float scale = web_view_->pageScaleFactor();
862   gfx::RectF bounding_box_scaled(bounding_box.x() * scale,
863                                  bounding_box.y() * scale,
864                                  bounding_box.width() * scale,
865                                  bounding_box.height() * scale);
866   Send(new AutofillHostMsg_ShowPasswordSuggestions(
867       routing_id(), field, bounding_box_scaled, suggestions, realms));
868   return !suggestions.empty();
869 }
870 
FillFormOnPasswordRecieved(const PasswordFormFillData & fill_data,blink::WebInputElement username_element,blink::WebInputElement password_element)871 void PasswordAutofillAgent::FillFormOnPasswordRecieved(
872     const PasswordFormFillData& fill_data,
873     blink::WebInputElement username_element,
874     blink::WebInputElement password_element) {
875   // Do not fill if the password field is in an iframe.
876   DCHECK(password_element.document().frame());
877   if (password_element.document().frame()->parent())
878     return;
879 
880   if (!ShouldIgnoreAutocompleteOffForPasswordFields() &&
881       !username_element.form().autoComplete())
882     return;
883 
884   // If we can't modify the password, don't try to set the username
885   if (!IsElementAutocompletable(password_element))
886     return;
887 
888   // Try to set the username to the preferred name, but only if the field
889   // can be set and isn't prefilled.
890   if (IsElementAutocompletable(username_element) &&
891       username_element.value().isEmpty()) {
892     // TODO(tkent): Check maxlength and pattern.
893     username_element.setValue(fill_data.basic_data.fields[0].value, true);
894   }
895 
896   // Fill if we have an exact match for the username. Note that this sets
897   // username to autofilled.
898   FillUserNameAndPassword(&username_element,
899                           &password_element,
900                           fill_data,
901                           true /* exact_username_match */,
902                           false /* set_selection */);
903 }
904 
FillUserNameAndPassword(blink::WebInputElement * username_element,blink::WebInputElement * password_element,const PasswordFormFillData & fill_data,bool exact_username_match,bool set_selection)905 bool PasswordAutofillAgent::FillUserNameAndPassword(
906     blink::WebInputElement* username_element,
907     blink::WebInputElement* password_element,
908     const PasswordFormFillData& fill_data,
909     bool exact_username_match,
910     bool set_selection) {
911   base::string16 current_username = username_element->value();
912   // username and password will contain the match found if any.
913   base::string16 username;
914   base::string16 password;
915 
916   // Look for any suitable matches to current field text.
917   if (DoUsernamesMatch(fill_data.basic_data.fields[0].value,
918                        current_username,
919                        exact_username_match)) {
920     username = fill_data.basic_data.fields[0].value;
921     password = fill_data.basic_data.fields[1].value;
922   } else {
923     // Scan additional logins for a match.
924     PasswordFormFillData::LoginCollection::const_iterator iter;
925     for (iter = fill_data.additional_logins.begin();
926          iter != fill_data.additional_logins.end();
927          ++iter) {
928       if (DoUsernamesMatch(
929               iter->first, current_username, exact_username_match)) {
930         username = iter->first;
931         password = iter->second.password;
932         break;
933       }
934     }
935 
936     // Check possible usernames.
937     if (username.empty() && password.empty()) {
938       for (PasswordFormFillData::UsernamesCollection::const_iterator iter =
939                fill_data.other_possible_usernames.begin();
940            iter != fill_data.other_possible_usernames.end();
941            ++iter) {
942         for (size_t i = 0; i < iter->second.size(); ++i) {
943           if (DoUsernamesMatch(
944                   iter->second[i], current_username, exact_username_match)) {
945             usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
946             username = iter->second[i];
947             password = iter->first.password;
948             break;
949           }
950         }
951         if (!username.empty() && !password.empty())
952           break;
953       }
954     }
955   }
956   if (password.empty())
957     return false;  // No match was found.
958 
959   // TODO(tkent): Check maxlength and pattern for both username and password
960   // fields.
961 
962   // Don't fill username if password can't be set.
963   if (!IsElementAutocompletable(*password_element)) {
964     return false;
965   }
966 
967   // Input matches the username, fill in required values.
968   if (IsElementAutocompletable(*username_element)) {
969     username_element->setValue(username, true);
970     username_element->setAutofilled(true);
971 
972     if (set_selection) {
973       username_element->setSelectionRange(current_username.length(),
974                                           username.length());
975     }
976   } else if (current_username != username) {
977     // If the username can't be filled and it doesn't match a saved password
978     // as is, don't autofill a password.
979     return false;
980   }
981 
982   // Wait to fill in the password until a user gesture occurs. This is to make
983   // sure that we do not fill in the DOM with a password until we believe the
984   // user is intentionally interacting with the page.
985   password_element->setSuggestedValue(password);
986   gatekeeper_.RegisterElement(password_element);
987 
988   password_element->setAutofilled(true);
989   return true;
990 }
991 
PerformInlineAutocomplete(const blink::WebInputElement & username_input,const blink::WebInputElement & password_input,const PasswordFormFillData & fill_data)992 void PasswordAutofillAgent::PerformInlineAutocomplete(
993     const blink::WebInputElement& username_input,
994     const blink::WebInputElement& password_input,
995     const PasswordFormFillData& fill_data) {
996   DCHECK(!fill_data.wait_for_username);
997 
998   // We need non-const versions of the username and password inputs.
999   blink::WebInputElement username = username_input;
1000   blink::WebInputElement password = password_input;
1001 
1002   // Don't inline autocomplete if the caret is not at the end.
1003   // TODO(jcivelli): is there a better way to test the caret location?
1004   if (username.selectionStart() != username.selectionEnd() ||
1005       username.selectionEnd() != static_cast<int>(username.value().length())) {
1006     return;
1007   }
1008 
1009   // Show the popup with the list of available usernames.
1010   ShowSuggestionPopup(fill_data, username);
1011 
1012 #if !defined(OS_ANDROID)
1013   // Fill the user and password field with the most relevant match. Android
1014   // only fills in the fields after the user clicks on the suggestion popup.
1015   FillUserNameAndPassword(&username,
1016                           &password,
1017                           fill_data,
1018                           false /* exact_username_match */,
1019                           true /* set_selection */);
1020 #endif
1021 }
1022 
FrameClosing(const blink::WebFrame * frame)1023 void PasswordAutofillAgent::FrameClosing(const blink::WebFrame* frame) {
1024   for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
1025        iter != login_to_password_info_.end();) {
1026     if (iter->first.document().frame() == frame)
1027       login_to_password_info_.erase(iter++);
1028     else
1029       ++iter;
1030   }
1031   for (FrameToPasswordFormMap::iterator iter =
1032            provisionally_saved_forms_.begin();
1033        iter != provisionally_saved_forms_.end();) {
1034     if (iter->first == frame)
1035       provisionally_saved_forms_.erase(iter++);
1036     else
1037       ++iter;
1038   }
1039 }
1040 
FindLoginInfo(const blink::WebNode & node,blink::WebInputElement * found_input,PasswordInfo * found_password)1041 bool PasswordAutofillAgent::FindLoginInfo(const blink::WebNode& node,
1042                                           blink::WebInputElement* found_input,
1043                                           PasswordInfo* found_password) {
1044   if (!node.isElementNode())
1045     return false;
1046 
1047   blink::WebElement element = node.toConst<blink::WebElement>();
1048   if (!element.hasHTMLTagName("input"))
1049     return false;
1050 
1051   blink::WebInputElement input = element.to<blink::WebInputElement>();
1052   LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input);
1053   if (iter == login_to_password_info_.end())
1054     return false;
1055 
1056   *found_input = input;
1057   *found_password = iter->second;
1058   return true;
1059 }
1060 
ClearPreview(blink::WebInputElement * username,blink::WebInputElement * password)1061 void PasswordAutofillAgent::ClearPreview(
1062     blink::WebInputElement* username,
1063     blink::WebInputElement* password) {
1064   if (!username->suggestedValue().isEmpty()) {
1065     username->setSuggestedValue(blink::WebString());
1066     username->setAutofilled(was_username_autofilled_);
1067     username->setSelectionRange(username_selection_start_,
1068                                 username->value().length());
1069   }
1070   if (!password->suggestedValue().isEmpty()) {
1071       password->setSuggestedValue(blink::WebString());
1072       password->setAutofilled(was_password_autofilled_);
1073   }
1074 }
1075 
1076 }  // namespace autofill
1077