Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/autofill/content/renderer/password_autofill_agent.h" | 5 #include "components/autofill/content/renderer/password_autofill_agent.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 157 curr_elements->form_element = fe; | 157 curr_elements->form_element = fe; |
| 158 results->push_back(curr_elements.release()); | 158 results->push_back(curr_elements.release()); |
| 159 } | 159 } |
| 160 } | 160 } |
| 161 } | 161 } |
| 162 | 162 |
| 163 bool IsElementEditable(const WebKit::WebInputElement& element) { | 163 bool IsElementEditable(const WebKit::WebInputElement& element) { |
| 164 return element.isEnabled() && !element.isReadOnly(); | 164 return element.isEnabled() && !element.isReadOnly(); |
| 165 } | 165 } |
| 166 | 166 |
| 167 void FillForm(FormElements* fe, const FormData& data) { | |
| 168 if (!fe->form_element.autoComplete()) | |
| 169 return; | |
| 170 | |
| 171 std::map<base::string16, base::string16> data_map; | |
| 172 for (size_t i = 0; i < data.fields.size(); i++) | |
| 173 data_map[data.fields[i].name] = data.fields[i].value; | |
| 174 | |
| 175 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
| 176 it != fe->input_elements.end(); ++it) { | |
| 177 WebKit::WebInputElement element = it->second; | |
| 178 // Don't fill a form that has pre-filled values distinct from the ones we | |
| 179 // want to fill with. | |
| 180 if (!element.value().isEmpty() && element.value() != data_map[it->first]) | |
| 181 return; | |
| 182 | |
| 183 // Don't fill forms with uneditable fields or fields with autocomplete | |
| 184 // disabled. | |
| 185 if (!IsElementEditable(element) || !element.autoComplete()) | |
| 186 return; | |
| 187 } | |
| 188 | |
| 189 for (FormInputElementMap::iterator it = fe->input_elements.begin(); | |
| 190 it != fe->input_elements.end(); ++it) { | |
| 191 WebKit::WebInputElement element = it->second; | |
| 192 | |
| 193 // TODO(tkent): Check maxlength and pattern. | |
| 194 element.setValue(data_map[it->first]); | |
| 195 element.setAutofilled(true); | |
| 196 element.dispatchFormControlChangeEvent(); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { | 167 void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) { |
| 201 if (element->isAutofilled() == autofilled) | 168 if (element->isAutofilled() == autofilled) |
| 202 return; | 169 return; |
| 203 element->setAutofilled(autofilled); | 170 element->setAutofilled(autofilled); |
| 204 // Notify any changeEvent listeners. | 171 // Notify any changeEvent listeners. |
| 205 element->dispatchFormControlChangeEvent(); | 172 element->dispatchFormControlChangeEvent(); |
| 206 } | 173 } |
| 207 | 174 |
| 208 bool DoUsernamesMatch(const base::string16& username1, | 175 bool DoUsernamesMatch(const base::string16& username1, |
| 209 const base::string16& username2, | 176 const base::string16& username2, |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 return false; | 210 return false; |
| 244 | 211 |
| 245 WebKit::WebInputElement password = iter->second.password_field; | 212 WebKit::WebInputElement password = iter->second.password_field; |
| 246 if (!IsElementEditable(password)) | 213 if (!IsElementEditable(password)) |
| 247 return false; | 214 return false; |
| 248 | 215 |
| 249 WebKit::WebInputElement username = element; // We need a non-const. | 216 WebKit::WebInputElement username = element; // We need a non-const. |
| 250 | 217 |
| 251 // Do not set selection when ending an editing session, otherwise it can | 218 // Do not set selection when ending an editing session, otherwise it can |
| 252 // mess with focus. | 219 // mess with focus. |
| 253 FillUserNameAndPassword(&username, &password, fill_data, true, false); | 220 FillUserNameAndPassword(&username, &password, fill_data, |
| 221 true /* exact_username_match */, | |
| 222 false /* set_selection */); | |
| 254 return true; | 223 return true; |
| 255 } | 224 } |
| 256 | 225 |
| 257 bool PasswordAutofillAgent::TextDidChangeInTextField( | 226 bool PasswordAutofillAgent::TextDidChangeInTextField( |
| 258 const WebKit::WebInputElement& element) { | 227 const WebKit::WebInputElement& element) { |
| 259 LoginToPasswordInfoMap::const_iterator iter = | 228 LoginToPasswordInfoMap::const_iterator iter = |
| 260 login_to_password_info_.find(element); | 229 login_to_password_info_.find(element); |
| 261 if (iter == login_to_password_info_.end()) | 230 if (iter == login_to_password_info_.end()) |
| 262 return false; | 231 return false; |
| 263 | 232 |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 324 const WebKit::WebString& value) { | 293 const WebKit::WebString& value) { |
| 325 WebKit::WebInputElement input; | 294 WebKit::WebInputElement input; |
| 326 PasswordInfo password; | 295 PasswordInfo password; |
| 327 if (!FindLoginInfo(node, &input, &password)) | 296 if (!FindLoginInfo(node, &input, &password)) |
| 328 return false; | 297 return false; |
| 329 | 298 |
| 330 // Set the incoming |value| in the text field and |FillUserNameAndPassword| | 299 // Set the incoming |value| in the text field and |FillUserNameAndPassword| |
| 331 // will do the rest. | 300 // will do the rest. |
| 332 input.setValue(value, true); | 301 input.setValue(value, true); |
| 333 return FillUserNameAndPassword(&input, &password.password_field, | 302 return FillUserNameAndPassword(&input, &password.password_field, |
| 334 password.fill_data, true, true); | 303 password.fill_data, |
| 304 true /* exact_username_match */, | |
| 305 true /* set_selection */); | |
| 335 } | 306 } |
| 336 | 307 |
| 337 bool PasswordAutofillAgent::DidClearAutofillSelection( | 308 bool PasswordAutofillAgent::DidClearAutofillSelection( |
| 338 const WebKit::WebNode& node) { | 309 const WebKit::WebNode& node) { |
| 339 WebKit::WebInputElement input; | 310 WebKit::WebInputElement input; |
| 340 PasswordInfo password; | 311 PasswordInfo password; |
| 341 return FindLoginInfo(node, &input, &password); | 312 return FindLoginInfo(node, &input, &password); |
| 342 } | 313 } |
| 343 | 314 |
| 344 bool PasswordAutofillAgent::ShowSuggestions( | 315 bool PasswordAutofillAgent::ShowSuggestions( |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 509 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; | 480 usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT; |
| 510 } | 481 } |
| 511 | 482 |
| 512 FormElementsList forms; | 483 FormElementsList forms; |
| 513 // We own the FormElements* in forms. | 484 // We own the FormElements* in forms. |
| 514 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); | 485 FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); |
| 515 FormElementsList::iterator iter; | 486 FormElementsList::iterator iter; |
| 516 for (iter = forms.begin(); iter != forms.end(); ++iter) { | 487 for (iter = forms.begin(); iter != forms.end(); ++iter) { |
| 517 scoped_ptr<FormElements> form_elements(*iter); | 488 scoped_ptr<FormElements> form_elements(*iter); |
| 518 | 489 |
| 519 // If wait_for_username is true, we don't want to initially fill the form | |
| 520 // until the user types in a valid username. | |
| 521 if (!form_data.wait_for_username) | |
| 522 FillForm(form_elements.get(), form_data.basic_data); | |
| 523 | |
| 524 // Attach autocomplete listener to enable selecting alternate logins. | 490 // Attach autocomplete listener to enable selecting alternate logins. |
| 525 // First, get pointers to username element. | 491 // First, get pointers to username element. |
| 526 WebKit::WebInputElement username_element = | 492 WebKit::WebInputElement username_element = |
| 527 form_elements->input_elements[form_data.basic_data.fields[0].name]; | 493 form_elements->input_elements[form_data.basic_data.fields[0].name]; |
| 528 | 494 |
| 529 // Get pointer to password element. (We currently only support single | 495 // Get pointer to password element. (We currently only support single |
| 530 // password forms). | 496 // password forms). |
| 531 WebKit::WebInputElement password_element = | 497 WebKit::WebInputElement password_element = |
| 532 form_elements->input_elements[form_data.basic_data.fields[1].name]; | 498 form_elements->input_elements[form_data.basic_data.fields[1].name]; |
| 533 | 499 |
| 500 // If wait_for_username is true, we don't want to initially fill the form | |
| 501 // until the user types in a valid username. | |
| 502 if (!form_data.wait_for_username) | |
| 503 FillFormOnPasswordRecieved(form_data, username_element, password_element); | |
| 504 | |
| 534 // We might have already filled this form if there are two <form> elements | 505 // We might have already filled this form if there are two <form> elements |
| 535 // with identical markup. | 506 // with identical markup. |
| 536 if (login_to_password_info_.find(username_element) != | 507 if (login_to_password_info_.find(username_element) != |
| 537 login_to_password_info_.end()) | 508 login_to_password_info_.end()) |
| 538 continue; | 509 continue; |
| 539 | 510 |
| 540 PasswordInfo password_info; | 511 PasswordInfo password_info; |
| 541 password_info.fill_data = form_data; | 512 password_info.fill_data = form_data; |
| 542 password_info.password_field = password_element; | 513 password_info.password_field = password_element; |
| 543 login_to_password_info_[username_element] = password_info; | 514 login_to_password_info_[username_element] = password_info; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 618 bounding_box.width() * scale, | 589 bounding_box.width() * scale, |
| 619 bounding_box.height() * scale); | 590 bounding_box.height() * scale); |
| 620 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), | 591 Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), |
| 621 field, | 592 field, |
| 622 bounding_box_scaled, | 593 bounding_box_scaled, |
| 623 suggestions, | 594 suggestions, |
| 624 realms)); | 595 realms)); |
| 625 return !suggestions.empty(); | 596 return !suggestions.empty(); |
| 626 } | 597 } |
| 627 | 598 |
| 599 void PasswordAutofillAgent::FillFormOnPasswordRecieved( | |
| 600 const PasswordFormFillData& fill_data, | |
| 601 WebKit::WebInputElement username_element, | |
| 602 WebKit::WebInputElement password_element) { | |
| 603 if (!username_element.form().autoComplete()) | |
|
Ilya Sherman
2013/11/05 02:21:42
nit: Theoretically, it's possible for username_ele
Garrett Casto
2013/11/05 05:56:11
Added a test to verify this, but we only get forms
| |
| 604 return; | |
| 605 | |
| 606 // If we can't modify the password, don't try to set the username | |
| 607 if (!IsElementEditable(password_element) || !password_element.autoComplete()) | |
| 608 return; | |
| 609 | |
| 610 // Try to set the username to the preferred name, but only if the field | |
| 611 // can be set and isn't prefilled. | |
| 612 if (IsElementEditable(username_element) && | |
| 613 username_element.autoComplete() && | |
| 614 username_element.value().isEmpty()) { | |
| 615 // TODO(tkent): Check maxlength and pattern. | |
| 616 username_element.setValue(fill_data.basic_data.fields[0].value); | |
| 617 } | |
| 618 | |
| 619 // Fill if we have an exact match for the username. Note that this sets | |
| 620 // username to autofilled. | |
| 621 FillUserNameAndPassword(&username_element, &password_element, fill_data, | |
| 622 true /* exact_username_match */, | |
| 623 false /* set_selection */); | |
| 624 } | |
| 625 | |
| 628 bool PasswordAutofillAgent::FillUserNameAndPassword( | 626 bool PasswordAutofillAgent::FillUserNameAndPassword( |
| 629 WebKit::WebInputElement* username_element, | 627 WebKit::WebInputElement* username_element, |
| 630 WebKit::WebInputElement* password_element, | 628 WebKit::WebInputElement* password_element, |
| 631 const PasswordFormFillData& fill_data, | 629 const PasswordFormFillData& fill_data, |
| 632 bool exact_username_match, | 630 bool exact_username_match, |
| 633 bool set_selection) { | 631 bool set_selection) { |
| 634 base::string16 current_username = username_element->value(); | 632 base::string16 current_username = username_element->value(); |
| 635 // username and password will contain the match found if any. | 633 // username and password will contain the match found if any. |
| 636 base::string16 username; | 634 base::string16 username; |
| 637 base::string16 password; | 635 base::string16 password; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 669 } | 667 } |
| 670 } | 668 } |
| 671 if (!username.empty() && !password.empty()) | 669 if (!username.empty() && !password.empty()) |
| 672 break; | 670 break; |
| 673 } | 671 } |
| 674 } | 672 } |
| 675 } | 673 } |
| 676 if (password.empty()) | 674 if (password.empty()) |
| 677 return false; // No match was found. | 675 return false; // No match was found. |
| 678 | 676 |
| 677 // TODO(tkent): Check maxlength and pattern for both username and password | |
| 678 // fields. | |
| 679 | |
| 679 // Input matches the username, fill in required values. | 680 // Input matches the username, fill in required values. |
| 680 username_element->setValue(username); | 681 if (IsElementEditable(*username_element)) { |
|
Ilya Sherman
2013/11/05 02:21:42
Please check username_element.autoComplete() as we
Garrett Casto
2013/11/05 05:56:11
Done.
| |
| 682 username_element->setValue(username); | |
| 683 SetElementAutofilled(username_element, true); | |
| 681 | 684 |
| 682 if (set_selection) { | 685 if (set_selection) { |
| 683 username_element->setSelectionRange(current_username.length(), | 686 username_element->setSelectionRange(current_username.length(), |
| 684 username.length()); | 687 username.length()); |
| 688 } | |
| 689 } else if (current_username != username) { | |
| 690 // If the username can't be filled and it doesn't match a saved password | |
| 691 // as is, don't autofill a password. | |
| 692 return false; | |
| 685 } | 693 } |
| 686 | 694 |
| 687 SetElementAutofilled(username_element, true); | 695 if (IsElementEditable(*password_element)) { |
|
Ilya Sherman
2013/11/05 02:21:42
Please check password_element.autoComplete() as we
Garrett Casto
2013/11/05 05:56:11
Added, though I actually moved this check up befor
Ilya Sherman
2013/11/05 06:12:58
Yeah, that's a good call. Thanks :)
| |
| 688 if (IsElementEditable(*password_element)) | |
| 689 password_element->setValue(password); | 696 password_element->setValue(password); |
| 690 SetElementAutofilled(password_element, true); | 697 SetElementAutofilled(password_element, true); |
| 691 return true; | 698 return true; |
| 699 } | |
| 700 // Couldn't fill password | |
| 701 return false; | |
| 692 } | 702 } |
| 693 | 703 |
| 694 void PasswordAutofillAgent::PerformInlineAutocomplete( | 704 void PasswordAutofillAgent::PerformInlineAutocomplete( |
| 695 const WebKit::WebInputElement& username_input, | 705 const WebKit::WebInputElement& username_input, |
| 696 const WebKit::WebInputElement& password_input, | 706 const WebKit::WebInputElement& password_input, |
| 697 const PasswordFormFillData& fill_data) { | 707 const PasswordFormFillData& fill_data) { |
| 698 DCHECK(!fill_data.wait_for_username); | 708 DCHECK(!fill_data.wait_for_username); |
| 699 | 709 |
| 700 // We need non-const versions of the username and password inputs. | 710 // We need non-const versions of the username and password inputs. |
| 701 WebKit::WebInputElement username = username_input; | 711 WebKit::WebInputElement username = username_input; |
| 702 WebKit::WebInputElement password = password_input; | 712 WebKit::WebInputElement password = password_input; |
| 703 | 713 |
| 704 // Don't inline autocomplete if the caret is not at the end. | 714 // Don't inline autocomplete if the caret is not at the end. |
| 705 // TODO(jcivelli): is there a better way to test the caret location? | 715 // TODO(jcivelli): is there a better way to test the caret location? |
| 706 if (username.selectionStart() != username.selectionEnd() || | 716 if (username.selectionStart() != username.selectionEnd() || |
| 707 username.selectionEnd() != static_cast<int>(username.value().length())) { | 717 username.selectionEnd() != static_cast<int>(username.value().length())) { |
| 708 return; | 718 return; |
| 709 } | 719 } |
| 710 | 720 |
| 711 // Show the popup with the list of available usernames. | 721 // Show the popup with the list of available usernames. |
| 712 ShowSuggestionPopup(fill_data, username); | 722 ShowSuggestionPopup(fill_data, username); |
| 713 | 723 |
| 714 | 724 |
| 715 #if !defined(OS_ANDROID) | 725 #if !defined(OS_ANDROID) |
| 716 // Fill the user and password field with the most relevant match. Android | 726 // Fill the user and password field with the most relevant match. Android |
| 717 // only fills in the fields after the user clicks on the suggestion popup. | 727 // only fills in the fields after the user clicks on the suggestion popup. |
| 718 FillUserNameAndPassword(&username, &password, fill_data, false, true); | 728 FillUserNameAndPassword(&username, &password, fill_data, |
| 729 false /* exact_username_match */, | |
| 730 true /* set_selection */); | |
| 719 #endif | 731 #endif |
| 720 } | 732 } |
| 721 | 733 |
| 722 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { | 734 void PasswordAutofillAgent::FrameClosing(const WebKit::WebFrame* frame) { |
| 723 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); | 735 for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin(); |
| 724 iter != login_to_password_info_.end();) { | 736 iter != login_to_password_info_.end();) { |
| 725 if (iter->first.document().frame() == frame) | 737 if (iter->first.document().frame() == frame) |
| 726 login_to_password_info_.erase(iter++); | 738 login_to_password_info_.erase(iter++); |
| 727 else | 739 else |
| 728 ++iter; | 740 ++iter; |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 743 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); | 755 LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input); |
| 744 if (iter == login_to_password_info_.end()) | 756 if (iter == login_to_password_info_.end()) |
| 745 return false; | 757 return false; |
| 746 | 758 |
| 747 *found_input = input; | 759 *found_input = input; |
| 748 *found_password = iter->second; | 760 *found_password = iter->second; |
| 749 return true; | 761 return true; |
| 750 } | 762 } |
| 751 | 763 |
| 752 } // namespace autofill | 764 } // namespace autofill |
| OLD | NEW |