Add ICU message format support
[chromium-blink-merge.git] / ios / web / web_state / js / core_js_unittest.mm
blob93b91b5a5cdab3983fb91823cb1b53d0575ffa12
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>
7 #include <vector>
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.
17 namespace {
19 struct TestScriptAndExpectedValue {
20   NSString* testScript;
21   NSString* expectedValue;
24 // A mixin class for testing with CRWWKWebViewWebController or
25 // CRWUIWebViewWebController.
26 template <typename WebTestT>
27 class CoreJsTest : public WebTestT {
28  protected:
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'>"
37             "%@"
38             "    Chrome rocks!"
39             "  </p></div></body></html>";
40     NSString* pageContent =
41         [NSString stringWithFormat:pageContentTemplate, htmlForImage];
43     TestScriptAndExpectedValue testData[] = {
44       // Point outside the document margins.
45       {
46         @"__gCrWeb.getElementFromPoint(0, 0)",
47         @"{}"
48       },
49       // Point outside the <p> element.
50       {
51         @"__gCrWeb.getElementFromPoint(100, 100)",
52         expectedValueWhenClickingOnImage
53       },
54       // Point inside the <p> element.
55       {
56         @"__gCrWeb.getElementFromPoint(300, 300)",
57         @"{}"
58       },
59     };
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];
66     }
67   }
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'/>"
98           "</a>";
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;'>"
109   "<img id='foo'"
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[] = {
121     {
122       @"__gCrWeb.getElementFromPoint(2, 20)",
123       success
124     },
125     {
126       @"__gCrWeb.getElementFromPoint(5, 20)",
127       failure
128     },
129   };
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];
137   }
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.
148     {
149       @"<form><input type='text' name='password'></form>",
150       @"false"
151     },
152     // Form with a password field.
153     {
154       @"<form><input type='password' name='password'></form>",
155       @"true"
156     }
157   };
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];
164   }
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>"
171     "<script>"
172     "  var doc = frames['pf'].document.open();"
173     "  doc.write('<form><input type=\\'password\\'></form>');"
174     "  doc.close();"
175     "</script>",
176     @"true"
177   };
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
185   //returns undefined.
186   TestScriptAndExpectedValue testData[] = {
187     // Stringify a string that contains various characters that must
188     // be escaped.
189     {
190       @"__gCrWeb.stringify('a\\u000a\\t\\b\\\\\\\"Z')",
191       @"\"a\\n\\t\\b\\\\\\\"Z\""
192     },
193     // Stringify a number.
194     {
195       @"__gCrWeb.stringify(77.7)",
196       @"77.7"
197     },
198     // Stringify an array.
199     {
200       @"__gCrWeb.stringify(['a','b'])",
201       @"[\"a\",\"b\"]"
202     },
203     // Stringify an object.
204     {
205       @"__gCrWeb.stringify({'a':'b','c':'d'})",
206       @"{\"a\":\"b\",\"c\":\"d\"}"
207     },
208     // Stringify a hierarchy of objects and arrays.
209     {
210       @"__gCrWeb.stringify([{'a':['b','c'],'d':'e'},'f'])",
211       @"[{\"a\":[\"b\",\"c\"],\"d\":\"e\"},\"f\"]"
212     },
213     // Stringify null.
214     {
215       @"__gCrWeb.stringify(null)",
216       @"null"
217     },
218     // Stringify an object with a toJSON function.
219     {
220       @"temp = [1,2];"
221       "temp.toJSON = function (key) {return undefined};"
222       "__gCrWeb.stringify(temp)",
223       @"[1,2]"
224     },
225     // Stringify an object with a toJSON property that is not a function.
226     {
227       @"temp = [1,2];"
228       "temp.toJSON = 42;"
229       "__gCrWeb.stringify(temp)",
230       @"[1,2]"
231     },
232   };
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];
242   }
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"));
253   NSString* result =
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)");
264   expected_result =
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]);
284   }
287 }  // namespace