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