web_view_js_utils_unittest.mm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. // Copyright 2014 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.
  4. #import "ios/web/js_messaging/web_view_js_utils.h"
  5. #import <WebKit/WebKit.h>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #import "base/test/ios/wait_util.h"
  9. #include "base/values.h"
  10. #import "ios/web/test/fakes/crw_fake_script_message_handler.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. #import "testing/gtest_mac.h"
  13. #include "testing/platform_test.h"
  14. #include "third_party/abseil-cpp/absl/types/optional.h"
  15. #if !defined(__has_feature) || !__has_feature(objc_arc)
  16. #error "This file requires ARC support."
  17. #endif
  18. using base::test::ios::WaitUntilConditionOrTimeout;
  19. using base::test::ios::kWaitForJSCompletionTimeout;
  20. namespace web {
  21. using WebViewJsUtilsTest = PlatformTest;
  22. // Tests that ValueResultFromWKResult converts nil value to nullptr.
  23. TEST_F(WebViewJsUtilsTest, ValueResultFromUndefinedWKResult) {
  24. EXPECT_FALSE(ValueResultFromWKResult(nil));
  25. }
  26. // Tests that ValueResultFromWKResult converts string to Value::Type::STRING.
  27. TEST_F(WebViewJsUtilsTest, ValueResultFromStringWKResult) {
  28. std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@"test"));
  29. EXPECT_TRUE(value);
  30. EXPECT_EQ(base::Value::Type::STRING, value->type());
  31. ASSERT_TRUE(value->is_string());
  32. EXPECT_EQ("test", value->GetString());
  33. }
  34. // Tests that ValueResultFromWKResult converts inetger to Value::Type::DOUBLE.
  35. // NOTE: WKWebView API returns all numbers as kCFNumberFloat64Type, so there is
  36. // no way to tell if the result is integer or double.
  37. TEST_F(WebViewJsUtilsTest, ValueResultFromIntegerWKResult) {
  38. std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@1));
  39. EXPECT_TRUE(value);
  40. ASSERT_EQ(base::Value::Type::DOUBLE, value->type());
  41. EXPECT_EQ(1, value->GetDouble());
  42. }
  43. // Tests that ValueResultFromWKResult converts double to Value::Type::DOUBLE.
  44. TEST_F(WebViewJsUtilsTest, ValueResultFromDoubleWKResult) {
  45. std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@3.14));
  46. EXPECT_TRUE(value);
  47. ASSERT_EQ(base::Value::Type::DOUBLE, value->type());
  48. EXPECT_EQ(3.14, value->GetDouble());
  49. }
  50. // Tests that ValueResultFromWKResult converts bool to Value::Type::BOOLEAN.
  51. TEST_F(WebViewJsUtilsTest, ValueResultFromBoolWKResult) {
  52. std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(@YES));
  53. ASSERT_TRUE(value);
  54. ASSERT_TRUE(value->is_bool());
  55. EXPECT_TRUE(value->GetBool());
  56. }
  57. // Tests that ValueResultFromWKResult converts null to Value::Type::NONE.
  58. TEST_F(WebViewJsUtilsTest, ValueResultFromNullWKResult) {
  59. std::unique_ptr<base::Value> value(
  60. web::ValueResultFromWKResult([NSNull null]));
  61. EXPECT_TRUE(value);
  62. EXPECT_EQ(base::Value::Type::NONE, value->type());
  63. }
  64. // Tests that ValueResultFromWKResult converts NSDictionaries to properly
  65. // initialized base::DictionaryValue.
  66. TEST_F(WebViewJsUtilsTest, ValueResultFromDictionaryWKResult) {
  67. NSDictionary* test_dictionary =
  68. @{@"Key1" : @"Value1",
  69. @"Key2" : @{@"Key3" : @42}};
  70. std::unique_ptr<base::Value> value(
  71. web::ValueResultFromWKResult(test_dictionary));
  72. base::DictionaryValue* dictionary = nullptr;
  73. value->GetAsDictionary(&dictionary);
  74. EXPECT_NE(nullptr, dictionary);
  75. std::string value1;
  76. dictionary->GetString("Key1", &value1);
  77. EXPECT_EQ("Value1", value1);
  78. base::DictionaryValue const* inner_dictionary = nullptr;
  79. dictionary->GetDictionary("Key2", &inner_dictionary);
  80. EXPECT_NE(nullptr, inner_dictionary);
  81. EXPECT_EQ(42, *inner_dictionary->FindDoubleKey("Key3"));
  82. }
  83. // Tests that ValueResultFromWKResult converts NSArray to properly
  84. // initialized base::ListValue.
  85. TEST_F(WebViewJsUtilsTest, ValueResultFromArrayWKResult) {
  86. NSArray* test_array = @[ @"Value1", @[ @YES ], @42 ];
  87. std::unique_ptr<base::Value> value(web::ValueResultFromWKResult(test_array));
  88. ASSERT_TRUE(value->is_list());
  89. base::Value::ConstListView list = value->GetListDeprecated();
  90. size_t list_size = 3;
  91. ASSERT_EQ(list_size, list.size());
  92. ASSERT_TRUE(list[0].is_string());
  93. std::string value1 = list[0].GetString();
  94. EXPECT_EQ("Value1", value1);
  95. EXPECT_TRUE(list[1].is_list());
  96. ASSERT_TRUE(list[2].is_double());
  97. double value3 = list[2].GetDouble();
  98. EXPECT_EQ(42, value3);
  99. }
  100. // Tests that an NSDictionary with a cycle does not cause infinite recursion.
  101. TEST_F(WebViewJsUtilsTest, ValueResultFromDictionaryWithDepthCheckWKResult) {
  102. // Create a dictionary with a cycle.
  103. NSMutableDictionary* test_dictionary =
  104. [NSMutableDictionary dictionaryWithCapacity:1];
  105. NSMutableDictionary* test_dictionary_2 =
  106. [NSMutableDictionary dictionaryWithCapacity:1];
  107. const char* key = "key";
  108. NSString* obj_c_key = [NSString stringWithCString:key
  109. encoding:NSASCIIStringEncoding];
  110. test_dictionary[obj_c_key] = test_dictionary_2;
  111. test_dictionary_2[obj_c_key] = test_dictionary;
  112. // Break the retain cycle so that the dictionaries are freed.
  113. base::ScopedClosureRunner runner(base::BindOnce(^{
  114. [test_dictionary_2 removeAllObjects];
  115. }));
  116. // Check that parsing the dictionary stopped at a depth of
  117. // |kMaximumParsingRecursionDepth|.
  118. std::unique_ptr<base::Value> value =
  119. web::ValueResultFromWKResult(test_dictionary);
  120. base::DictionaryValue* current_dictionary = nullptr;
  121. base::DictionaryValue* inner_dictionary = nullptr;
  122. value->GetAsDictionary(&current_dictionary);
  123. EXPECT_NE(nullptr, current_dictionary);
  124. for (int current_depth = 0; current_depth <= kMaximumParsingRecursionDepth;
  125. current_depth++) {
  126. EXPECT_NE(nullptr, current_dictionary);
  127. inner_dictionary = nullptr;
  128. current_dictionary->GetDictionary(key, &inner_dictionary);
  129. current_dictionary = inner_dictionary;
  130. }
  131. EXPECT_EQ(nullptr, current_dictionary);
  132. }
  133. // Tests that an NSArray with a cycle does not cause infinite recursion.
  134. TEST_F(WebViewJsUtilsTest, ValueResultFromArrayWithDepthCheckWKResult) {
  135. // Create an array with a cycle.
  136. NSMutableArray* test_array = [NSMutableArray arrayWithCapacity:1];
  137. NSMutableArray* test_array_2 = [NSMutableArray arrayWithCapacity:1];
  138. test_array[0] = test_array_2;
  139. test_array_2[0] = test_array;
  140. // Break the retain cycle so that the arrays are freed.
  141. base::ScopedClosureRunner runner(base::BindOnce(^{
  142. [test_array removeAllObjects];
  143. }));
  144. // Check that parsing the array stopped at a depth of
  145. // |kMaximumParsingRecursionDepth|.
  146. std::unique_ptr<base::Value> value = web::ValueResultFromWKResult(test_array);
  147. absl::optional<base::Value::ConstListView> current_list;
  148. absl::optional<base::Value::ConstListView> inner_list;
  149. ASSERT_TRUE(value->is_list());
  150. current_list = value->GetListDeprecated();
  151. for (int current_depth = 0; current_depth <= kMaximumParsingRecursionDepth;
  152. current_depth++) {
  153. ASSERT_TRUE(current_list.has_value());
  154. inner_list = absl::nullopt;
  155. if (!current_list.value().empty() && current_list.value()[0].is_list())
  156. inner_list = current_list.value()[0].GetListDeprecated();
  157. current_list = inner_list;
  158. }
  159. EXPECT_FALSE(current_list.has_value());
  160. }
  161. // Tests that ExecuteJavaScript returns an error if there is no web view.
  162. TEST_F(WebViewJsUtilsTest, ExecuteJavaScriptNoWebView) {
  163. __block bool complete = false;
  164. __block id block_result = nil;
  165. __block NSError* block_error = nil;
  166. web::ExecuteJavaScript(nil, @"return true;", ^(id result, NSError* error) {
  167. block_result = [result copy];
  168. block_error = [error copy];
  169. complete = true;
  170. });
  171. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  172. return complete;
  173. }));
  174. EXPECT_TRUE(block_error);
  175. EXPECT_FALSE(block_result);
  176. }
  177. // Tests that javascript can be executed.
  178. TEST_F(WebViewJsUtilsTest, ExecuteJavaScript) {
  179. WKWebView* web_view = [[WKWebView alloc] init];
  180. __block bool complete = false;
  181. __block id block_result = nil;
  182. __block NSError* block_error = nil;
  183. web::ExecuteJavaScript(web_view, @"true", ^(id result, NSError* error) {
  184. block_result = [result copy];
  185. block_error = [error copy];
  186. complete = true;
  187. });
  188. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  189. return complete;
  190. }));
  191. EXPECT_FALSE(block_error);
  192. EXPECT_TRUE(block_result);
  193. }
  194. // Tests that javascript can be executed in the page content world when no
  195. // content world or web frame are specified.
  196. TEST_F(WebViewJsUtilsTest, ExecuteJavaScriptPageContentWorldByDefault) {
  197. __block bool complete = false;
  198. __block id result = nil;
  199. __block NSError* error = nil;
  200. WKWebView* web_view = [[WKWebView alloc] init];
  201. __block bool set_value_complete = false;
  202. __block NSError* set_value_error = nil;
  203. // Set |value| in the page content world.
  204. web::ExecuteJavaScript(web_view, WKContentWorld.pageWorld,
  205. /*frame_info=*/nil, @"var value = 3;",
  206. ^(id result, NSError* error) {
  207. set_value_error = [error copy];
  208. set_value_complete = true;
  209. });
  210. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  211. return set_value_complete;
  212. }));
  213. ASSERT_FALSE(set_value_error);
  214. // Retrieve the value without specifying the content world to verify that
  215. // ExecuteJavaScript defaults to the page content world.
  216. web::ExecuteJavaScript(web_view, /*content_world=*/nil, /*frame_info=*/nil,
  217. @"value", ^(id block_result, NSError* block_error) {
  218. result = [block_result copy];
  219. error = [block_error copy];
  220. complete = true;
  221. });
  222. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  223. return complete;
  224. }));
  225. EXPECT_FALSE(error);
  226. EXPECT_TRUE(result);
  227. EXPECT_NSEQ(@(3), result);
  228. }
  229. // Tests that javascript can be executed when the page content world is
  230. // explicitly specified but no frame info is given, implying the main frame.
  231. TEST_F(WebViewJsUtilsTest, ExecuteJavaScriptInPageWorldWithoutFrameInfo) {
  232. __block bool complete = false;
  233. __block id result = nil;
  234. __block NSError* error = nil;
  235. WKWebView* web_view = [[WKWebView alloc] init];
  236. __block bool set_value_complete = false;
  237. __block NSError* set_value_error = nil;
  238. // Set |value| in the page content world.
  239. web::ExecuteJavaScript(web_view, WKContentWorld.pageWorld,
  240. /*frame_info=*/nil, @"var value = 3;",
  241. ^(id result, NSError* error) {
  242. set_value_error = [error copy];
  243. set_value_complete = true;
  244. });
  245. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  246. return set_value_complete;
  247. }));
  248. ASSERT_FALSE(set_value_error);
  249. // Ensure the value can be accessed when specifying the content world.
  250. web::ExecuteJavaScript(web_view, WKContentWorld.pageWorld,
  251. /*frame_info=*/nil, @"value",
  252. ^(id block_result, NSError* block_error) {
  253. result = [block_result copy];
  254. error = [block_error copy];
  255. complete = true;
  256. });
  257. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  258. return complete;
  259. }));
  260. EXPECT_FALSE(error);
  261. EXPECT_TRUE(result);
  262. EXPECT_NSEQ(@(3), result);
  263. }
  264. // Tests that javascript can be executed in the page content world when the page
  265. // content world and web frame are both specified.
  266. TEST_F(WebViewJsUtilsTest, ExecuteJavaScriptPageContentWorld) {
  267. CRWFakeScriptMessageHandler* script_message_handler =
  268. [[CRWFakeScriptMessageHandler alloc] init];
  269. WKWebView* web_view = [[WKWebView alloc] init];
  270. [web_view.configuration.userContentController
  271. addScriptMessageHandler:script_message_handler
  272. name:@"TestHandler"];
  273. // Fetch WKFrameInfo instance.
  274. web::ExecuteJavaScript(
  275. web_view,
  276. @"window.webkit.messageHandlers['TestHandler'].postMessage({});",
  277. /*completion_handler=*/nil);
  278. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  279. return !!script_message_handler.lastReceivedScriptMessage.frameInfo;
  280. }));
  281. WKFrameInfo* frame_info =
  282. script_message_handler.lastReceivedScriptMessage.frameInfo;
  283. __block bool complete = false;
  284. __block id result = nil;
  285. __block NSError* error = nil;
  286. __block bool set_value_complete = false;
  287. __block NSError* set_value_error = nil;
  288. // Set |value| in the page content world.
  289. web::ExecuteJavaScript(web_view, WKContentWorld.pageWorld, frame_info,
  290. @"var value = 3;", ^(id result, NSError* error) {
  291. set_value_error = [error copy];
  292. set_value_complete = true;
  293. });
  294. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  295. return set_value_complete;
  296. }));
  297. ASSERT_FALSE(set_value_error);
  298. // Ensure the value can be accessed when specifying |frame_info|.
  299. web::ExecuteJavaScript(web_view, WKContentWorld.pageWorld, frame_info,
  300. @"value", ^(id block_result, NSError* block_error) {
  301. result = [block_result copy];
  302. error = [block_error copy];
  303. complete = true;
  304. });
  305. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  306. return complete;
  307. }));
  308. EXPECT_FALSE(error);
  309. EXPECT_TRUE(result);
  310. EXPECT_NSEQ(@(3), result);
  311. }
  312. // Tests that javascript can be executed in an isolated content world and that
  313. // it can not be accessed from the page content world.
  314. TEST_F(WebViewJsUtilsTest, ExecuteJavaScriptIsolatedWorld) {
  315. CRWFakeScriptMessageHandler* script_message_handler =
  316. [[CRWFakeScriptMessageHandler alloc] init];
  317. WKWebView* web_view = [[WKWebView alloc] init];
  318. [web_view.configuration.userContentController
  319. addScriptMessageHandler:script_message_handler
  320. name:@"TestHandler"];
  321. // Fetch WKFrameInfo instance.
  322. web::ExecuteJavaScript(
  323. web_view,
  324. @"window.webkit.messageHandlers['TestHandler'].postMessage({});",
  325. /*completion_handler=*/nil);
  326. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  327. return !!script_message_handler.lastReceivedScriptMessage.frameInfo;
  328. }));
  329. WKFrameInfo* frame_info =
  330. script_message_handler.lastReceivedScriptMessage.frameInfo;
  331. __block bool set_value_complete = false;
  332. __block NSError* set_value_error = nil;
  333. // Set |value| in the page content world.
  334. web::ExecuteJavaScript(web_view, WKContentWorld.defaultClientWorld,
  335. frame_info, @"var value = 3;",
  336. ^(id result, NSError* error) {
  337. set_value_error = [error copy];
  338. set_value_complete = true;
  339. });
  340. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  341. return set_value_complete;
  342. }));
  343. ASSERT_FALSE(set_value_error);
  344. __block bool isolated_world_complete = false;
  345. __block id isolated_world_result = nil;
  346. __block NSError* isolated_world_error = nil;
  347. // Ensure the value can be accessed when specifying an isolated world and
  348. // |frame_info|.
  349. web::ExecuteJavaScript(web_view, WKContentWorld.defaultClientWorld,
  350. frame_info, @"value",
  351. ^(id block_result, NSError* block_error) {
  352. isolated_world_result = [block_result copy];
  353. isolated_world_error = [block_error copy];
  354. isolated_world_complete = true;
  355. });
  356. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  357. return isolated_world_complete;
  358. }));
  359. EXPECT_FALSE(isolated_world_error);
  360. EXPECT_TRUE(isolated_world_result);
  361. EXPECT_NSEQ(@(3), isolated_world_result);
  362. __block bool page_world_complete = false;
  363. __block id page_world_result = nil;
  364. __block NSError* page_world_error = nil;
  365. // The value should not be accessible from the page content world.
  366. web::ExecuteJavaScript(web_view, WKContentWorld.pageWorld, frame_info,
  367. @"try { value } catch (error) { false }",
  368. ^(id block_result, NSError* block_error) {
  369. page_world_result = [block_result copy];
  370. page_world_error = [block_error copy];
  371. page_world_complete = true;
  372. });
  373. ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
  374. return page_world_complete;
  375. }));
  376. EXPECT_FALSE(page_world_error);
  377. EXPECT_TRUE(page_world_result);
  378. EXPECT_FALSE([page_world_result boolValue]);
  379. }
  380. } // namespace web