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 "chrome/renderer/autofill/password_autofill_manager.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/message_loop.h" | |
10 #include "chrome/renderer/autofill/form_autofill_util.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 "content/public/common/password_form.h" | |
15 #include "content/public/renderer/password_form_conversion_utils.h" | |
16 #include "content/public/renderer/render_view.h" | |
17 #include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h" | |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" | |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" | |
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
26 #include "ui/base/keycodes/keyboard_codes.h" | |
27 | |
28 namespace { | |
29 | |
30 // The size above which we stop triggering autocomplete. | |
31 static const size_t kMaximumTextSizeForAutocomplete = 1000; | |
32 | |
33 // Maps element names to the actual elements to simplify form filling. | |
34 typedef std::map<string16, WebKit::WebInputElement> | |
35 FormInputElementMap; | |
36 | |
37 // Utility struct for form lookup and autofill. When we parse the DOM to look up | |
38 // a form, in addition to action and origin URL's we have to compare all | |
39 // necessary form elements. To avoid having to look these up again when we want | |
40 // to fill the form, the FindFormElements function stores the pointers | |
41 // in a FormElements* result, referenced to ensure they are safe to use. | |
42 struct FormElements { | |
43 WebKit::WebFormElement form_element; | |
44 FormInputElementMap input_elements; | |
45 }; | |
46 | |
47 typedef std::vector<FormElements*> FormElementsList; | |
48 | |
49 // Helper to search the given form element for the specified input elements | |
50 // in |data|, and add results to |result|. | |
51 static bool FindFormInputElements(WebKit::WebFormElement* fe, | |
52 const FormData& data, | |
53 FormElements* result) { | |
54 // Loop through the list of elements we need to find on the form in order to | |
55 // autofill it. If we don't find any one of them, abort processing this | |
56 // form; it can't be the right one. | |
57 for (size_t j = 0; j < data.fields.size(); j++) { | |
58 WebKit::WebVector<WebKit::WebNode> temp_elements; | |
59 fe->getNamedElements(data.fields[j].name, temp_elements); | |
60 | |
61 // Match the first input element, if any. | |
62 // |getNamedElements| may return non-input elements where the names match, | |
63 // so the results are filtered for input elements. | |
64 // If more than one match is made, then we have ambiguity (due to misuse | |
65 // of "name" attribute) so is it considered not found. | |
66 bool found_input = false; | |
67 for (size_t i = 0; i < temp_elements.size(); ++i) { | |
68 if (temp_elements[i].to<WebKit::WebElement>().hasTagName("input")) { | |
69 // Check for a non-unique match. | |
70 if (found_input) { | |
71 found_input = false; | |
72 break; | |
73 } | |
74 | |
75 // This element matched, add it to our temporary result. It's possible | |
76 // there are multiple matches, but for purposes of identifying the form | |
77 // one suffices and if some function needs to deal with multiple | |
78 // matching elements it can get at them through the FormElement*. | |
79 // Note: This assignment adds a reference to the InputElement. | |
80 result->input_elements[data.fields[j].name] = | |
81 temp_elements[i].to<WebKit::WebInputElement>(); | |
82 found_input = true; | |
83 } | |
84 } | |
85 | |
86 // A required element was not found. This is not the right form. | |
87 // Make sure no input elements from a partially matched form in this | |
88 // iteration remain in the result set. | |
89 // Note: clear will remove a reference from each InputElement. | |
90 if (!found_input) { | |
91 result->input_elements.clear(); | |
92 return false; | |
93 } | |
94 } | |
95 return true; | |
96 } | |
97 | |
98 // Helper to locate form elements identified by |data|. | |
99 void FindFormElements(WebKit::WebView* view, | |
100 const FormData& data, | |
101 FormElementsList* results) { | |
102 DCHECK(view); | |
103 DCHECK(results); | |
104 WebKit::WebFrame* main_frame = view->mainFrame(); | |
105 if (!main_frame) | |
106 return; | |
107 | |
108 GURL::Replacements rep; | |
109 rep.ClearQuery(); | |
110 rep.ClearRef(); | |
111 | |
112 // Loop through each frame. | |
113 for (WebKit::WebFrame* f = main_frame; f; f = f->traverseNext(false)) { | |
114 WebKit::WebDocument doc = f->document(); | |
115 if (!doc.isHTMLDocument()) | |
116 continue; | |
117 | |
118 GURL full_origin(doc.url()); | |
119 if (data.origin != full_origin.ReplaceComponents(rep)) | |
120 continue; | |
121 | |
122 WebKit::WebVector<WebKit::WebFormElement> forms; | |
123 doc.forms(forms); | |
124 | |
125 for (size_t i = 0; i < forms.size(); ++i) { | |
126 WebKit::WebFormElement fe = forms[i]; | |
127 | |
128 GURL full_action(f->document().completeURL(fe.action())); | |
129 if (full_action.is_empty()) { | |
130 // The default action URL is the form's origin. | |
131 full_action = full_origin; | |
132 } | |
133 | |
134 // Action URL must match. | |
135 if (data.action != full_action.ReplaceComponents(rep)) | |
136 continue; | |
137 | |
138 scoped_ptr<FormElements> curr_elements(new FormElements); | |
139 if (!FindFormInputElements(&fe, data, curr_elements.get())) | |
140 continue; | |
141 | |
142 // We found the right element. | |
143 // Note: this assignment adds a reference to |fe|. | |
144 curr_elements->form_element = fe; | |
145 results->push_back(curr_elements.release()); | |
146 } | |
147 } | |
148 } | |
149 | |
150 bool IsElementEditable(const WebKit::WebInputElement& element) { | |
151 return element.isEnabled() && !element.isReadOnly(); | |
152 } | |
153 | |
154 void FillForm(FormElements* fe, const FormData& data) { | |
155 if (!fe->form_element.autoComplete()) | |
156 return; | |
157 | |
158 std::map<string16, string16> data_map; | |
159 for (size_t i = 0; i < data.fields.size(); i++) | |
160 data_map[data.fields[i].name] = data.fields[i].value; | |
161 | |
162 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
163 it != fe->input_elements.end(); ++it) { | |
164 WebKit::WebInputElement element = it->second; | |
165 // Don't fill a form that has pre-filled values distinct from the ones we | |
166 // want to fill with. | |
167 if (!element.value().isEmpty() && element.value() != data_map[it->first]) | |
168 return; | |
169 } | |
170 | |
171 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
172 it != fe->input_elements.end(); ++it) { | |
173 WebKit::WebInputElement element = it->second; | |
174 if (!IsElementEditable(element)) | |
175 continue; // Don't fill uneditable fields. | |
176 | |
177 // TODO(tkent): Check maxlength and pattern. | |
178 element.setValue(data_map[it->first]); | |
179 element.setAutofilled(true); | |
180 element.dispatchFormControlChangeEvent(); | |
181 } | |
182 } | |
183 | |
184 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { | |
185 if (element->isAutofilled() == autofilled) | |
186 return; | |
187 element->setAutofilled(autofilled); | |
188 // Notify any changeEvent listeners. | |
189 element->dispatchFormControlChangeEvent(); | |
190 } | |
191 | |
192 bool DoUsernamesMatch(const string16& username1, | |
193 const string16& username2, | |
194 bool exact_match) { | |
195 if (exact_match) | |
196 return username1 == username2; | |
197 return StartsWith(username1, username2, true); | |
198 } | |
199 | |
200 } // namespace | |
201 | |
202 namespace autofill { | |
203 | |
204 //////////////////////////////////////////////////////////////////////////////// | |
205 // PasswordAutofillManager, public: | |
206 | |
207 PasswordAutofillManager::PasswordAutofillManager( | |
208 content::RenderView* render_view) | |
209 : content::RenderViewObserver(render_view), | |
210 disable_popup_(false), | |
211 web_view_(render_view->GetWebView()), | |
212 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
213 } | |
214 | |
215 PasswordAutofillManager::~PasswordAutofillManager() { | |
216 } | |
217 | |
218 bool PasswordAutofillManager::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 PasswordAutofillManager::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(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 PasswordAutofillManager::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 PasswordAutofillManager::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 PasswordAutofillManager::DidSelectAutofillSuggestion( | |
325 const WebKit::WebNode& node) { | |
326 WebKit::WebInputElement input; | |
327 PasswordInfo password; | |
328 return FindLoginInfo(node, &input, &password); | |
329 } | |
330 | |
331 bool PasswordAutofillManager::DidClearAutofillSelection( | |
332 const WebKit::WebNode& node) { | |
333 WebKit::WebInputElement input; | |
334 PasswordInfo password; | |
335 return FindLoginInfo(node, &input, &password); | |
336 } | |
337 | |
338 void PasswordAutofillManager::SendPasswordForms(WebKit::WebFrame* frame, | |
339 bool only_visible) { | |
340 // Make sure that this security origin is allowed to use password manager. | |
341 WebKit::WebSecurityOrigin origin = frame->document().securityOrigin(); | |
342 if (!origin.canAccessPasswordManager()) | |
343 return; | |
344 | |
345 WebKit::WebVector<WebKit::WebFormElement> forms; | |
346 frame->document().forms(forms); | |
347 | |
348 std::vector<content::PasswordForm> password_forms; | |
349 for (size_t i = 0; i < forms.size(); ++i) { | |
350 const WebKit::WebFormElement& form = forms[i]; | |
351 | |
352 // If requested, ignore non-rendered forms, e.g. those styled with | |
353 // display:none. | |
354 if (only_visible && !form.hasNonEmptyBoundingBox()) | |
355 continue; | |
356 | |
357 scoped_ptr<content::PasswordForm> password_form( | |
358 content::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( | |
372 routing_id(), password_forms)); | |
373 } else { | |
374 Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms)); | |
375 } | |
376 } | |
377 | |
378 bool PasswordAutofillManager::OnMessageReceived(const IPC::Message& message) { | |
379 bool handled = true; | |
380 IPC_BEGIN_MESSAGE_MAP(PasswordAutofillManager, message) | |
381 IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm) | |
382 IPC_MESSAGE_UNHANDLED(handled = false) | |
383 IPC_END_MESSAGE_MAP() | |
384 return handled; | |
385 } | |
386 | |
387 void PasswordAutofillManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) { | |
388 // The |frame| contents have been parsed, but not yet rendered. Let the | |
389 // PasswordManager know that forms are loaded, even though we can't yet tell | |
390 // whether they're visible. | |
391 SendPasswordForms(frame, false); | |
392 } | |
393 | |
394 void PasswordAutofillManager::DidFinishLoad(WebKit::WebFrame* frame) { | |
395 // The |frame| contents have been rendered. Let the PasswordManager know | |
396 // which of the loaded frames are actually visible to the user. This also | |
397 // triggers the "Save password?" infobar if the user just submitted a password | |
398 // form. | |
399 SendPasswordForms(frame, true); | |
400 } | |
401 | |
402 void PasswordAutofillManager::FrameDetached(WebKit::WebFrame* frame) { | |
403 FrameClosing(frame); | |
404 } | |
405 | |
406 void PasswordAutofillManager::FrameWillClose(WebKit::WebFrame* frame) { | |
407 FrameClosing(frame); | |
408 } | |
409 | |
410 | |
411 //////////////////////////////////////////////////////////////////////////////// | |
412 // PageClickListener implementation: | |
413 | |
414 bool PasswordAutofillManager::InputElementClicked( | |
415 const WebKit::WebInputElement& element, | |
416 bool was_focused, | |
417 bool is_focused) { | |
418 // TODO(jcivelli): http://crbug.com/51644 Implement behavior. | |
419 return false; | |
420 } | |
421 | |
422 bool PasswordAutofillManager::InputElementLostFocus() { | |
423 return false; | |
424 } | |
425 | |
426 void PasswordAutofillManager::OnFillPasswordForm( | |
427 const PasswordFormFillData& form_data, | |
428 bool disable_popup) { | |
429 disable_popup_ = disable_popup; | |
430 | |
431 FormElementsList forms; | |
432 // We own the FormElements* in forms. | |
433 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); | |
434 FormElementsList::iterator iter; | |
435 for (iter = forms.begin(); iter != forms.end(); ++iter) { | |
436 scoped_ptr<FormElements> form_elements(*iter); | |
437 | |
438 // If wait_for_username is true, we don't want to initially fill the form | |
439 // until the user types in a valid username. | |
440 if (!form_data.wait_for_username) | |
441 FillForm(form_elements.get(), form_data.basic_data); | |
442 | |
443 // Attach autocomplete listener to enable selecting alternate logins. | |
444 // First, get pointers to username element. | |
445 WebKit::WebInputElement username_element = | |
446 form_elements->input_elements[form_data.basic_data.fields[0].name]; | |
447 | |
448 // Get pointer to password element. (We currently only support single | |
449 // password forms). | |
450 WebKit::WebInputElement password_element = | |
451 form_elements->input_elements[form_data.basic_data.fields[1].name]; | |
452 | |
453 // We might have already filled this form if there are two <form> elements | |
454 // with identical markup. | |
455 if (login_to_password_info_.find(username_element) != | |
456 login_to_password_info_.end()) | |
457 continue; | |
458 | |
459 PasswordInfo password_info; | |
460 password_info.fill_data = form_data; | |
461 password_info.password_field = password_element; | |
462 login_to_password_info_[username_element] = password_info; | |
463 | |
464 FormData form; | |
465 FormFieldData field; | |
466 FindFormAndFieldForInputElement( | |
467 username_element, &form, &field, REQUIRE_NONE); | |
468 Send(new AutofillHostMsg_AddPasswordFormMapping( | |
469 routing_id(), | |
470 field, | |
471 form_data)); | |
472 } | |
473 } | |
474 | |
475 //////////////////////////////////////////////////////////////////////////////// | |
476 // PasswordAutofillManager, private: | |
477 | |
478 void PasswordAutofillManager::GetSuggestions( | |
479 const PasswordFormFillData& fill_data, | |
480 const string16& input, | |
481 std::vector<string16>* suggestions) { | |
482 if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) | |
483 suggestions->push_back(fill_data.basic_data.fields[0].value); | |
484 | |
485 PasswordFormFillData::LoginCollection::const_iterator iter; | |
486 for (iter = fill_data.additional_logins.begin(); | |
487 iter != fill_data.additional_logins.end(); ++iter) { | |
488 if (StartsWith(iter->first, input, false)) | |
489 suggestions->push_back(iter->first); | |
490 } | |
491 } | |
492 | |
493 bool PasswordAutofillManager::ShowSuggestionPopup( | |
494 const PasswordFormFillData& fill_data, | |
495 const WebKit::WebInputElement& user_input) { | |
496 WebKit::WebFrame* frame = user_input.document().frame(); | |
497 if (!frame) | |
498 return false; | |
499 | |
500 WebKit::WebView* webview = frame->view(); | |
501 if (!webview) | |
502 return false; | |
503 | |
504 std::vector<string16> suggestions; | |
505 GetSuggestions(fill_data, user_input.value(), &suggestions); | |
506 | |
507 if (disable_popup_) { | |
508 FormData form; | |
509 FormFieldData field; | |
510 FindFormAndFieldForInputElement( | |
511 user_input, &form, &field, REQUIRE_NONE); | |
512 | |
513 WebKit::WebInputElement selected_element = user_input; | |
514 gfx::Rect bounding_box(selected_element.boundsInViewportSpace()); | |
515 | |
516 float scale = web_view_->pageScaleFactor(); | |
517 gfx::RectF bounding_box_scaled(bounding_box.x() * scale, | |
518 bounding_box.y() * scale, | |
519 bounding_box.width() * scale, | |
520 bounding_box.height() * scale); | |
521 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), | |
522 field, | |
523 bounding_box_scaled, | |
524 suggestions)); | |
525 return !suggestions.empty(); | |
526 } | |
527 | |
528 | |
529 if (suggestions.empty()) { | |
530 webview->hidePopups(); | |
531 return false; | |
532 } | |
533 | |
534 std::vector<string16> labels(suggestions.size()); | |
535 std::vector<string16> icons(suggestions.size()); | |
536 std::vector<int> ids(suggestions.size(), | |
537 WebKit::WebAutofillClient::MenuItemIDPasswordEntry); | |
538 webview->applyAutofillSuggestions( | |
539 user_input, suggestions, labels, icons, ids); | |
540 return true; | |
541 } | |
542 | |
543 bool PasswordAutofillManager::FillUserNameAndPassword( | |
544 WebKit::WebInputElement* username_element, | |
545 WebKit::WebInputElement* password_element, | |
546 const PasswordFormFillData& fill_data, | |
547 bool exact_username_match, | |
548 bool set_selection) { | |
549 string16 current_username = username_element->value(); | |
550 // username and password will contain the match found if any. | |
551 string16 username; | |
552 string16 password; | |
553 | |
554 // Look for any suitable matches to current field text. | |
555 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, current_username, | |
556 exact_username_match)) { | |
557 username = fill_data.basic_data.fields[0].value; | |
558 password = fill_data.basic_data.fields[1].value; | |
559 } else { | |
560 // Scan additional logins for a match. | |
561 PasswordFormFillData::LoginCollection::const_iterator iter; | |
562 for (iter = fill_data.additional_logins.begin(); | |
563 iter != fill_data.additional_logins.end(); ++iter) { | |
564 if (DoUsernamesMatch(iter->first, current_username, | |
565 exact_username_match)) { | |
566 username = iter->first; | |
567 password = iter->second; | |
568 break; | |
569 } | |
570 } | |
571 } | |
572 if (password.empty()) | |
573 return false; // No match was found. | |
574 | |
575 // Input matches the username, fill in required values. | |
576 username_element->setValue(username); | |
577 | |
578 if (set_selection) { | |
579 username_element->setSelectionRange(current_username.length(), | |
580 username.length()); | |
581 } | |
582 | |
583 SetElementAutofilled(username_element, true); | |
584 if (IsElementEditable(*password_element)) | |
585 password_element->setValue(password); | |
586 SetElementAutofilled(password_element, true); | |
587 return true; | |
588 } | |
589 | |
590 void PasswordAutofillManager::PerformInlineAutocomplete( | |
591 const WebKit::WebInputElement& username_input, | |
592 const WebKit::WebInputElement& password_input, | |
593 const PasswordFormFillData& fill_data) { | |
594 DCHECK(!fill_data.wait_for_username); | |
595 | |
596 // We need non-const versions of the username and password inputs. | |
597 WebKit::WebInputElement username = username_input; | |
598 WebKit::WebInputElement password = password_input; | |
599 | |
600 // Don't inline autocomplete if the caret is not at the end. | |
601 // TODO(jcivelli): is there a better way to test the caret location? | |
602 if (username.selectionStart() != username.selectionEnd() || | |
603 username.selectionEnd() != static_cast<int>(username.value().length())) { | |
604 return; | |
605 } | |
606 | |
607 // Show the popup with the list of available usernames. | |
608 ShowSuggestionPopup(fill_data, username); | |
609 | |
610 | |
611 #if !defined(OS_ANDROID) | |
612 // Fill the user and password field with the most relevant match. Android | |
613 // only fills in the fields after the user clicks on the suggestion popup. | |
614 FillUserNameAndPassword(&username, &password, fill_data, false, true); | |
615 #endif | |
616 } | |
617 | |
618 void PasswordAutofillManager::FrameClosing(const WebKit::WebFrame* frame) { | |
619 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); | |
620 iter != login_to_password_info_.end();) { | |
621 if (iter->first.document().frame() == frame) | |
622 login_to_password_info_.erase(iter++); | |
623 else | |
624 ++iter; | |
625 } | |
626 } | |
627 | |
628 bool PasswordAutofillManager::FindLoginInfo( | |
629 const WebKit::WebNode& node, | |
630 WebKit::WebInputElement* found_input, | |
631 PasswordInfo* found_password) { | |
632 if (!node.isElementNode()) | |
633 return false; | |
634 | |
635 WebKit::WebElement element = node.toConst<WebKit::WebElement>(); | |
636 if (!element.hasTagName("input")) | |
637 return false; | |
638 | |
639 WebKit::WebInputElement input = element.to<WebKit::WebInputElement>(); | |
640 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); | |
641 if (iter == login_to_password_info_.end()) | |
642 return false; | |
643 | |
644 *found_input = input; | |
645 *found_password = iter->second; | |
646 return true; | |
647 } | |
648 | |
649 } // namespace autofill | |
OLD | NEW |