/* * Copyright (C) 2015 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 "HTTPServer.h" #import "PlatformUtilities.h" #import "RemoteObjectRegistry.h" #import "Test.h" #import "TestAwakener.h" #import "TestNavigationDelegate.h" #import "WKWebViewConfigurationExtras.h" #import #import #import #import #import #import #import #import #import TEST(RemoteObjectRegistry, Basic) { __block bool isDone = false; @autoreleasepool { NSString * const testPlugInClassName = @"RemoteObjectRegistryPlugIn"; auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:testPlugInClassName]); configuration.get()._groupIdentifier = @"testGroupIdentifier"; auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]); isDone = false; _WKRemoteObjectInterface *interface = remoteObjectInterface(); id object = [[webView _remoteObjectRegistry] remoteObjectProxyWithInterface:interface]; [object sayHello:@"Hello, World!"]; [webView evaluateJavaScript:@"helloString" completionHandler:^(id result, NSError *error) { EXPECT_TRUE([result isKindOfClass:[NSString class]]); EXPECT_WK_STREQ(result, @"Hello, World!"); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object sayHello:@"Hello Again!" completionHandler:^(NSString *result) { EXPECT_TRUE([result isKindOfClass:[NSString class]]); EXPECT_WK_STREQ(result, @"Your string was 'Hello Again!'"); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object selectionAndClickInformationForClickAtPoint:[NSValue valueWithPoint:NSMakePoint(12, 34)] completionHandler:^(NSDictionary *result) { EXPECT_TRUE([result isEqual:@{ @"URL": [NSURL URLWithString:@"http://www.webkit.org/"] }]); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object takeRange:NSMakeRange(345, 123) completionHandler:^(NSUInteger location, NSUInteger length) { EXPECT_EQ(345U, location); EXPECT_EQ(123U, length); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; auto initialAwakener = adoptNS([[TestAwakener alloc] initWithValue:42]); [object sendAwakener:initialAwakener.get() completionHandler:^(TestAwakener *awakener) { EXPECT_EQ(awakener.value, 42); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object takeSize:CGSizeMake(123.45, 678.91) completionHandler:^(CGFloat width, CGFloat height) { EXPECT_EQ(123.45, width); EXPECT_EQ(678.91, height); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object takeUnsignedLongLong:std::numeric_limits::max() completionHandler:^(unsigned long long value) { EXPECT_EQ(std::numeric_limits::max(), value); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object takeLongLong:std::numeric_limits::max() completionHandler:^(long long value) { EXPECT_EQ(std::numeric_limits::max(), value); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object takeUnsignedLong:std::numeric_limits::max() completionHandler:^(unsigned long value) { EXPECT_EQ(std::numeric_limits::max(), value); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object takeLong:std::numeric_limits::max() completionHandler:^(long value) { EXPECT_EQ(std::numeric_limits::max(), value); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://webkit.org/"]]; auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://webkit.org/"] statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"testFieldName" : @"testFieldValue" }]); NSError *error = [NSError errorWithDomain:@"testDomain" code:123 userInfo:@{@"a":@"b"}]; auto protectionSpace = adoptNS([[NSURLProtectionSpace alloc] initWithHost:@"testHost" port:80 protocol:@"testProtocol" realm:@"testRealm" authenticationMethod:NSURLAuthenticationMethodHTTPDigest]); NSURLCredential *credential = [NSURLCredential credentialWithUser:@"testUser" password:@"testPassword" persistence:NSURLCredentialPersistenceForSession]; id sender = nil; auto challenge = adoptNS([[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:protectionSpace.get() proposedCredential:credential previousFailureCount:42 failureResponse:response.get() error:error sender:sender]); [object sendRequest:request response:response.get() challenge:challenge.get() error:error completionHandler:^(NSURLRequest *deserializedRequest, NSURLResponse *deserializedResponse, NSURLAuthenticationChallenge *deserializedChallenge, NSError *deserializedError) { EXPECT_WK_STREQ(deserializedRequest.URL.absoluteString, "https://webkit.org/"); EXPECT_WK_STREQ([(NSHTTPURLResponse *)deserializedResponse allHeaderFields][@"testFieldName"], "testFieldValue"); EXPECT_WK_STREQ(deserializedChallenge.protectionSpace.realm, "testRealm"); EXPECT_WK_STREQ(deserializedError.domain, "testDomain"); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; [object getGroupIdentifier:^(NSString *identifier) { EXPECT_WK_STREQ(identifier, "testGroupIdentifier"); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; bool exceptionThrown = false; NSMutableDictionary *child = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"foo", @"name", [NSNumber numberWithInt:1], @"value", nil]; NSMutableDictionary *dictionaryWithCycle = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"root", @"name", child, @"child", nil]; [child setValue:dictionaryWithCycle forKey:@"parent"]; // Creates a cycle. @try { [object takeDictionary:dictionaryWithCycle completionHandler:^(NSDictionary* value) { EXPECT_TRUE(!value.count); isDone = true; }]; TestWebKitAPI::Util::run(&isDone); isDone = false; } @catch (NSException *e) { exceptionThrown = true; } EXPECT_FALSE(exceptionThrown); class DoneWhenDestroyed : public RefCounted { public: DoneWhenDestroyed(bool& isDone) : isDone(isDone) { } ~DoneWhenDestroyed() { isDone = true; } private: bool& isDone; }; { RefPtr doneWhenDestroyed = adoptRef(*new DoneWhenDestroyed(isDone)); [object doNotCallCompletionHandler:[doneWhenDestroyed]() { }]; } TestWebKitAPI::Util::run(&isDone); } } @interface LocalObject : NSObject { @public BlockPtr completionHandlerFromWebProcess; bool hasCompletionHandler; } @end @implementation LocalObject - (void)doSomethingWithCompletionHandler:(void (^)(void))completionHandler { completionHandlerFromWebProcess = completionHandler; hasCompletionHandler = true; } @end TEST(RemoteObjectRegistry, CallReplyBlockAfterOriginatingWebViewDeallocates) { auto localObject = adoptNS([[LocalObject alloc] init]); WeakObjCPtr weakWebViewPtr; @autoreleasepool { NSString * const testPlugInClassName = @"RemoteObjectRegistryPlugIn"; auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:testPlugInClassName]); auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]); weakWebViewPtr = webView.get(); _WKRemoteObjectInterface *interface = remoteObjectInterface(); id object = [[webView _remoteObjectRegistry] remoteObjectProxyWithInterface:interface]; [[webView _remoteObjectRegistry] registerExportedObject:localObject.get() interface:localObjectInterface()]; [object callUIProcessMethodWithReplyBlock]; TestWebKitAPI::Util::run(&localObject->hasCompletionHandler); } while (true) { @autoreleasepool { if (!weakWebViewPtr.get()) break; TestWebKitAPI::Util::spinRunLoop(); } } localObject->completionHandlerFromWebProcess(); } TEST(RemoteObjectRegistry, SerializeErrorWithCertificates) { TestWebKitAPI::HTTPServer server({ }, TestWebKitAPI::HTTPServer::Protocol::Https); auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:[WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"RemoteObjectRegistryPlugIn"]]); auto delegate = adoptNS([TestNavigationDelegate new]); webView.get().navigationDelegate = delegate.get(); [webView loadRequest:server.request()]; NSError *error = [delegate waitForDidFailProvisionalNavigation]; NSString *key = @"NSErrorPeerCertificateChainKey"; EXPECT_WK_STREQ(error.domain, "NSURLErrorDomain"); EXPECT_TRUE(error.userInfo[key]); _WKRemoteObjectInterface *interface = [_WKRemoteObjectInterface remoteObjectInterfaceWithProtocol:@protocol(RemoteObjectProtocol)]; id object = [[webView _remoteObjectRegistry] remoteObjectProxyWithInterface:interface]; __block bool roundTripComplete = false; [object sendError:error completionHandler:^(NSError *deserializedError) { EXPECT_WK_STREQ(deserializedError.domain, "NSURLErrorDomain"); NSArray *chain = deserializedError.userInfo[key]; EXPECT_TRUE(chain); EXPECT_EQ(CFGetTypeID(chain[0]), SecCertificateGetTypeID()); roundTripComplete = true; }]; TestWebKitAPI::Util::run(&roundTripComplete); }