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