| /* |
| * Copyright (C) 2020 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| |
| #import "DeprecatedGlobalValues.h" |
| #import "RemoteObjectRegistry.h" |
| #import "TestWKWebView.h" |
| #import "Utilities.h" |
| #import <WebKit/WKNavigationDelegatePrivate.h> |
| #import <WebKit/WKPreferencesPrivate.h> |
| #import <WebKit/WKWebView.h> |
| #import <WebKit/WKWebViewPrivate.h> |
| #import <WebKit/_WKInternalDebugFeature.h> |
| #import <WebKit/_WKRemoteObjectInterface.h> |
| #import <WebKit/_WKRemoteObjectRegistry.h> |
| #import <wtf/RetainPtr.h> |
| |
| static bool didCrash = false; |
| static RetainPtr<NSString> alertMessage; |
| static RetainPtr<NSString> promptDefault; |
| static RetainPtr<NSString> promptResult; |
| |
| @interface IPCTestingAPIDelegate : NSObject <WKUIDelegate, WKNavigationDelegate> |
| - (BOOL)sayHelloWasCalled; |
| @end |
| |
| @implementation IPCTestingAPIDelegate { |
| BOOL _didCallSayHello; |
| } |
| |
| - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler |
| { |
| alertMessage = message; |
| done = true; |
| completionHandler(); |
| } |
| |
| - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler |
| { |
| promptDefault = defaultText; |
| done = true; |
| completionHandler(promptResult.get()); |
| } |
| |
| - (void)_webView:(WKWebView *)webView webContentProcessDidTerminateWithReason:(_WKProcessTerminationReason)reason |
| { |
| didCrash = false; |
| done = true; |
| } |
| |
| - (void)sayHello:(NSString *)hello completionHandler:(void (^)(NSString *))completionHandler |
| { |
| _didCallSayHello = YES; |
| } |
| |
| - (BOOL)sayHelloWasCalled |
| { |
| return _didCallSayHello; |
| } |
| |
| @end |
| |
| TEST(IPCTestingAPI, IsDisabledByDefault) |
| { |
| auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>alert(typeof(IPC));</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| EXPECT_STREQ([alertMessage UTF8String], "undefined"); |
| } |
| |
| #if ENABLE(IPC_TESTING_API) |
| |
| static RetainPtr<TestWKWebView> createWebViewWithIPCTestingAPI() |
| { |
| RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| for (_WKInternalDebugFeature *feature in [WKPreferences _internalDebugFeatures]) { |
| if ([feature.key isEqualToString:@"IPCTestingAPIEnabled"]) { |
| [[configuration preferences] _setEnabled:YES forInternalDebugFeature:feature]; |
| break; |
| } |
| } |
| return adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get()]); |
| } |
| |
| #if !ASSERT_ENABLED |
| |
| TEST(IPCTestingAPI, CanDetectNilReplyBlocks) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| _WKRemoteObjectInterface *interface = remoteObjectInterface(); |
| [[webView _remoteObjectRegistry] remoteObjectProxyWithInterface:interface]; |
| [[webView _remoteObjectRegistry] registerExportedObject:delegate.get() interface:interface]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>buf = new Uint8Array([" |
| // Strings in this buffer are encoded as follows: |
| // string length, 3 NUL bytes, 0x1 byte, then string contents |
| // For example, this string is 0x14 length (20 bytes), 3 NUL bytes + 0x1, then "RemoteObjectProtocol" |
| "0x14,0x0,0x0,0x0,0x1,0x52,0x65,0x6d,0x6f,0x74,0x65,0x4f,0x62,0x6a,0x65,0x63,0x74,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c," |
| // padding + "invocation" |
| "0x0,0x0,0x0,0x9,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x1,0x69,0x6e,0x76,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e," |
| // a serialized object + "typeString" |
| "0x0,0x9,0x0,0x0,0x0,0xf5,0xeb,0x54,0xa9,0x3,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x1,0x74,0x79,0x70,0x65,0x53,0x74,0x72,0x69,0x6e,0x67,0x0," |
| // a zeroed object + "$string" |
| "0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x1,0x24,0x73,0x74,0x72,0x69,0x6e,0x67,0x15,0x0,0x0,0x0," |
| // "v@:@.@.?" (an objective-C method signature) + "class" |
| "0x6,0x0,0x0,0x0,0x1,0x76,0x40,0x3a,0x40,0x40,0x3f,0x0,0x6,0x0,0x0,0x0,0x1,0x24,0x63,0x6c,0x61,0x73,0x73,0x0," |
| // "NSString" + "selector" |
| "0x15,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4e,0x53,0x53,0x74,0x72,0x69,0x6e,0x67,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x73,0x65,0x6c,0x65,0x63,0x74,0x6f,0x72,0x0,0x0,0x0," |
| // a zeroed object + "$string" |
| "0x9,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x1,0x24,0x73,0x74,0x72,0x69,0x6e,0x67,0x15,0x0,0x0,0x0," |
| // "sayHello:completionHandler:" (method name we're trying to call) |
| "0x1b,0x0,0x0,0x0,0x1,0x73,0x61,0x79,0x48,0x65,0x6c,0x6c,0x6f,0x3a,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x69,0x6f,0x6e,0x48,0x61,0x6e,0x64,0x6c,0x65,0x72,0x3a," |
| // "$class" + "NSString" |
| "0x6,0x0,0x0,0x0,0x1,0x24,0x63,0x6c,0x61,0x73,0x73,0x0,0x15,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x1,0x4e,0x53,0x53,0x74,0x72,0x69,0x6e,0x67,0x0,0x0,0x0," |
| // "$class" + "NSInvocation" |
| "0x6,0x0,0x0,0x0,0x1,0x24,0x63,0x6c,0x61,0x73,0x73,0x0,0x15,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x1,0x4e,0x53,0x49,0x6e,0x76,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x0,0x0,0x0," |
| // "$objectStam" + zero object |
| "0xd,0x0,0x0,0x0,0x1,0x24,0x6f,0x62,0x6a,0x65,0x63,0x74,0x53,0x74,0x61,0x6d,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0," |
| // zeroed objects + ".NS.uuidbytes" |
| "0x9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x91,0x4e,0x53,0x2e,0x75,0x75,0x69,0x64,0x62,0x79,0x74,0x65,0x73,0x0,0x0,0x0," |
| // some zeroed objects |
| "0x8,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x29,0xc5,0x6d,0x2,0x13,0xa,0x4e,0xe7,0xaa,0xac,0x8,0x55,0xf2,0x66,0x2c,0x7c," |
| // "$class" + "NSUUID" |
| "0x6,0x0,0x0,0x0,0x1,0x24,0x63,0x6c,0x61,0x73,0x73,0x0,0x15,0x0,0x0,0x0,0x6,0x0,0x0,0x0,0x1,0x4e,0x53,0x55,0x55,0x49,0x44,0x0,0x0,0x0," |
| // mostly zero objects + "v@?c" (objective-C method signature) |
| "0x0,0x0,0x1,0x0,0x0,0x0,0x2c,0x0,0x0,0x0,0x59,0x1,0x0,0x0,0x0,0x9b,0x0,0x0,0x4,0x0,0x0,0x0,0x1,0x76,0x40,0x3f,0x63,0x0,]);" |
| "for(var x=0; x<100; x++) IPC.sendMessage('UI', x, IPC.messages.RemoteObjectRegistry_InvokeMethod.name, [buf]);</script>"]; |
| TestWebKitAPI::Util::runFor(&done, 1); |
| |
| // Make sure sayHello was not called, as the reply block was nil. |
| EXPECT_FALSE([delegate.get() sayHelloWasCalled]); |
| } |
| |
| #endif |
| |
| TEST(IPCTestingAPI, CanSendAlert) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>IPC.sendSyncMessage('UI', IPC.webPageProxyID, IPC.messages.WebPageProxy_RunJavaScriptAlert.name, 100," |
| "[{type: 'uint64_t', value: IPC.frameID}, {type: 'FrameInfoData'}, {'type': 'String', 'value': 'hi'}]);</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "hi"); |
| } |
| |
| TEST(IPCTestingAPI, AlertIsSyncMessage) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>alert(IPC.messages.WebPageProxy_RunJavaScriptAlert.isSync);</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "true"); |
| } |
| |
| TEST(IPCTestingAPI, CanSendInvalidAsyncMessageToUIProcessWithoutTermination) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>" |
| "IPC.sendMessage('UI', IPC.webPageProxyID, IPC.messages.WebPageProxy_ShowShareSheet.name, []);" |
| "alert('hi')</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "hi"); |
| } |
| |
| TEST(IPCTestingAPI, CanSendInvalidSyncMessageToUIProcessWithoutTermination) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>" |
| "IPC.sendSyncMessage('UI', IPC.webPageProxyID, IPC.messages.WebPageProxy_RunJavaScriptAlert.name, 100, [{type: 'uint64_t', value: IPC.frameID}]);" |
| "alert('hi')</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "hi"); |
| } |
| |
| #if ENABLE(GPU_PROCESS) |
| |
| TEST(IPCTestingAPI, CanSendSyncMessageToGPUProcess) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>" |
| "result = !!IPC.sendSyncMessage('GPU', 0, IPC.messages.GPUConnectionToWebProcess_EnsureAudioSession.name, 100, []);" |
| "alert(result)</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_TRUE([alertMessage boolValue]); |
| } |
| |
| TEST(IPCTestingAPI, CanSendAsyncMessageToGPUProcess) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>(async function test() {" |
| "window.result = await IPC.sendMessage('GPU', 0, IPC.messages.RemoteAudioDestinationManager_StartAudioDestination.name, [{type: 'uint64_t', value: 12345}]);" |
| "alert(!!result)" |
| "})();</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_TRUE([alertMessage boolValue]); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"result.arguments[0].type"].UTF8String, "bool"); |
| EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"result.arguments[0].value"].boolValue); |
| } |
| |
| TEST(IPCTestingAPI, CanSendInvalidAsyncMessageToGPUProcessWithoutTermination) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>(async function test() {" |
| "IPC.sendMessage('GPU', 0, IPC.messages.GPUConnectionToWebProcess_CreateRenderingBackend.name, []);" |
| "window.result = await IPC.sendMessage('GPU', 0, IPC.messages.RemoteAudioDestinationManager_StartAudioDestination.name, [{type: 'uint64_t', value: 12345}]);" |
| "alert(!!result)" |
| "})();</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_TRUE([alertMessage boolValue]); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"result.arguments[0].type"].UTF8String, "bool"); |
| EXPECT_FALSE([webView stringByEvaluatingJavaScript:@"result.arguments[0].value"].boolValue); |
| } |
| |
| TEST(IPCTestingAPI, CanReceiveIPCSemaphore) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| auto html = @"<!DOCTYPE html>" |
| "<script>" |
| "const bufferSize = 1 << 16;" |
| "const streamConnection = IPC.createStreamClientConnection('GPU', bufferSize);" |
| "IPC.sendMessage('GPU', 0, IPC.messages.GPUConnectionToWebProcess_CreateRenderingBackend.name, [" |
| " { type: 'RemoteRenderingBackendCreationParameters', 'identifier': 123, 'pageProxyID': IPC.webPageProxyID, 'pageID': IPC.pageID }," |
| " { type: 'StreamConnectionBuffer', value: streamConnection.streamBuffer() }," |
| "]);" |
| "const arguments = IPC.waitForMessage('GPU', 123, IPC.messages.RemoteRenderingBackendProxy_DidCreateWakeUpSemaphoreForDisplayListStream.name, 100);" |
| "alert(arguments.length + ':' + arguments[0].type + ':' + arguments[0].value.waitFor(100));" |
| "</script>"; |
| [webView synchronouslyLoadHTMLString:html]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "1:Semaphore:false"); |
| } |
| |
| TEST(IPCTestingAPI, CanReceiveSharedMemory) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| auto html = @"<!DOCTYPE html>" |
| "<script>" |
| "const bufferSize = 1 << 16;" |
| "const streamConnection = IPC.createStreamClientConnection('GPU', bufferSize);" |
| "IPC.sendMessage('GPU', 0, IPC.messages.GPUConnectionToWebProcess_CreateRenderingBackend.name, [" |
| " { type: 'RemoteRenderingBackendCreationParameters', 'identifier': 123, 'pageProxyID': IPC.webPageProxyID, 'pageID': IPC.pageID }," |
| " { type: 'StreamConnectionBuffer', value: streamConnection.streamBuffer() }," |
| "]);" |
| "const arguments = IPC.waitForMessage('GPU', 123, IPC.messages.RemoteRenderingBackendProxy_DidCreateWakeUpSemaphoreForDisplayListStream.name, 100);" |
| "streamConnection.setWakeUpSemaphore(arguments[0].value);" |
| "const result = streamConnection.sendSyncMessage(123, IPC.messages.RemoteRenderingBackend_UpdateSharedMemoryForGetPixelBuffer.name, 100, [{type: 'uint32_t', value: 8}]);" |
| "alert(result.arguments.length);" |
| "</script>"; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:html]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_EQ([alertMessage intValue], 1); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"firstReply = result.arguments[0]; firstReply.type"].UTF8String, "SharedMemory"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"firstReply.protection"].UTF8String, "ReadOnly"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"Array.from(new Uint8Array(firstReply.value.readBytes(0, 8))).toString()"].UTF8String, "0,0,0,0,0,0,0,0"); |
| } |
| |
| #endif // ENABLE(GPU_PROCESS) |
| |
| TEST(IPCTestingAPI, CanCreateIPCSemaphore) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>alert(IPC.createSemaphore().waitFor(100));</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_FALSE([alertMessage boolValue]); |
| } |
| |
| TEST(IPCTestingAPI, CanCreateSharedMemory) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>const sharedMemory = IPC.createSharedMemory(8); alert(sharedMemory.toString());</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "[object SharedMemory]"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"new Int8Array(sharedMemory.readBytes(0))[0]"].intValue, 0); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"sharedMemory.writeBytes(new Int8Array([1, 2, 4, 8, 16, 32]))"].intValue, 0); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"Array.from(new Int8Array(sharedMemory.readBytes(1, 3))).toString()"].UTF8String, "2,4,8"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"sharedMemory.writeBytes(new Int8Array([101, 102, 103, 104, 105, 106]), 2, 3)"].intValue, 0); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"Array.from(new Int8Array(sharedMemory.readBytes())).toString()"].UTF8String, "1,2,101,102,103,32,0,0"); |
| } |
| |
| TEST(IPCTestingAPI, CanSendSemaphore) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| auto* html = @R"HTML(<!DOCTYPE html> |
| <body> |
| <script> |
| const audioContext = new AudioContext; |
| const destination = audioContext.createMediaStreamDestination(); |
| const semaphore = IPC.createSemaphore(); |
| const result = IPC.sendSyncMessage('GPU', 0, IPC.messages.RemoteAudioDestinationManager_CreateAudioDestination.name, 100, |
| [{type: 'String', value: 'some device'}, |
| {type: 'uint32_t', value: destination.numberOfInputs}, |
| {type: 'uint32_t', value: destination.channelCount}, |
| {type: 'float', value: audioContext.sampleRate}, {type: 'float', value: audioContext.sampleRate}, |
| {type: 'Semaphore', value: semaphore}]); |
| alert(result.arguments[0].type); |
| </script> |
| </body>)HTML"; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:html]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "uint64_t"); |
| } |
| |
| #if PLATFORM(COCOA) |
| TEST(IPCTestingAPI, CanSendSharedMemory) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| auto* html = @R"HTML(<!DOCTYPE html> |
| <body> |
| <script> |
| const sharedMemory = IPC.createSharedMemory(8); |
| sharedMemory.writeBytes(new Uint8Array(Array.from('hello').map((char) => char.charCodeAt(0)))); |
| const result = IPC.sendSyncMessage('UI', 0, IPC.messages.WebPasteboardProxy_SetPasteboardBufferForType.name, 100, [ |
| {type: 'String', value: 'Apple CFPasteboard general'}, {type: 'String', value: 'text/plain'}, |
| {type: 'SharedMemory', value: sharedMemory, protection: 'ReadOnly'}, {type: 'bool', value: 1}, {type: 'uint64_t', value: IPC.pageID}]); |
| alert(result.arguments.length + ':' + JSON.stringify(result.arguments[0])); |
| </script> |
| </body>)HTML"; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:html]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "1:{\"type\":\"int64_t\",\"value\":0}"); |
| } |
| #endif |
| |
| TEST(IPCTestingAPI, DecodesReplyArgumentsForPrompt) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| promptResult = @"foo"; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>result = IPC.sendSyncMessage('UI', IPC.webPageProxyID, IPC.messages.WebPageProxy_RunJavaScriptPrompt.name, 100," |
| "[{type: 'uint64_t', value: IPC.frameID}, {type: 'FrameInfoData'}, {'type': 'String', 'value': 'hi'}, {'type': 'String', 'value': 'bar'}]);</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([promptDefault UTF8String], "bar"); |
| EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"JSON.stringify(result.arguments)"] UTF8String], "[{\"type\":\"String\",\"value\":\"foo\"}]"); |
| } |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| TEST(IPCTestingAPI, DecodesReplyArgumentsForAsyncMessage) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| promptResult = @"foo"; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>IPC.sendMessage(\"Networking\", 0, IPC.messages.NetworkConnectionToWebProcess_HasStorageAccess.name," |
| "[{type: 'RegistrableDomain', value: 'https://ipctestingapi.com'}, {type: 'RegistrableDomain', value: 'https://webkit.org'}, {type: 'uint64_t', value: IPC.frameID}," |
| "{type: 'uint64_t', value: IPC.pageID}]).then((result) => alert(JSON.stringify(result.arguments)));</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "[{\"type\":\"bool\",\"value\":false}]"); |
| } |
| #endif |
| |
| TEST(IPCTestingAPI, DescribesArguments) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>window.args = IPC.messages.WebPageProxy_RunJavaScriptAlert.arguments; alert('ok')</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"args.length"] UTF8String], "3"); |
| EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"args[0].type"] UTF8String], "WebCore::FrameIdentifier"); |
| EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"args[1].type"] UTF8String], "WebKit::FrameInfoData"); |
| EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"args[2].name"] UTF8String], "message"); |
| EXPECT_STREQ([[webView stringByEvaluatingJavaScript:@"args[2].type"] UTF8String], "String"); |
| } |
| |
| TEST(IPCTestingAPI, CanInterceptAlert) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>messages = []; IPC.addOutgoingMessageListener('UI', (message) => messages.push(message)); alert('ok');</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "ok"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages = messages.filter((message) => message.name == IPC.messages.WebPageProxy_RunJavaScriptAlert.name); messages.length"].UTF8String, "1"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages[0].description"].UTF8String, "WebPageProxy_RunJavaScriptAlert"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args = messages[0].arguments; args.length"].intValue, 3); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].type"].UTF8String, "uint64_t"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[0].value"].intValue, [webView stringByEvaluatingJavaScript:@"IPC.frameID.toString()"].intValue); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1] instanceof ArrayBuffer"].boolValue, YES); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[2].type"].UTF8String, "String"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[2].value"].UTF8String, "ok"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(messages[0].syncRequestID)"].UTF8String, "number"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].destinationID"].intValue, |
| [webView stringByEvaluatingJavaScript:@"IPC.webPageProxyID.toString()"].intValue); |
| } |
| |
| #if ENABLE(INTELLIGENT_TRACKING_PREVENTION) |
| TEST(IPCTestingAPI, CanInterceptHasStorageAccess) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| done = false; |
| promptResult = @"foo"; |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><script>let targetMessage = {}; const messageName = IPC.messages.NetworkConnectionToWebProcess_HasStorageAccess.name;" |
| "IPC.addOutgoingMessageListener('Networking', (currentMessage) => { if (currentMessage.name == messageName) targetMessage = currentMessage; });" |
| "IPC.sendMessage('Networking', 0, messageName, [{type: 'RegistrableDomain', value: 'https://ipctestingapi.com'}, {type: 'RegistrableDomain', value: 'https://webkit.org'}," |
| "{type: 'uint64_t', value: IPC.frameID}, {type: 'uint64_t', value: IPC.pageID}]).then((result) => alert(JSON.stringify(result.arguments)));</script>"]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([alertMessage UTF8String], "[{\"type\":\"bool\",\"value\":false}]"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.description"].UTF8String, "NetworkConnectionToWebProcess_HasStorageAccess"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments.length"].intValue, 4); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[0].type"].UTF8String, "RegistrableDomain"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[0].value"].UTF8String, "ipctestingapi.com"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[1].type"].UTF8String, "RegistrableDomain"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[1].value"].UTF8String, "webkit.org"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[2].type"].UTF8String, "uint64_t"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[2].value"].UTF8String, [webView stringByEvaluatingJavaScript:@"IPC.frameID.toString()"].UTF8String); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[3].type"].UTF8String, "uint64_t"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.arguments[3].value"].intValue, [webView stringByEvaluatingJavaScript:@"IPC.pageID.toString()"].intValue); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(targetMessage.syncRequestID)"].UTF8String, "undefined"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"targetMessage.destinationID"].intValue, 0); |
| } |
| #endif |
| |
| TEST(IPCTestingAPI, CanInterceptFindString) |
| { |
| auto webView = createWebViewWithIPCTestingAPI(); |
| |
| auto delegate = adoptNS([[IPCTestingAPIDelegate alloc] init]); |
| [webView setUIDelegate:delegate.get()]; |
| |
| [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><body><p>hello</p><script>messages = []; IPC.addIncomingMessageListener('UI', (message) => messages.push(message));</script>"]; |
| |
| done = false; |
| auto findConfiguration = adoptNS([[WKFindConfiguration alloc] init]); |
| [webView findString:@"hello" withConfiguration:findConfiguration.get() completionHandler:^(WKFindResult *result) { |
| EXPECT_TRUE(result.matchFound); |
| EXPECT_TRUE([webView selectionRangeHasStartOffset:0 endOffset:5]); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages = messages.filter((message) => message.name == IPC.messages.WebPage_FindString.name); messages.length"].UTF8String, "1"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"messages[0].description"].UTF8String, "WebPage_FindString"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args = messages[0].arguments; args.length"].intValue, 3); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].type"].UTF8String, "String"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[0].value"].UTF8String, "hello"); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[1].type"].UTF8String, "uint16_t"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1].value"].intValue, 0x11); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[1].isOptionSet"].boolValue, YES); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"args[2].type"].UTF8String, "uint32_t"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"args[2].value"].intValue, 1); |
| EXPECT_STREQ([webView stringByEvaluatingJavaScript:@"typeof(messages[0].syncRequestID)"].UTF8String, "undefined"); |
| EXPECT_EQ([webView stringByEvaluatingJavaScript:@"messages[0].destinationID"].intValue, |
| [webView stringByEvaluatingJavaScript:@"IPC.webPageProxyID.toString()"].intValue); |
| } |
| |
| #endif |