| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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 #import "ios/chrome/browser/passwords/password_controller.h" | 5 #import "ios/chrome/browser/passwords/password_controller.h" |
| 6 | 6 |
| 7 #import <Foundation/Foundation.h> | 7 #import <Foundation/Foundation.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <utility> | 10 #include <utility> |
| (...skipping 710 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 721 "<input id='un4' type='text' name='u4'>" | 721 "<input id='un4' type='text' name='u4'>" |
| 722 "<input id='pw4' type='password' name='p4'>" | 722 "<input id='pw4' type='password' name='p4'>" |
| 723 "</form>" | 723 "</form>" |
| 724 "<form>" | 724 "<form>" |
| 725 "<input id='un5' type='text' name='u4'>" | 725 "<input id='un5' type='text' name='u4'>" |
| 726 "<input id='pw5' type='password' name='p4'>" | 726 "<input id='pw5' type='password' name='p4'>" |
| 727 "</form>" | 727 "</form>" |
| 728 "<form name=\"f6'\">" | 728 "<form name=\"f6'\">" |
| 729 "<input id=\"un6'\" type='text' name=\"u6'\">" | 729 "<input id=\"un6'\" type='text' name=\"u6'\">" |
| 730 "<input id=\"pw6'\" type='password' name=\"p6'\">" | 730 "<input id=\"pw6'\" type='password' name=\"p6'\">" |
| 731 "</form>"; | 731 "</form>" |
| 732 | 732 "<iframe name='pf'></iframe>" |
| 733 // A script that resets all text fields. | |
| 734 static NSString* kClearInputFieldsScript = | |
| 735 @"var inputs = document.getElementsByTagName('input');" | |
| 736 "for(var i = 0; i < inputs.length; i++){" | |
| 737 " inputs[i].value = '';" | |
| 738 "}"; | |
| 739 | |
| 740 // A script that we run after autofilling forms. It returns | |
| 741 // ids and values of all non-empty fields. | |
| 742 static NSString* kInputFieldValueVerificationScript = | |
| 743 @"var result='';" | |
| 744 "var inputs = document.getElementsByTagName('input');" | |
| 745 "for(var i = 0; i < inputs.length; i++){" | |
| 746 " var input = inputs[i];" | |
| 747 " if (input.value) {" | |
| 748 " result += input.id + '=' + input.value +';';" | |
| 749 " }" | |
| 750 "}; result"; | |
| 751 | |
| 752 // Test html content and expected result for __gCrWeb.hasPasswordField call. | |
| 753 struct TestDataForPasswordFormDetection { | |
| 754 NSString* page_content; | |
| 755 BOOL contains_password; | |
| 756 }; | |
| 757 | |
| 758 // Tests that the existence of (or the lack of) a password field in the page is | |
| 759 // detected correctly. | |
| 760 TEST_F(PasswordControllerTest, HasPasswordField) { | |
| 761 TestDataForPasswordFormDetection test_data[] = { | |
| 762 // Form without a password field. | |
| 763 {@"<form><input type='text' name='password'></form>", NO}, | |
| 764 // Form with a password field. | |
| 765 {@"<form><input type='password' name='password'></form>", YES}}; | |
| 766 for (size_t i = 0; i < arraysize(test_data); i++) { | |
| 767 TestDataForPasswordFormDetection& data = test_data[i]; | |
| 768 LoadHtml(data.page_content); | |
| 769 id result = ExecuteJavaScript(@"__gCrWeb.hasPasswordField()"); | |
| 770 EXPECT_NSEQ(@(data.contains_password), result) | |
| 771 << " in test " << i << ": " | |
| 772 << base::SysNSStringToUTF8(data.page_content); | |
| 773 } | |
| 774 } | |
| 775 | |
| 776 // Tests that the existence a password field in a nested iframe/ is detected | |
| 777 // correctly. | |
| 778 TEST_F(PasswordControllerTest, HasPasswordFieldinFrame) { | |
| 779 TestDataForPasswordFormDetection data = { | |
| 780 // Form with a password field in a nested iframe. | |
| 781 @"<iframe name='pf'></iframe>" | |
| 782 "<script>" | 733 "<script>" |
| 783 " var doc = frames['pf'].document.open();" | 734 " var doc = frames['pf'].document.open();" |
| 784 " doc.write('<form><input type=\\'password\\'></form>');" | 735 // Add a form inside iframe. It should also be matched and autofilled. |
| 736 " doc.write('<form><input id=\\'un7\\' type=\\'text\\' name=\\'u4\\'>');" |
| 737 " doc.write('<input id=\\'pw7\\' type=\\'password\\' name=\\'p4\\'>');" |
| 738 " doc.write('</form>');" |
| 739 // Add a non-password form inside iframe. It should not be matched. |
| 740 " doc.write('<form><input id=\\'un8\\' type=\\'text\\' name=\\'u4\\'>');" |
| 741 " doc.write('<input id=\\'pw8\\' type=\\'text\\' name=\\'p4\\'>');" |
| 742 " doc.write('</form>');" |
| 785 " doc.close();" | 743 " doc.close();" |
| 786 "</script>", | 744 "</script>"; |
| 787 YES | 745 |
| 788 }; | 746 // A script that resets all text fields, including those in iframes. |
| 789 LoadHtml(data.page_content); | 747 static NSString* kClearInputFieldsScript = |
| 790 id result = ExecuteJavaScript(@"__gCrWeb.hasPasswordField()"); | 748 @"function clearInputFields(win) {" |
| 791 EXPECT_NSEQ(@(data.contains_password), result) | 749 " var inputs = win.document.getElementsByTagName('input');" |
| 792 << base::SysNSStringToUTF8(data.page_content); | 750 " for (var i = 0; i < inputs.length; i++) {" |
| 793 } | 751 " inputs[i].value = '';" |
| 752 " }" |
| 753 " var frames = win.frames;" |
| 754 " for (var i = 0; i < frames.length; i++) {" |
| 755 " clearInputFields(frames[i]);" |
| 756 " }" |
| 757 "}" |
| 758 "clearInputFields(window);"; |
| 759 |
| 760 // A script that runs after autofilling forms. It returns ids and values of all |
| 761 // non-empty fields, including those in iframes. |
| 762 static NSString* kInputFieldValueVerificationScript = |
| 763 @"function findAllInputs(win) {" |
| 764 " var result = '';" |
| 765 " var inputs = win.document.getElementsByTagName('input');" |
| 766 " for (var i = 0; i < inputs.length; i++) {" |
| 767 " var input = inputs[i];" |
| 768 " if (input.value) {" |
| 769 " result += input.id + '=' + input.value + ';';" |
| 770 " }" |
| 771 " }" |
| 772 " var frames = win.frames;" |
| 773 " for (var i = 0; i < frames.length; i++) {" |
| 774 " result += findAllInputs(frames[i]);" |
| 775 " }" |
| 776 " return result;" |
| 777 "};" |
| 778 "var result = findAllInputs(window); result"; |
| 794 | 779 |
| 795 struct FillPasswordFormTestData { | 780 struct FillPasswordFormTestData { |
| 796 const std::string origin; | 781 const std::string origin; |
| 797 const std::string action; | 782 const std::string action; |
| 798 const char* username_field; | 783 const char* username_field; |
| 799 const char* username_value; | 784 const char* username_value; |
| 800 const char* password_field; | 785 const char* password_field; |
| 801 const char* password_value; | 786 const char* password_value; |
| 802 const BOOL should_succeed; | 787 const BOOL should_succeed; |
| 803 NSString* expected_result; | 788 NSString* expected_result; |
| 804 }; | 789 }; |
| 805 | 790 |
| 806 // Tests that filling password forms works correctly. | 791 // Tests that filling password forms works correctly. |
| 807 TEST_F(PasswordControllerTest, FillPasswordForm) { | 792 TEST_F(PasswordControllerTest, FillPasswordForm) { |
| 808 LoadHtml(kHtmlWithMultiplePasswordForms); | 793 LoadHtml(kHtmlWithMultiplePasswordForms); |
| 809 | 794 |
| 810 // TODO(crbug.com/614092): can we remove this assertion? This call is the only | |
| 811 // reason why hasPasswordField is a public API on gCrWeb. If the page does | |
| 812 // not contain a password field, shouldn't one of the expectations of the | |
| 813 // remaining tests also fail? | |
| 814 EXPECT_NSEQ(@YES, ExecuteJavaScript(@"__gCrWeb.hasPasswordField()")); | |
| 815 | |
| 816 const std::string base_url = BaseUrl(); | 795 const std::string base_url = BaseUrl(); |
| 817 // clang-format off | 796 // clang-format off |
| 818 FillPasswordFormTestData test_data[] = { | 797 FillPasswordFormTestData test_data[] = { |
| 819 // Basic test: one-to-one match on the first password form. | 798 // Basic test: one-to-one match on the first password form. |
| 820 { | 799 { |
| 821 base_url, | 800 base_url, |
| 822 base_url, | 801 base_url, |
| 823 "u0", | 802 "u0", |
| 824 "test_user", | 803 "test_user", |
| 825 "p0", | 804 "p0", |
| 826 "test_password", | 805 "test_password", |
| 827 YES, | 806 YES, |
| 828 @"un0=test_user;pw0=test_password;" | 807 @"un0=test_user;pw0=test_password;" |
| 829 }, | 808 }, |
| 830 // Multiple forms match: they should all be autofilled. | 809 // Multiple forms match (including one in iframe): they should all be |
| 810 // autofilled. |
| 831 { | 811 { |
| 832 base_url, | 812 base_url, |
| 833 base_url, | 813 base_url, |
| 834 "u4", | 814 "u4", |
| 835 "test_user", | 815 "test_user", |
| 836 "p4", | 816 "p4", |
| 837 "test_password", | 817 "test_password", |
| 838 YES, | 818 YES, |
| 839 @"un4=test_user;pw4=test_password;un5=test_user;pw5=test_password;" | 819 @"un4=test_user;pw4=test_password;un5=test_user;pw5=test_password;" |
| 820 "un7=test_user;pw7=test_password;" |
| 840 }, | 821 }, |
| 841 // The form matches despite a different action: the only difference | 822 // The form matches despite a different action: the only difference |
| 842 // is a query and reference. | 823 // is a query and reference. |
| 843 { | 824 { |
| 844 base_url, | 825 base_url, |
| 845 base_url, | 826 base_url, |
| 846 "u1", | 827 "u1", |
| 847 "test_user", | 828 "test_user", |
| 848 "p1", | 829 "p1", |
| 849 "test_password", | 830 "test_password", |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 983 }); | 964 }); |
| 984 EXPECT_EQ(2, success_counter); | 965 EXPECT_EQ(2, success_counter); |
| 985 id result = ExecuteJavaScript(kInputFieldValueVerificationScript); | 966 id result = ExecuteJavaScript(kInputFieldValueVerificationScript); |
| 986 EXPECT_NSEQ(@"u2=john.doe@gmail.com;p2=super!secret;" | 967 EXPECT_NSEQ(@"u2=john.doe@gmail.com;p2=super!secret;" |
| 987 "u3=john.doe@gmail.com;p3=super!secret;", | 968 "u3=john.doe@gmail.com;p3=super!secret;", |
| 988 result); | 969 result); |
| 989 } | 970 } |
| 990 | 971 |
| 991 BOOL PasswordControllerTest::BasicFormFill(NSString* html) { | 972 BOOL PasswordControllerTest::BasicFormFill(NSString* html) { |
| 992 LoadHtml(html); | 973 LoadHtml(html); |
| 993 EXPECT_NSEQ(@YES, ExecuteJavaScript(@"__gCrWeb.hasPasswordField()")); | |
| 994 const std::string base_url = BaseUrl(); | 974 const std::string base_url = BaseUrl(); |
| 995 PasswordFormFillData form_data; | 975 PasswordFormFillData form_data; |
| 996 SetPasswordFormFillData(form_data, base_url, base_url, "u0", "test_user", | 976 SetPasswordFormFillData(form_data, base_url, base_url, "u0", "test_user", |
| 997 "p0", "test_password", nullptr, nullptr, false); | 977 "p0", "test_password", nullptr, nullptr, false); |
| 998 __block BOOL block_was_called = NO; | 978 __block BOOL block_was_called = NO; |
| 999 __block BOOL return_value = NO; | 979 __block BOOL return_value = NO; |
| 1000 [passwordController_ fillPasswordForm:form_data | 980 [passwordController_ fillPasswordForm:form_data |
| 1001 completionHandler:^(BOOL success) { | 981 completionHandler:^(BOOL success) { |
| 1002 block_was_called = YES; | 982 block_was_called = YES; |
| 1003 return_value = success; | 983 return_value = success; |
| (...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1377 // Tests that an HTTPS page with a password field does not update the SSL status | 1357 // Tests that an HTTPS page with a password field does not update the SSL status |
| 1378 // to indicate DISPLAYED_PASSWORD_FIELD_ON_HTTP. | 1358 // to indicate DISPLAYED_PASSWORD_FIELD_ON_HTTP. |
| 1379 TEST_F(PasswordControllerTest, HTTPSPassword) { | 1359 TEST_F(PasswordControllerTest, HTTPSPassword) { |
| 1380 LoadHtml(kHtmlWithPasswordForm, GURL("https://chromium.test")); | 1360 LoadHtml(kHtmlWithPasswordForm, GURL("https://chromium.test")); |
| 1381 | 1361 |
| 1382 web::SSLStatus ssl_status = | 1362 web::SSLStatus ssl_status = |
| 1383 web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL(); | 1363 web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL(); |
| 1384 EXPECT_FALSE(ssl_status.content_status & | 1364 EXPECT_FALSE(ssl_status.content_status & |
| 1385 web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); | 1365 web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP); |
| 1386 } | 1366 } |
| OLD | NEW |