|  | // Copyright 2018 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #import "ios/web/js_messaging/web_frame_impl.h" | 
|  |  | 
|  | #import <Foundation/Foundation.h> | 
|  | #import <WebKit/WebKit.h> | 
|  |  | 
|  | #import "base/functional/bind.h" | 
|  | #import "base/ios/ios_util.h" | 
|  | #import "base/json/json_reader.h" | 
|  | #import "base/run_loop.h" | 
|  | #import "base/strings/string_number_conversions.h" | 
|  | #import "base/strings/sys_string_conversions.h" | 
|  | #import "base/test/ios/wait_util.h" | 
|  | #import "base/values.h" | 
|  | #import "ios/web/js_messaging/java_script_feature_manager.h" | 
|  | #import "ios/web/public/test/fakes/fake_web_state.h" | 
|  | #import "ios/web/public/test/web_test.h" | 
|  | #import "testing/gtest/include/gtest/gtest.h" | 
|  | #import "testing/gtest_mac.h" | 
|  | #import "third_party/ocmock/OCMock/OCMock.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kFrameId[] = "1effd8f52a067c8d3a01762d3c41dfd8"; | 
|  |  | 
|  | const char kFrameInfoRequestUrl[] = "https://test.com"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace web { | 
|  |  | 
|  | class WebFrameImplTest : public web::WebTest { | 
|  | protected: | 
|  | WebFrameImplTest() { | 
|  | mock_ns_url_request_ = OCMClassMock([NSURLRequest class]); | 
|  | mock_frame_info_ = OCMClassMock([WKFrameInfo class]); | 
|  | mock_web_view_ = OCMClassMock([WKWebView class]); | 
|  |  | 
|  | OCMStub([mock_web_view_ evaluateJavaScript:OCMOCK_ANY | 
|  | inFrame:OCMOCK_ANY | 
|  | inContentWorld:OCMOCK_ANY | 
|  | completionHandler:OCMOCK_ANY]) | 
|  | .andDo(^(NSInvocation* invocation) { | 
|  | [invocation retainArguments]; | 
|  | [invocation getArgument:&last_received_script_ atIndex:2]; | 
|  | [invocation getArgument:&last_received_content_world_ atIndex:4]; | 
|  | }); | 
|  | OCMStub([mock_frame_info_ webView]).andReturn(mock_web_view_); | 
|  | NSURL* url = [[NSURL alloc] | 
|  | initWithString:base::SysUTF8ToNSString(kFrameInfoRequestUrl)]; | 
|  | OCMStub([mock_ns_url_request_ URL]).andReturn(url); | 
|  | OCMStub([mock_frame_info_ request]).andReturn(mock_ns_url_request_); | 
|  | } | 
|  |  | 
|  | void SetUp() override { | 
|  | web::WebTest::SetUp(); | 
|  |  | 
|  | JavaScriptFeatureManager* java_script_feature_manager = | 
|  | JavaScriptFeatureManager::FromBrowserState(GetBrowserState()); | 
|  | java_script_feature_manager->ConfigureFeatures({}); | 
|  |  | 
|  | fake_web_state_.SetBrowserState(GetBrowserState()); | 
|  | } | 
|  |  | 
|  | id mock_frame_info_; | 
|  | id mock_web_view_; | 
|  | id mock_ns_url_request_; | 
|  | NSString* last_received_script_; | 
|  | WKContentWorld* last_received_content_world_; | 
|  |  | 
|  | FakeWebState fake_web_state_; | 
|  | url::Origin security_origin_; | 
|  | }; | 
|  |  | 
|  | // Tests creation of a WebFrame for the main frame. | 
|  | TEST_F(WebFrameImplTest, CreateWebFrameForMainFrame) { | 
|  | WebFrameImpl web_frame(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/true, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kPageContentWorld); | 
|  |  | 
|  | EXPECT_EQ(&fake_web_state_, web_frame.GetWebState()); | 
|  | EXPECT_TRUE(web_frame.IsMainFrame()); | 
|  | EXPECT_EQ(security_origin_, web_frame.GetSecurityOrigin()); | 
|  | EXPECT_EQ(web_frame.GetUrl(), GURL(kFrameInfoRequestUrl)); | 
|  | EXPECT_EQ(kFrameId, web_frame.GetFrameId()); | 
|  | } | 
|  |  | 
|  | // Tests that the WebFrame properly creates JavaScript for the main frame. | 
|  | TEST_F(WebFrameImplTest, CallJavaScriptFunctionMainFrame) { | 
|  | WebFrameImpl web_frame(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/true, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kPageContentWorld); | 
|  |  | 
|  | base::Value::List function_params; | 
|  | EXPECT_TRUE( | 
|  | web_frame.CallJavaScriptFunction("functionName", function_params)); | 
|  | EXPECT_NSEQ(@"__gCrWeb.callFunctionInGcrWeb(\"\", \"functionName\", [])", | 
|  | last_received_script_); | 
|  | EXPECT_NSEQ(WKContentWorld.pageWorld, last_received_content_world_); | 
|  |  | 
|  | function_params.Append("param1"); | 
|  | EXPECT_TRUE( | 
|  | web_frame.CallJavaScriptFunction("functionName", function_params)); | 
|  | EXPECT_NSEQ(@"__gCrWeb.callFunctionInGcrWeb(\"\", \"functionName\", " | 
|  | @"[\"param1\"])", | 
|  | last_received_script_); | 
|  | EXPECT_NSEQ(WKContentWorld.pageWorld, last_received_content_world_); | 
|  |  | 
|  | function_params.Append(true); | 
|  | function_params.Append(27); | 
|  | function_params.Append(3.14); | 
|  | EXPECT_TRUE( | 
|  | web_frame.CallJavaScriptFunction("functionName", function_params)); | 
|  | EXPECT_NSEQ(@"__gCrWeb.callFunctionInGcrWeb(\"\", \"functionName\", " | 
|  | @"[\"param1\",true,27,3.14])", | 
|  | last_received_script_); | 
|  | EXPECT_NSEQ(WKContentWorld.pageWorld, last_received_content_world_); | 
|  | } | 
|  |  | 
|  | // Tests that the WebFrame creates JavaScript for an iframe. | 
|  | TEST_F(WebFrameImplTest, CallJavaScriptFunctionIFrame) { | 
|  | WebFrameImpl web_frame(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/false, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kIsolatedWorld); | 
|  |  | 
|  | base::Value::List function_params; | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | web_frame.CallJavaScriptFunction("functionName", function_params)); | 
|  | EXPECT_NSEQ(@"__gCrWeb.callFunctionInGcrWeb(\"\", \"functionName\", [])", | 
|  | last_received_script_); | 
|  | EXPECT_NSEQ(WKContentWorld.defaultClientWorld, last_received_content_world_); | 
|  |  | 
|  | function_params.Append("param1"); | 
|  | EXPECT_TRUE( | 
|  | web_frame.CallJavaScriptFunction("functionName", function_params)); | 
|  | EXPECT_NSEQ(@"__gCrWeb.callFunctionInGcrWeb(\"\", \"functionName\", " | 
|  | @"[\"param1\"])", | 
|  | last_received_script_); | 
|  | EXPECT_NSEQ(WKContentWorld.defaultClientWorld, last_received_content_world_); | 
|  |  | 
|  | function_params.Append(true); | 
|  | function_params.Append(27); | 
|  | function_params.Append(3.14); | 
|  | EXPECT_TRUE( | 
|  | web_frame.CallJavaScriptFunction("functionName", function_params)); | 
|  | EXPECT_NSEQ(@"__gCrWeb.callFunctionInGcrWeb(\"\", \"functionName\", " | 
|  | @"[\"param1\",true,27,3.14])", | 
|  | last_received_script_); | 
|  | EXPECT_NSEQ(WKContentWorld.defaultClientWorld, last_received_content_world_); | 
|  | } | 
|  |  | 
|  | // Tests that the WebFrame can execute arbitrary JavaScript. | 
|  | TEST_F(WebFrameImplTest, ExecuteJavaScript) { | 
|  | NSString* script = @"__gCrWeb = {};" | 
|  | @"__gCrWeb['fakeFunction'] = function() {" | 
|  | @"  return '10';" | 
|  | @"}"; | 
|  |  | 
|  | WebFrameImpl web_frame(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/true, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kPageContentWorld); | 
|  |  | 
|  | EXPECT_TRUE(web_frame.ExecuteJavaScript(base::SysNSStringToUTF16(script))); | 
|  |  | 
|  | WebFrameImpl web_frame2(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/false, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kPageContentWorld); | 
|  | EXPECT_TRUE(web_frame2.ExecuteJavaScript(base::SysNSStringToUTF16(script))); | 
|  | } | 
|  |  | 
|  | // Tests that the WebFrame can execute arbitrary JavaScript given a callback. | 
|  | TEST_F(WebFrameImplTest, ExecuteJavaScriptWithCallback) { | 
|  | NSString* script = @"__gCrWeb = {};" | 
|  | @"__gCrWeb['fakeFunction'] = function() {" | 
|  | @"  return '10';" | 
|  | @"}"; | 
|  |  | 
|  | WebFrameImpl web_frame(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/true, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kPageContentWorld); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | web_frame.ExecuteJavaScript(base::SysNSStringToUTF16(script), | 
|  | base::BindOnce(^(const base::Value* value){ | 
|  | }))); | 
|  |  | 
|  | WebFrameImpl web_frame2(mock_frame_info_, kFrameId, | 
|  | /*is_main_frame=*/false, security_origin_, | 
|  | &fake_web_state_, ContentWorld::kPageContentWorld); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | web_frame2.ExecuteJavaScript(base::SysNSStringToUTF16(script), | 
|  | base::BindOnce(^(const base::Value* value){ | 
|  | }))); | 
|  | } | 
|  |  | 
|  | }  // namespace web |