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