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 30 namespace autofill { 31 namespace { 32 33 // The size above which we stop triggering autocomplete. 34 static const size_t kMaximumTextSizeForAutocomplete = 1000; 35 36 // Maps element names to the actual elements to simplify form filling. 37 typedef 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. 45 struct FormElements { 46 WebKit::WebFormElement form_element; 47 FormInputElementMap input_elements; 48 }; 49 50 typedef 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|. 54 static 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|. 109 void 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 160 bool IsElementEditable(const WebKit::WebInputElement& element) { 161 return element.isEnabled() && !element.isReadOnly(); 162 } 163 164 void 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 // Don't fill forms with uneditable fields or fields with autocomplete 181 // disabled. 182 if (!IsElementEditable(element) || !element.autoComplete()) 183 return; 184 } 185 186 for (FormInputElementMap::iterator it = fe->input_elements.begin(); 187 it != fe->input_elements.end(); ++it) { 188 WebKit::WebInputElement element = it->second; 189 190 // TODO(tkent): Check maxlength and pattern. 191 element.setValue(data_map[it->first]); 192 element.setAutofilled(true); 193 element.dispatchFormControlChangeEvent(); 194 } 195 } 196 197 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { 198 if (element->isAutofilled() == autofilled) 199 return; 200 element->setAutofilled(autofilled); 201 // Notify any changeEvent listeners. 202 element->dispatchFormControlChangeEvent(); 203 } 204 205 bool DoUsernamesMatch(const base::string16& username1, 206 const base::string16& username2, 207 bool exact_match) { 208 if (exact_match) 209 return username1 == username2; 210 return StartsWith(username1, username2, true); 211 } 212 213 } // namespace 214 215 //////////////////////////////////////////////////////////////////////////////// 216 // PasswordAutofillAgent, public: 217 218 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view) 219 : content::RenderViewObserver(render_view), 220 usernames_usage_(NOTHING_TO_AUTOFILL), 221 web_view_(render_view->GetWebView()), 222 weak_ptr_factory_(this) { 223 } 224 225 PasswordAutofillAgent::~PasswordAutofillAgent() { 226 } 227 228 bool PasswordAutofillAgent::TextFieldDidEndEditing( 229 const WebKit::WebInputElement& element) { 230 LoginToPasswordInfoMap::const_iterator iter = 231 login_to_password_info_.find(element); 232 if (iter == login_to_password_info_.end()) 233 return false; 234 235 const PasswordFormFillData& fill_data = 236 iter->second.fill_data; 237 238 // If wait_for_username is false, we should have filled when the text changed. 239 if (!fill_data.wait_for_username) 240 return false; 241 242 WebKit::WebInputElement password = iter->second.password_field; 243 if (!IsElementEditable(password)) 244 return false; 245 246 WebKit::WebInputElement username = element; // We need a non-const. 247 248 // Do not set selection when ending an editing session, otherwise it can 249 // mess with focus. 250 FillUserNameAndPassword(&username, &password, fill_data, true, false); 251 return true; 252 } 253 254 bool PasswordAutofillAgent::TextDidChangeInTextField( 255 const WebKit::WebInputElement& element) { 256 LoginToPasswordInfoMap::const_iterator iter = 257 login_to_password_info_.find(element); 258 if (iter == login_to_password_info_.end()) 259 return false; 260 261 // The input text is being changed, so any autofilled password is now 262 // outdated. 263 WebKit::WebInputElement username = element; // We need a non-const. 264 WebKit::WebInputElement password = iter->second.password_field; 265 SetElementAutofilled(&username, false); 266 if (password.isAutofilled()) { 267 password.setValue(base::string16()); 268 SetElementAutofilled(&password, false); 269 } 270 271 // If wait_for_username is true we will fill when the username loses focus. 272 if (iter->second.fill_data.wait_for_username) 273 return false; 274 275 if (!IsElementEditable(element) || 276 !element.isText() || 277 !element.autoComplete()) { 278 return false; 279 } 280 281 // Don't inline autocomplete if the user is deleting, that would be confusing. 282 // But refresh the popup. Note, since this is ours, return true to signal 283 // no further processing is required. 284 if (iter->second.backspace_pressed_last) { 285 ShowSuggestionPopup(iter->second.fill_data, username); 286 return true; 287 } 288 289 WebKit::WebString name = element.nameForAutofill(); 290 if (name.isEmpty()) 291 return false; // If the field has no name, then we won't have values. 292 293 // Don't attempt to autofill with values that are too large. 294 if (element.value().length() > kMaximumTextSizeForAutocomplete) 295 return false; 296 297 // The caret position should have already been updated. 298 PerformInlineAutocomplete(element, password, iter->second.fill_data); 299 return true; 300 } 301 302 bool PasswordAutofillAgent::TextFieldHandlingKeyDown( 303 const WebKit::WebInputElement& element, 304 const WebKit::WebKeyboardEvent& event) { 305 // If using the new Autofill UI that lives in the browser, it will handle 306 // keypresses before this function. This is not currently an issue but if 307 // the keys handled there or here change, this issue may appear. 308 309 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element); 310 if (iter == login_to_password_info_.end()) 311 return false; 312 313 int win_key_code = event.windowsKeyCode; 314 iter->second.backspace_pressed_last = 315 (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE); 316 return true; 317 } 318 319 bool PasswordAutofillAgent::DidAcceptAutofillSuggestion( 320 const WebKit::WebNode& node, 321 const WebKit::WebString& value) { 322 WebKit::WebInputElement input; 323 PasswordInfo password; 324 if (!FindLoginInfo(node, &input, &password)) 325 return false; 326 327 // Set the incoming |value| in the text field and |FillUserNameAndPassword| 328 // will do the rest. 329 input.setValue(value, true); 330 return FillUserNameAndPassword(&input, &password.password_field, 331 password.fill_data, true, true); 332 } 333 334 bool PasswordAutofillAgent::DidClearAutofillSelection( 335 const WebKit::WebNode& node) { 336 WebKit::WebInputElement input; 337 PasswordInfo password; 338 return FindLoginInfo(node, &input, &password); 339 } 340 341 bool PasswordAutofillAgent::ShowSuggestions( 342 const WebKit::WebInputElement& element) { 343 LoginToPasswordInfoMap::const_iterator iter = 344 login_to_password_info_.find(element); 345 if (iter == login_to_password_info_.end()) 346 return false; 347 348 return ShowSuggestionPopup(iter->second.fill_data, element); 349 } 350 351 void PasswordAutofillAgent::SendPasswordForms(WebKit::WebFrame* frame, 352 bool only_visible) { 353 // Make sure that this security origin is allowed to use password manager. 354 WebKit::WebSecurityOrigin origin = frame->document().securityOrigin(); 355 if (!origin.canAccessPasswordManager()) 356 return; 357 358 WebKit::WebVector<WebKit::WebFormElement> forms; 359 frame->document().forms(forms); 360 361 std::vector<content::PasswordForm> password_forms; 362 for (size_t i = 0; i < forms.size(); ++i) { 363 const WebKit::WebFormElement& form = forms[i]; 364 365 // If requested, ignore non-rendered forms, e.g. those styled with 366 // display:none. 367 if (only_visible && !form.hasNonEmptyBoundingBox()) 368 continue; 369 370 scoped_ptr<content::PasswordForm> password_form( 371 content::CreatePasswordForm(form)); 372 if (password_form.get()) 373 password_forms.push_back(*password_form); 374 } 375 376 if (password_forms.empty() && !only_visible) { 377 // We need to send the PasswordFormsRendered message regardless of whether 378 // there are any forms visible, as this is also the code path that triggers 379 // showing the infobar. 380 return; 381 } 382 383 if (only_visible) { 384 Send(new AutofillHostMsg_PasswordFormsRendered( 385 routing_id(), password_forms)); 386 } else { 387 Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms)); 388 } 389 } 390 391 bool PasswordAutofillAgent::OnMessageReceived(const IPC::Message& message) { 392 bool handled = true; 393 IPC_BEGIN_MESSAGE_MAP(PasswordAutofillAgent, message) 394 IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm) 395 IPC_MESSAGE_UNHANDLED(handled = false) 396 IPC_END_MESSAGE_MAP() 397 return handled; 398 } 399 400 void PasswordAutofillAgent::DidStartLoading() { 401 if (usernames_usage_ != NOTHING_TO_AUTOFILL) { 402 UMA_HISTOGRAM_ENUMERATION("PasswordManager.OtherPossibleUsernamesUsage", 403 usernames_usage_, OTHER_POSSIBLE_USERNAMES_MAX); 404 usernames_usage_ = NOTHING_TO_AUTOFILL; 405 } 406 } 407 408 void PasswordAutofillAgent::DidFinishDocumentLoad(WebKit::WebFrame* frame) { 409 // The |frame| contents have been parsed, but not yet rendered. Let the 410 // PasswordManager know that forms are loaded, even though we can't yet tell 411 // whether they're visible. 412 SendPasswordForms(frame, false); 413 } 414 415 void PasswordAutofillAgent::DidFinishLoad(WebKit::WebFrame* frame) { 416 // The |frame| contents have been rendered. Let the PasswordManager know 417 // which of the loaded frames are actually visible to the user. This also 418 // triggers the "Save password?" infobar if the user just submitted a password 419 // form. 420 SendPasswordForms(frame, true); 421 } 422 423 void PasswordAutofillAgent::FrameDetached(WebKit::WebFrame* frame) { 424 FrameClosing(frame); 425 } 426 427 void PasswordAutofillAgent::FrameWillClose(WebKit::WebFrame* frame) { 428 FrameClosing(frame); 429 } 430 431 void PasswordAutofillAgent::OnFillPasswordForm( 432 const PasswordFormFillData& form_data) { 433 if (usernames_usage_ == NOTHING_TO_AUTOFILL) { 434 if (form_data.other_possible_usernames.size()) 435 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT; 436 else if (usernames_usage_ == NOTHING_TO_AUTOFILL) 437 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; 438 } 439 440 FormElementsList forms; 441 // We own the FormElements* in forms. 442 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); 443 FormElementsList::iterator iter; 444 for (iter = forms.begin(); iter != forms.end(); ++iter) { 445 scoped_ptr<FormElements> form_elements(*iter); 446 447 // If wait_for_username is true, we don't want to initially fill the form 448 // until the user types in a valid username. 449 if (!form_data.wait_for_username) 450 FillForm(form_elements.get(), form_data.basic_data); 451 452 // Attach autocomplete listener to enable selecting alternate logins. 453 // First, get pointers to username element. 454 WebKit::WebInputElement username_element = 455 form_elements->input_elements[form_data.basic_data.fields[0].name]; 456 457 // Get pointer to password element. (We currently only support single 458 // password forms). 459 WebKit::WebInputElement password_element = 460 form_elements->input_elements[form_data.basic_data.fields[1].name]; 461 462 // We might have already filled this form if there are two <form> elements 463 // with identical markup. 464 if (login_to_password_info_.find(username_element) != 465 login_to_password_info_.end()) 466 continue; 467 468 PasswordInfo password_info; 469 password_info.fill_data = form_data; 470 password_info.password_field = password_element; 471 login_to_password_info_[username_element] = password_info; 472 473 FormData form; 474 FormFieldData field; 475 FindFormAndFieldForInputElement( 476 username_element, &form, &field, REQUIRE_NONE); 477 Send(new AutofillHostMsg_AddPasswordFormMapping( 478 routing_id(), 479 field, 480 form_data)); 481 } 482 } 483 484 //////////////////////////////////////////////////////////////////////////////// 485 // PasswordAutofillAgent, private: 486 487 void PasswordAutofillAgent::GetSuggestions( 488 const PasswordFormFillData& fill_data, 489 const base::string16& input, 490 std::vector<base::string16>* suggestions, 491 std::vector<base::string16>* realms) { 492 if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) { 493 suggestions->push_back(fill_data.basic_data.fields[0].value); 494 realms->push_back(UTF8ToUTF16(fill_data.preferred_realm)); 495 } 496 497 for (PasswordFormFillData::LoginCollection::const_iterator iter = 498 fill_data.additional_logins.begin(); 499 iter != fill_data.additional_logins.end(); ++iter) { 500 if (StartsWith(iter->first, input, false)) { 501 suggestions->push_back(iter->first); 502 realms->push_back(UTF8ToUTF16(iter->second.realm)); 503 } 504 } 505 506 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = 507 fill_data.other_possible_usernames.begin(); 508 iter != fill_data.other_possible_usernames.end(); ++iter) { 509 for (size_t i = 0; i < iter->second.size(); ++i) { 510 if (StartsWith(iter->second[i], input, false)) { 511 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SHOWN; 512 suggestions->push_back(iter->second[i]); 513 realms->push_back(UTF8ToUTF16(iter->first.realm)); 514 } 515 } 516 } 517 } 518 519 bool PasswordAutofillAgent::ShowSuggestionPopup( 520 const PasswordFormFillData& fill_data, 521 const WebKit::WebInputElement& user_input) { 522 WebKit::WebFrame* frame = user_input.document().frame(); 523 if (!frame) 524 return false; 525 526 WebKit::WebView* webview = frame->view(); 527 if (!webview) 528 return false; 529 530 std::vector<base::string16> suggestions; 531 std::vector<base::string16> realms; 532 GetSuggestions(fill_data, user_input.value(), &suggestions, &realms); 533 DCHECK_EQ(suggestions.size(), realms.size()); 534 535 FormData form; 536 FormFieldData field; 537 FindFormAndFieldForInputElement( 538 user_input, &form, &field, REQUIRE_NONE); 539 540 WebKit::WebInputElement selected_element = user_input; 541 gfx::Rect bounding_box(selected_element.boundsInViewportSpace()); 542 543 float scale = web_view_->pageScaleFactor(); 544 gfx::RectF bounding_box_scaled(bounding_box.x() * scale, 545 bounding_box.y() * scale, 546 bounding_box.width() * scale, 547 bounding_box.height() * scale); 548 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), 549 field, 550 bounding_box_scaled, 551 suggestions, 552 realms)); 553 return !suggestions.empty(); 554 } 555 556 bool PasswordAutofillAgent::FillUserNameAndPassword( 557 WebKit::WebInputElement* username_element, 558 WebKit::WebInputElement* password_element, 559 const PasswordFormFillData& fill_data, 560 bool exact_username_match, 561 bool set_selection) { 562 base::string16 current_username = username_element->value(); 563 // username and password will contain the match found if any. 564 base::string16 username; 565 base::string16 password; 566 567 // Look for any suitable matches to current field text. 568 if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, current_username, 569 exact_username_match)) { 570 username = fill_data.basic_data.fields[0].value; 571 password = fill_data.basic_data.fields[1].value; 572 } else { 573 // Scan additional logins for a match. 574 PasswordFormFillData::LoginCollection::const_iterator iter; 575 for (iter = fill_data.additional_logins.begin(); 576 iter != fill_data.additional_logins.end(); ++iter) { 577 if (DoUsernamesMatch(iter->first, current_username, 578 exact_username_match)) { 579 username = iter->first; 580 password = iter->second.password; 581 break; 582 } 583 } 584 585 // Check possible usernames. 586 if (username.empty() && password.empty()) { 587 for (PasswordFormFillData::UsernamesCollection::const_iterator iter = 588 fill_data.other_possible_usernames.begin(); 589 iter != fill_data.other_possible_usernames.end(); ++iter) { 590 for (size_t i = 0; i < iter->second.size(); ++i) { 591 if (DoUsernamesMatch(iter->second[i], current_username, 592 exact_username_match)) { 593 usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED; 594 username = iter->second[i]; 595 password = iter->first.password; 596 break; 597 } 598 } 599 if (!username.empty() && !password.empty()) 600 break; 601 } 602 } 603 } 604 if (password.empty()) 605 return false; // No match was found. 606 607 // Input matches the username, fill in required values. 608 username_element->setValue(username); 609 610 if (set_selection) { 611 username_element->setSelectionRange(current_username.length(), 612 username.length()); 613 } 614 615 SetElementAutofilled(username_element, true); 616 if (IsElementEditable(*password_element)) 617 password_element->setValue(password); 618 SetElementAutofilled(password_element, true); 619 return true; 620 } 621 622 void PasswordAutofillAgent::PerformInlineAutocomplete( 623 const WebKit::WebInputElement& username_input, 624 const WebKit::WebInputElement& password_input, 625 const PasswordFormFillData& fill_data) { 626 DCHECK(!fill_data.wait_for_username); 627 628 // We need non-const versions of the username and password inputs. 629 WebKit::WebInputElement username = username_input; 630 WebKit::WebInputElement password = password_input; 631 632 // Don't inline autocomplete if the caret is not at the end. 633 // TODO(jcivelli): is there a better way to test the caret location? 634 if (username.selectionStart() != username.selectionEnd() || 635 username.selectionEnd() != static_cast<int>(username.value().length())) { 636 return; 637 } 638 639 // Show the popup with the list of available usernames. 640 ShowSuggestionPopup(fill_data, username); 641 642 643 #if !defined(OS_ANDROID) 644 // Fill the user and password field with the most relevant match. Android 645 // only fills in the fields after the user clicks on the suggestion popup. 646 FillUserNameAndPassword(&username, &password, fill_data, false, true); 647 #endif 648 } 649 650 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { 651 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); 652 iter != login_to_password_info_.end();) { 653 if (iter->first.document().frame() == frame) 654 login_to_password_info_.erase(iter++); 655 else 656 ++iter; 657 } 658 } 659 660 bool PasswordAutofillAgent::FindLoginInfo(const WebKit::WebNode& node, 661 WebKit::WebInputElement* found_input, 662 PasswordInfo* found_password) { 663 if (!node.isElementNode()) 664 return false; 665 666 WebKit::WebElement element = node.toConst<WebKit::WebElement>(); 667 if (!element.hasTagName("input")) 668 return false; 669 670 WebKit::WebInputElement input = element.to<WebKit::WebInputElement>(); 671 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); 672 if (iter == login_to_password_info_.end()) 673 return false; 674 675 *found_input = input; 676 *found_password = iter->second; 677 return true; 678 } 679 680 } // namespace autofill 681