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
3 // found in the LICENSE file.
5 #import <UIKit/UIKit.h>
9 #include "base/strings/stringprintf.h"
10 #include "ios/web/public/test/web_test_util.h"
11 #include "ios/web/test/web_test.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "testing/gtest_mac.h"
15 // Unit tests for ios/web/web_state/js/resources/core.js.
19 struct TestScriptAndExpectedValue {
21 NSString* expectedValue;
24 // A mixin class for testing with CRWWKWebViewWebController or
25 // CRWUIWebViewWebController.
26 template <typename WebTestT>
27 class CoreJsTest : public WebTestT {
29 void ImageTesterHelper(
30 NSString* htmlForImage,
31 NSString* expectedValueWhenClickingOnImage) {
32 NSString* pageContentTemplate =
33 @"<html><body style='margin-left:10px;margin-top:10px;'>"
34 "<div style='width:100px;height:100px;'>"
35 " <p style='position:absolute;left:25px;top:25px;"
36 " width:50px;height:50px'>"
39 " </p></div></body></html>";
40 NSString* pageContent =
41 [NSString stringWithFormat:pageContentTemplate, htmlForImage];
43 TestScriptAndExpectedValue testData[] = {
44 // Point outside the document margins.
46 @"__gCrWeb.getElementFromPoint(0, 0)",
49 // Point outside the <p> element.
51 @"__gCrWeb.getElementFromPoint(100, 100)",
52 expectedValueWhenClickingOnImage
54 // Point inside the <p> element.
56 @"__gCrWeb.getElementFromPoint(300, 300)",
60 for (size_t i = 0; i < arraysize(testData); i++) {
61 TestScriptAndExpectedValue& data = testData[i];
62 WebTestT::LoadHtml(pageContent);
63 NSString* result = WebTestT::RunJavaScript(data.testScript);
64 EXPECT_NSEQ(data.expectedValue, result) << " in test " << i << ": " <<
65 [data.testScript UTF8String];
70 // Concrete test fixture to test core.js using UIWebView-based web controller.
71 typedef CoreJsTest<web::WebTestWithUIWebViewWebController> CoreJSUIWebViewTest;
73 // Concrete test fixture to test core.js using WKWebView-based web controller.
74 typedef CoreJsTest<web::WebTestWithWKWebViewWebController> CoreJSWKWebViewTest;
76 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, GetImageUrlAtPoint) {
77 NSString* htmlForImage =
78 @"<img id='foo' style='width:200;height:200;' src='file:///bogus'/>";
79 NSString* expectedValueWhenClickingOnImage =
80 @"{\"src\":\"file:///bogus\",\"referrerPolicy\":\"default\"}";
81 this->ImageTesterHelper(htmlForImage, expectedValueWhenClickingOnImage);
84 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, GetImageTitleAtPoint) {
85 NSString* htmlForImage =
86 @"<img id='foo' title='Hello world!'"
87 "style='width:200;height:200;' src='file:///bogus'/>";
88 NSString* expectedValueWhenClickingOnImage =
89 @"{\"src\":\"file:///bogus\",\"referrerPolicy\":\"default\","
90 "\"title\":\"Hello world!\"}";
91 this->ImageTesterHelper(htmlForImage, expectedValueWhenClickingOnImage);
94 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, GetLinkImageUrlAtPoint) {
95 NSString* htmlForImage =
96 @"<a href='file:///linky'>"
97 "<img id='foo' style='width:200;height:200;' src='file:///bogus'/>"
99 NSString* expectedValueWhenClickingOnImage =
100 @"{\"src\":\"file:///bogus\",\"referrerPolicy\":\"default\","
101 "\"href\":\"file:///linky\"}";
102 this->ImageTesterHelper(htmlForImage, expectedValueWhenClickingOnImage);
105 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, TextAreaStopsProximity) {
106 NSString* pageContent =
107 @"<html><body style='margin-left:10px;margin-top:10px;'>"
108 "<div style='width:100px;height:100px;'>"
110 " style='position:absolute;left:0px;top:0px;width:50px;height:50px'"
111 " src='file:///bogus' />"
112 "<input type='text' name='name'"
113 " style='position:absolute;left:5px;top:5px; width:40px;height:40px'/>"
114 "</div></body> </html>";
116 NSString* success = @"{\"src\":\"file:///bogus\","
117 "\"referrerPolicy\":\"default\"}";
118 NSString* failure = @"{}";
120 TestScriptAndExpectedValue testData[] = {
122 @"__gCrWeb.getElementFromPoint(2, 20)",
126 @"__gCrWeb.getElementFromPoint(5, 20)",
131 for (size_t i = 0; i < arraysize(testData); i++) {
132 TestScriptAndExpectedValue& data = testData[i];
133 this->LoadHtml(pageContent);
134 NSString* result = this->RunJavaScript(data.testScript);
135 EXPECT_NSEQ(data.expectedValue, result) << " in test " << i << ": " <<
136 [data.testScript UTF8String];
140 struct TestDataForPasswordFormDetection {
141 NSString* pageContent;
142 NSString* containsPassword;
145 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, HasPasswordField) {
146 TestDataForPasswordFormDetection testData[] = {
147 // Form without a password field.
149 @"<form><input type='text' name='password'></form>",
152 // Form with a password field.
154 @"<form><input type='password' name='password'></form>",
158 for (size_t i = 0; i < arraysize(testData); i++) {
159 TestDataForPasswordFormDetection& data = testData[i];
160 this->LoadHtml(data.pageContent);
161 NSString* result = this->RunJavaScript(@"__gCrWeb.hasPasswordField()");
162 EXPECT_NSEQ(data.containsPassword, result) <<
163 " in test " << i << ": " << [data.pageContent UTF8String];
167 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, HasPasswordFieldinFrame) {
168 TestDataForPasswordFormDetection data = {
169 // Form with a password field in a nested iframe.
170 @"<iframe name='pf'></iframe>"
172 " var doc = frames['pf'].document.open();"
173 " doc.write('<form><input type=\\'password\\'></form>');"
178 this->LoadHtml(data.pageContent);
179 NSString* result = this->RunJavaScript(@"__gCrWeb.hasPasswordField()");
180 EXPECT_NSEQ(data.containsPassword, result) << [data.pageContent UTF8String];
183 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, Stringify) {
184 // TODO(jeanfrancoisg): Test whether __gCrWeb.stringify(undefined) correctly
186 TestScriptAndExpectedValue testData[] = {
187 // Stringify a string that contains various characters that must
190 @"__gCrWeb.stringify('a\\u000a\\t\\b\\\\\\\"Z')",
191 @"\"a\\n\\t\\b\\\\\\\"Z\""
193 // Stringify a number.
195 @"__gCrWeb.stringify(77.7)",
198 // Stringify an array.
200 @"__gCrWeb.stringify(['a','b'])",
203 // Stringify an object.
205 @"__gCrWeb.stringify({'a':'b','c':'d'})",
206 @"{\"a\":\"b\",\"c\":\"d\"}"
208 // Stringify a hierarchy of objects and arrays.
210 @"__gCrWeb.stringify([{'a':['b','c'],'d':'e'},'f'])",
211 @"[{\"a\":[\"b\",\"c\"],\"d\":\"e\"},\"f\"]"
215 @"__gCrWeb.stringify(null)",
218 // Stringify an object with a toJSON function.
221 "temp.toJSON = function (key) {return undefined};"
222 "__gCrWeb.stringify(temp)",
225 // Stringify an object with a toJSON property that is not a function.
229 "__gCrWeb.stringify(temp)",
234 for (size_t i = 0; i < arraysize(testData); i++) {
235 TestScriptAndExpectedValue& data = testData[i];
236 // Load a sample HTML page. As a side-effect, loading HTML via
237 // |webController_| will also inject core.js.
238 this->LoadHtml(@"<p>");
239 NSString* result = this->RunJavaScript(data.testScript);
240 EXPECT_NSEQ(data.expectedValue, result) << " in test " << i << ": " <<
241 [data.testScript UTF8String];
245 // Tests the javascript of the url of the an image present in the DOM.
246 WEB_TEST_F(CoreJSUIWebViewTest, CoreJSWKWebViewTest, LinkOfImage) {
247 // A page with a large image surrounded by a link.
248 static const char image[] =
249 "<a href='%s'><img width=400 height=400 src='foo'></img></a>";
251 // A page with a link to a destination URL.
252 this->LoadHtml(base::StringPrintf(image, "http://destination"));
254 this->RunJavaScript(@"__gCrWeb['getElementFromPoint'](200, 200)");
255 std::string expected_result =
256 R"({"src":"foo","referrerPolicy":"default",)"
257 R"("href":"http://destination/"})";
258 EXPECT_EQ(expected_result, [result UTF8String]);
260 // A page with a link with some JavaScript that does not result in a NOP.
261 this->LoadHtml(base::StringPrintf(image,
262 "javascript:console.log('whatever')"));
263 result = this->RunJavaScript(@"__gCrWeb['getElementFromPoint'](200, 200)");
265 R"({"src":"foo","referrerPolicy":"default",)"
266 R"("href":"javascript:console.log("})";
267 EXPECT_EQ(expected_result, [result UTF8String]);
269 // A list of JavaScripts that result in a NOP.
270 std::vector<std::string> nop_javascripts;
271 nop_javascripts.push_back(";");
272 nop_javascripts.push_back("void(0);");
273 nop_javascripts.push_back("void(0); void(0); void(0)");
275 for (auto js : nop_javascripts) {
276 // A page with a link with some JavaScript that results in a NOP.
277 const std::string javascript = std::string("javascript:") + js;
278 this->LoadHtml(base::StringPrintf(image, javascript.c_str()));
279 result = this->RunJavaScript(@"__gCrWeb['getElementFromPoint'](200, 200)");
280 expected_result = R"({"src":"foo","referrerPolicy":"default"})";
282 // Make sure the returned JSON does not have an 'href' key.
283 EXPECT_EQ(expected_result, [result UTF8String]);