OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/renderer/password_autofill_agent.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/message_loop.h" | |
10 #include "base/metrics/histogram.h" | |
11 #include "components/autofill/common/autofill_messages.h" | |
12 #include "components/autofill/common/form_field_data.h" | |
13 #include "components/autofill/common/password_form_fill_data.h" | |
14 #include "components/autofill/renderer/form_autofill_util.h" | |
15 #include "content/public/common/password_form.h" | |
16 #include "content/public/renderer/password_form_conversion_utils.h" | |
17 #include "content/public/renderer/render_view.h" | |
18 #include "third_party/WebKit/public/platform/WebVector.h" | |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" | |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
27 #include "ui/base/keycodes/keyboard_codes.h" | |
28 | |
29 namespace autofill { | |
30 namespace { | |
31 | |
32 // The size above which we stop triggering autocomplete. | |
33 static const size_t kMaximumTextSizeForAutocomplete = 1000; | |
34 | |
35 // Maps element names to the actual elements to simplify form filling. | |
36 typedef std::map<base::string16, WebKit::WebInputElement> | |
37 FormInputElementMap; | |
38 | |
39 // Utility struct for form lookup and autofill. When we parse the DOM to look up | |
40 // a form, in addition to action and origin URL's we have to compare all | |
41 // necessary form elements. To avoid having to look these up again when we want | |
42 // to fill the form, the FindFormElements function stores the pointers | |
43 // in a FormElements* result, referenced to ensure they are safe to use. | |
44 struct FormElements { | |
45 WebKit::WebFormElement form_element; | |
46 FormInputElementMap input_elements; | |
47 }; | |
48 | |
49 typedef std::vector<FormElements*> FormElementsList; | |
50 | |
51 // Helper to search the given form element for the specified input elements | |
52 // in |data|, and add results to |result|. | |
53 static bool FindFormInputElements(WebKit::WebFormElement* fe, | |
54 const FormData& data, | |
55 FormElements* result) { | |
56 // Loop through the list of elements we need to find on the form in order to | |
57 // autofill it. If we don't find any one of them, abort processing this | |
58 // form; it can't be the right one. | |
59 for (size_t j = 0; j < data.fields.size(); j++) { | |
60 WebKit::WebVector<WebKit::WebNode> temp_elements; | |
61 fe->getNamedElements(data.fields[j].name, temp_elements); | |
62 | |
63 // Match the first input element, if any. | |
64 // |getNamedElements| may return non-input elements where the names match, | |
65 // so the results are filtered for input elements. | |
66 // If more than one match is made, then we have ambiguity (due to misuse | |
67 // of "name" attribute) so is it considered not found. | |
68 bool found_input = false; | |
69 for (size_t i = 0; i < temp_elements.size(); ++i) { | |
70 if (temp_elements[i].to<WebKit::WebElement>().hasTagName("input")) { | |
71 // Check for a non-unique match. | |
72 if (found_input) { | |
73 found_input = false; | |
74 break; | |
75 } | |
76 | |
77 // This element matched, add it to our temporary result. It's possible | |
78 // there are multiple matches, but for purposes of identifying the form | |
79 // one suffices and if some function needs to deal with multiple | |
80 // matching elements it can get at them through the FormElement*. | |
81 // Note: This assignment adds a reference to the InputElement. | |
82 result->input_elements[data.fields[j].name] = | |
83 temp_elements[i].to<WebKit::WebInputElement>(); | |
84 found_input = true; | |
85 } | |
86 } | |
87 | |
88 // A required element was not found. This is not the right form. | |
89 // Make sure no input elements from a partially matched form in this | |
90 // iteration remain in the result set. | |
91 // Note: clear will remove a reference from each InputElement. | |
92 if (!found_input) { | |
93 result->input_elements.clear(); | |
94 return false; | |
95 } | |
96 } | |
97 return true; | |
98 } | |
99 | |
100 // Helper to locate form elements identified by |data|. | |
101 void FindFormElements(WebKit::WebView* view, | |
102 const FormData& data, | |
103 FormElementsList* results) { | |
104 DCHECK(view); | |
105 DCHECK(results); | |
106 WebKit::WebFrame* main_frame = view->mainFrame(); | |
107 if (!main_frame) | |
108 return; | |
109 | |
110 GURL::Replacements rep; | |
111 rep.ClearQuery(); | |
112 rep.ClearRef(); | |
113 | |
114 // Loop through each frame. | |
115 for (WebKit::WebFrame* f = main_frame; f; f = f->traverseNext(false)) { | |
116 WebKit::WebDocument doc = f->document(); | |
117 if (!doc.isHTMLDocument()) | |
118 continue; | |
119 | |
120 GURL full_origin(doc.url()); | |
121 if (data.origin != full_origin.ReplaceComponents(rep)) | |
122 continue; | |
123 | |
124 WebKit::WebVector<WebKit::WebFormElement> forms; | |
125 doc.forms(forms); | |
126 | |
127 for (size_t i = 0; i < forms.size(); ++i) { | |
128 WebKit::WebFormElement fe = forms[i]; | |
129 | |
130 GURL full_action(f->document().completeURL(fe.action())); | |
131 if (full_action.is_empty()) { | |
132 // The default action URL is the form's origin. | |
133 full_action = full_origin; | |
134 } | |
135 | |
136 // Action URL must match. | |
137 if (data.action != full_action.ReplaceComponents(rep)) | |
138 continue; | |
139 | |
140 scoped_ptr<FormElements> curr_elements(new FormElements); | |
141 if (!FindFormInputElements(&fe, data, curr_elements.get())) | |
142 continue; | |
143 | |
144 // We found the right element. | |
145 // Note: this assignment adds a reference to |fe|. | |
146 curr_elements->form_element = fe; | |
147 results->push_back(curr_elements.release()); | |
148 } | |
149 } | |
150 } | |
151 | |
152 bool IsElementEditable(const WebKit::WebInputElement& element) { | |
153 return element.isEnabled() && !element.isReadOnly(); | |
154 } | |
155 | |
156 void FillForm(FormElements* fe, const FormData& data) { | |
157 if (!fe->form_element.autoComplete()) | |
158 return; | |
159 | |
160 std::map<base::string16, base::string16> data_map; | |
161 for (size_t i = 0; i < data.fields.size(); i++) | |
162 data_map[data.fields[i].name] = data.fields[i].value; | |
163 | |
164 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
165 it != fe->input_elements.end(); ++it) { | |
166 WebKit::WebInputElement element = it->second; | |
167 // Don't fill a form that has pre-filled values distinct from the ones we | |
168 // want to fill with. | |
169 if (!element.value().isEmpty() && element.value() != data_map[it->first]) | |
170 return; | |
171 } | |
172 | |
173 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
174 it != fe->input_elements.end(); ++it) { | |
175 WebKit::WebInputElement element = it->second; | |
176 if (!IsElementEditable(element)) | |
177 continue; // Don't fill uneditable fields. | |
178 | |
179 // TODO(tkent): Check maxlength and pattern. | |
180 element.setValue(data_map[it->first]); | |
181 element.setAutofilled(true); | |
182 element.dispatchFormControlChangeEvent(); | |
183 } | |
184 } | |
185 | |
186 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { | |
187 if (element->isAutofilled() == autofilled) | |
188 return; | |
189 element->setAutofilled(autofilled); | |
190 // Notify any changeEvent listeners. | |
191 element->dispatchFormControlChangeEvent(); | |
192 } | |
193 | |
194 bool DoUsernamesMatch(const base::string16& username1, | |
195 const base::string16& username2, | |
196 bool exact_match) { | |
197 if (exact_match) | |
198 return username1 == username2; | |
199 return StartsWith(username1, username2, true); | |
200 } | |
201 | |
202 } // namespace | |
203 | |
204 //////////////////////////////////////////////////////////////////////////////// | |
205 // PasswordAutofillAgent, public: | |
206 | |
207 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view) | |
208 : content::RenderViewObserver(render_view), | |
209 disable_popup_(false), | |
210 usernames_usage_(NOTHING_TO_AUTOFILL), | |
211 web_view_(render_view->GetWebView()), | |
212 weak_ptr_factory_(this) { | |
213 } | |
214 | |
215 PasswordAutofillAgent::~PasswordAutofillAgent() { | |
216 } | |
217 | |
218 bool PasswordAutofillAgent::TextFieldDidEndEditing( | |
219 const WebKit::WebInputElement& element) { | |
220 LoginToPasswordInfoMap::const_iterator iter = | |
221 login_to_password_info_.find(element); | |
222 if (iter == login_to_password_info_.end()) | |
223 return false; | |
224 | |
225 const PasswordFormFillData& fill_data = | |
226 iter->second.fill_data; | |
227 | |
228 // If wait_for_username is false, we should have filled when the text changed. | |
229 if (!fill_data.wait_for_username) | |
230 return false; | |
231 | |
232 WebKit::WebInputElement password = iter->second.password_field; | |
233 if (!IsElementEditable(password)) | |
234 return false; | |
235 | |
236 WebKit::WebInputElement username = element; // We need a non-const. | |
237 | |
238 // Do not set selection when ending an editing session, otherwise it can | |
239 // mess with focus. | |
240 FillUserNameAndPassword(&username, &password, fill_data, true, false); | |
241 return true; | |
242 } | |
243 | |
244 bool PasswordAutofillAgent::TextDidChangeInTextField( | |
245 const WebKit::WebInputElement& element) { | |
246 LoginToPasswordInfoMap::const_iterator iter = | |
247 login_to_password_info_.find(element); | |
248 if (iter == login_to_password_info_.end()) | |
249 return false; | |
250 | |
251 // The input text is being changed, so any autofilled password is now | |
252 // outdated. | |
253 WebKit::WebInputElement username = element; // We need a non-const. | |
254 WebKit::WebInputElement password = iter->second.password_field; | |
255 SetElementAutofilled(&username, false); | |
256 if (password.isAutofilled()) { | |
257 password.setValue(base::string16()); | |
258 SetElementAutofilled(&password, false); | |
259 } | |
260 | |
261 // If wait_for_username is true we will fill when the username loses focus. | |
262 if (iter->second.fill_data.wait_for_username) | |
263 return false; | |
264 | |
265 if (!IsElementEditable(element) || | |
266 !element.isText() || | |
267 !element.autoComplete()) { | |
268 return false; | |
269 } | |
270 | |
271 // Don't inline autocomplete if the user is deleting, that would be confusing. | |
272 // But refresh the popup. Note, since this is ours, return true to signal | |
273 // no further processing is required. | |
274 if (iter->second.backspace_pressed_last) { | |
275 ShowSuggestionPopup(iter->second.fill_data, username); | |
276 return true; | |
277 } | |
278 | |
279 WebKit::WebString name = element.nameForAutofill(); | |
280 if (name.isEmpty()) | |
281 return false; // If the field has no name, then we won't have values. | |
282 | |
283 // Don't attempt to autofill with values that are too large. | |
284 if (element.value().length() > kMaximumTextSizeForAutocomplete) | |
285 return false; | |
286 | |
287 // The caret position should have already been updated. | |
288 PerformInlineAutocomplete(element, password, iter->second.fill_data); | |
289 return true; | |
290 } | |
291 | |
292 bool PasswordAutofillAgent::TextFieldHandlingKeyDown( | |
293 const WebKit::WebInputElement& element, | |
294 const WebKit::WebKeyboardEvent& event) { | |
295 // If using the new Autofill UI that lives in the browser, it will handle | |
296 // keypresses before this function. This is not currently an issue but if | |
297 // the keys handled there or here change, this issue may appear. | |
298 | |
299 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element); | |
300 if (iter == login_to_password_info_.end()) | |
301 return false; | |
302 | |
303 int win_key_code = event.windowsKeyCode; | |
304 iter->second.backspace_pressed_last = | |
305 (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE); | |
306 return true; | |
307 } | |
308 | |
309 bool PasswordAutofillAgent::DidAcceptAutofillSuggestion( | |
310 const WebKit::WebNode& node, | |
311 const WebKit::WebString& value) { | |
312 WebKit::WebInputElement input; | |
313 PasswordInfo password; | |
314 if (!FindLoginInfo(node, &input, &password)) | |
315 return false; | |
316 | |
317 // Set the incoming |value| in the text field and |FillUserNameAndPassword| | |
318 // will do the rest. | |
319 input.setValue(value, true); | |
320 return FillUserNameAndPassword(&input, &password.password_field, | |
321 password.fill_data, true, true); | |
322 } | |
323 | |
324 bool PasswordAutofillAgent::DidSelectAutofillSuggestion( | |
325 const WebKit::WebNode& node) { | |
326 WebKit::WebInputElement input; | |
327 PasswordInfo password; | |
328 return FindLoginInfo(node, &input, &password); | |
329 } | |
330 | |
331 bool PasswordAutofillAgent::DidClearAutofillSelection( | |
332 const WebKit::WebNode& node) { | |
333 WebKit::WebInputElement input; | |
334 PasswordInfo password; | |
335 return FindLoginInfo(node, &input, &password); | |
336 } | |
337 | |
338 bool PasswordAutofillAgent::ShowSuggestions( | |
339 const WebKit::WebInputElement& element) { | |
340 LoginToPasswordInfoMap::const_iterator iter = | |
341 login_to_password_info_.find(element); | |
342 if (iter == login_to_password_info_.end()) | |
343 return false; | |
344 | |
345 return ShowSuggestionPopup(iter->second.fill_data, element); | |
346 } | |
347 | |
348 void PasswordAutofillAgent::SendPasswordForms(WebKit::WebFrame* frame, | |
349 bool only_visible) { | |
350 // Make sure that this security origin is allowed to use password manager. | |
351 WebKit::WebSecurityOrigin origin = frame->document().securityOrigin(); | |
352 if (!origin.canAccessPasswordManager()) | |
353 return; | |
354 | |
355 WebKit::WebVector<WebKit::WebFormElement> forms; | |
356 frame->document().forms(forms); | |
357 | |
358 std::vector<content::PasswordForm> password_forms; | |
359 for (size_t i = 0; i < forms.size(); ++i) { | |
360 const WebKit::WebFormElement& form = forms[i]; | |
361 | |
362 // If requested, ignore non-rendered forms, e.g. those styled with | |
363 // display:none. | |
364 if (only_visible && !form.hasNonEmptyBoundingBox()) | |
365 continue; | |
366 | |
367 scoped_ptr<content::PasswordForm> password_form( | |
368 content::CreatePasswordForm(form)); | |
369 if (password_form.get()) | |
370 password_forms.push_back(*password_form); | |
371 } | |
372 | |
373 if (password_forms.empty() && !only_visible) { | |
374 // We need to send the PasswordFormsRendered message regardless of whether | |
375 // there are any forms visible, as this is also the code path that triggers | |
376 // showing the infobar. | |
377 return; | |
378 } | |
379 | |
380 if (only_visible) { | |
381 Send(new AutofillHostMsg_PasswordFormsRendered( | |
382 routing_id(), password_forms)); | |
383 } else { | |
384 Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms)); | |
385 } | |
386 } | |
387 | |
388 bool PasswordAutofillAgent::OnMessageReceived(const IPC::Message& message) { | |
389 bool handled = true; | |
390 IPC_BEGIN_MESSAGE_MAP(PasswordAutofillAgent, message) | |
391 IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm) | |
392 IPC_MESSAGE_UNHANDLED(handled = false) | |
393 IPC_END_MESSAGE_MAP() | |
394 return handled; | |
395 } | |
396 | |
397 void PasswordAutofillAgent::DidStartLoading() { | |
398 if (usernames_usage_ != NOTHING_TO_AUTOFILL) { | |
399 UMA_HISTOGRAM_ENUMERATION("PasswordManager.OtherPossibleUsernamesUsage", | |
400 usernames_usage_, OTHER_POSSIBLE_USERNAMES_MAX); | |
401 usernames_usage_ = NOTHING_TO_AUTOFILL; | |
402 } | |
403 } | |
404 | |
405 void PasswordAutofillAgent::DidFinishDocumentLoad(WebKit::WebFrame* frame) { | |
406 // The |frame| contents have been parsed, but not yet rendered. Let the | |
407 // PasswordManager know that forms are loaded, even though we can't yet tell | |
408 // whether they're visible. | |
409 SendPasswordForms(frame, false); | |
410 } | |
411 | |
412 void PasswordAutofillAgent::DidFinishLoad(WebKit::WebFrame* frame) { | |
413 // The |frame| contents have been rendered. Let the PasswordManager know | |
414 // which of the loaded frames are actually visible to the user. This also | |
415 // triggers the "Save password?" infobar if the user just submitted a password | |
416 // form. | |
417 SendPasswordForms(frame, true); | |
418 } | |
419 | |
420 void PasswordAutofillAgent::FrameDetached(WebKit::WebFrame* frame) { | |
421 FrameClosing(frame); | |
422 } | |
423 | |
424 void PasswordAutofillAgent::FrameWillClose(WebKit::WebFrame* frame) { | |
425 FrameClosing(frame); | |
426 } | |
427 | |
428 void PasswordAutofillAgent::OnFillPasswordForm( | |
429 const PasswordFormFillData& form_data, | |
430 bool disable_popup) { | |
431 disable_popup_ = disable_popup; | |
432 if (usernames_usage_ == NOTHING_TO_AUTOFILL) { | |
433 if (form_data.other_possible_usernames.size()) | |
434 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT; | |
435 else if (usernames_usage_ == NOTHING_TO_AUTOFILL) | |
436 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; | |
437 } | |
438 | |
439 FormElementsList forms; | |
440 // We own the FormElements* in forms. | |
441 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); | |
442 FormElementsList::iterator iter; | |
443 for (iter = forms.begin(); iter != forms.end(); ++iter) { | |
444 scoped_ptr<FormElements> form_elements(*iter); | |
445 | |
446 // If wait_for_username is true, we don't want to initially fill the form | |
447 // until the user types in a valid username. | |
448 if (!form_data.wait_for_username) | |
449 FillForm(form_elements.get(), form_data.basic_data); | |
450 | |
451 // Attach autocomplete listener to enable selecting alternate logins. | |
452 // First, get pointers to username element. | |
453 WebKit::WebInputElement username_element = | |
454 form_elements->input_elements[form_data.basic_data.fields[0].name]; | |
455 | |
456 // Get pointer to password element. (We currently only support single | |
457 // password forms). | |
458 WebKit::WebInputElement password_element = | |
459 form_elements->input_elements[form_data.basic_data.fields[1].name]; | |
460 | |
461 // We might have already filled this form if there are two <form> elements | |
462 // with identical markup. | |
463 if (login_to_password_info_.find(username_element) != | |
464 login_to_password_info_.end()) | |
465 continue; | |
466 | |
467 PasswordInfo password_info; | |
468 password_info.fill_data = form_data; | |
469 password_info.password_field = password_element; | |
470 login_to_password_info_[username_element] = password_info; | |
471 | |
472 FormData form; | |
473 FormFieldData field; | |
474 FindFormAndFieldForInputElement( | |
475 username_element, &form, &field, REQUIRE_NONE); | |
476 Send(new AutofillHostMsg_AddPasswordFormMapping( | |
477 routing_id(), | |
478 field, | |
479 form_data)); | |
480 } | |
481 } | |
482 | |
483 //////////////////////////////////////////////////////////////////////////////// | |
484 // PasswordAutofillAgent, private: | |
485 | |
486 void PasswordAutofillAgent::GetSuggestions( | |
487 const PasswordFormFillData& fill_data, | |
488 const base::string16& input, | |
489 std::vector<base::string16>* suggestions) { | |
490 if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) | |
491 suggestions->push_back(fill_data.basic_data.fields[0].value); | |
492 | |
493 for (PasswordFormFillData::LoginCollection::const_iterator iter = | |
494 fill_data.additional_logins.begin(); | |
495 iter != fill_data.additional_logins.end(); ++iter) { | |
496 if (StartsWith(iter->first, input, false)) | |
497 suggestions->push_back(iter->first); | |
498 } | |
499 | |
500 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = | |
501 fill_data.other_possible_usernames.begin(); | |
502 iter != fill_data.other_possible_usernames.end(); ++iter) { | |
503 for (size_t i = 0; i < iter->second.size(); ++i) { | |
504 if (StartsWith(iter->second[i], input, false)) { | |
505 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SHOWN; | |
506 suggestions->push_back(iter->second[i]); | |
507 } | |
508 } | |
509 } | |
510 } | |
511 | |
512 bool PasswordAutofillAgent::ShowSuggestionPopup( | |
513 const PasswordFormFillData& fill_data, | |
514 const WebKit::WebInputElement& user_input) { | |
515 WebKit::WebFrame* frame = user_input.document().frame(); | |
516 if (!frame) | |
517 return false; | |
518 | |
519 WebKit::WebView* webview = frame->view(); | |
520 if (!webview) | |
521 return false; | |
522 | |
523 std::vector<base::string16> suggestions; | |
524 GetSuggestions(fill_data, user_input.value(), &suggestions); | |
525 | |
526 if (disable_popup_) { | |
527 FormData form; | |
528 FormFieldData field; | |
529 FindFormAndFieldForInputElement( | |
530 user_input, &form, &field, REQUIRE_NONE); | |
531 | |
532 WebKit::WebInputElement selected_element = user_input; | |
533 gfx::Rect bounding_box(selected_element.boundsInViewportSpace()); | |
534 | |
535 float scale = web_view_->pageScaleFactor(); | |
536 gfx::RectF bounding_box_scaled(bounding_box.x() * scale, | |
537 bounding_box.y() * scale, | |
538 bounding_box.width() * scale, | |
539 bounding_box.height() * scale); | |
540 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), | |
541 field, | |
542 bounding_box_scaled, | |
543 suggestions)); | |
544 return !suggestions.empty(); | |
545 } | |
546 | |
547 | |
548 if (suggestions.empty()) { | |
549 webview->hidePopups(); | |
550 return false; | |
551 } | |
552 | |
553 std::vector<base::string16> labels(suggestions.size()); | |
554 std::vector<base::string16> icons(suggestions.size()); | |
555 std::vector<int> ids(suggestions.size(), | |
556 WebKit::WebAutofillClient::MenuItemIDPasswordEntry); | |
557 webview->applyAutofillSuggestions( | |
558 user_input, suggestions, labels, icons, ids); | |
559 return true; | |
560 } | |
561 | |
562 bool PasswordAutofillAgent::FillUserNameAndPassword( | |
563 WebKit::WebInputElement* username_element, | |
564 WebKit::WebInputElement* password_element, | |
565 const PasswordFormFillData& fill_data, | |
566 bool exact_username_match, | |
567 bool set_selection) { | |
568 base::string16 current_username = username_element->value(); | |
569 // username and password will contain the match found if any. | |
570 base::string16 username; | |
571 base::string16 password; | |
572 | |
573 // Look for any suitable matches to current field text. | |
574 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, current_username, | |
575 exact_username_match)) { | |
576 username = fill_data.basic_data.fields[0].value; | |
577 password = fill_data.basic_data.fields[1].value; | |
578 } else { | |
579 // Scan additional logins for a match. | |
580 PasswordFormFillData::LoginCollection::const_iterator iter; | |
581 for (iter = fill_data.additional_logins.begin(); | |
582 iter != fill_data.additional_logins.end(); ++iter) { | |
583 if (DoUsernamesMatch(iter->first, current_username, | |
584 exact_username_match)) { | |
585 username = iter->first; | |
586 password = iter->second; | |
587 break; | |
588 } | |
589 } | |
590 | |
591 // Check possible usernames. | |
592 if (username.empty() && password.empty()) { | |
593 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = | |
594 fill_data.other_possible_usernames.begin(); | |
595 iter != fill_data.other_possible_usernames.end(); ++iter) { | |
596 for (size_t i = 0; i < iter->second.size(); ++i) { | |
597 if (DoUsernamesMatch(iter->second[i], current_username, | |
598 exact_username_match)) { | |
599 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED; | |
600 username = iter->second[i]; | |
601 password = iter->first.password; | |
602 break; | |
603 } | |
604 } | |
605 if (!username.empty() && !password.empty()) | |
606 break; | |
607 } | |
608 } | |
609 } | |
610 if (password.empty()) | |
611 return false; // No match was found. | |
612 | |
613 // Input matches the username, fill in required values. | |
614 username_element->setValue(username); | |
615 | |
616 if (set_selection) { | |
617 username_element->setSelectionRange(current_username.length(), | |
618 username.length()); | |
619 } | |
620 | |
621 SetElementAutofilled(username_element, true); | |
622 if (IsElementEditable(*password_element)) | |
623 password_element->setValue(password); | |
624 SetElementAutofilled(password_element, true); | |
625 return true; | |
626 } | |
627 | |
628 void PasswordAutofillAgent::PerformInlineAutocomplete( | |
629 const WebKit::WebInputElement& username_input, | |
630 const WebKit::WebInputElement& password_input, | |
631 const PasswordFormFillData& fill_data) { | |
632 DCHECK(!fill_data.wait_for_username); | |
633 | |
634 // We need non-const versions of the username and password inputs. | |
635 WebKit::WebInputElement username = username_input; | |
636 WebKit::WebInputElement password = password_input; | |
637 | |
638 // Don't inline autocomplete if the caret is not at the end. | |
639 // TODO(jcivelli): is there a better way to test the caret location? | |
640 if (username.selectionStart() != username.selectionEnd() || | |
641 username.selectionEnd() != static_cast<int>(username.value().length())) { | |
642 return; | |
643 } | |
644 | |
645 // Show the popup with the list of available usernames. | |
646 ShowSuggestionPopup(fill_data, username); | |
647 | |
648 | |
649 #if !defined(OS_ANDROID) | |
650 // Fill the user and password field with the most relevant match. Android | |
651 // only fills in the fields after the user clicks on the suggestion popup. | |
652 FillUserNameAndPassword(&username, &password, fill_data, false, true); | |
653 #endif | |
654 } | |
655 | |
656 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { | |
657 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); | |
658 iter != login_to_password_info_.end();) { | |
659 if (iter->first.document().frame() == frame) | |
660 login_to_password_info_.erase(iter++); | |
661 else | |
662 ++iter; | |
663 } | |
664 } | |
665 | |
666 bool PasswordAutofillAgent::FindLoginInfo(const WebKit::WebNode& node, | |
667 WebKit::WebInputElement* found_input, | |
668 PasswordInfo* found_password) { | |
669 if (!node.isElementNode()) | |
670 return false; | |
671 | |
672 WebKit::WebElement element = node.toConst<WebKit::WebElement>(); | |
673 if (!element.hasTagName("input")) | |
674 return false; | |
675 | |
676 WebKit::WebInputElement input = element.to<WebKit::WebInputElement>(); | |
677 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); | |
678 if (iter == login_to_password_info_.end()) | |
679 return false; | |
680 | |
681 *found_input = input; | |
682 *found_password = iter->second; | |
683 return true; | |
684 } | |
685 | |
686 } // namespace autofill | |
OLD | NEW |