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 we run after autofilling forms. It returns ids and values of | |
Eugene But (OOO till 7-30)
2017/04/19 00:18:13
Please avoid using "we" (go/avoidwe).
danyao
2017/04/19 15:49:03
Done.
| |
761 // all 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 |