WKProcessGroup.mm   [plain text]

/*
* Copyright (C) 2011-2019 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 "WKProcessGroupPrivate.h"

#import "APINavigationData.h"
#import "APIProcessPoolConfiguration.h"
#import "ObjCObjectGraph.h"
#import "WKAPICast.h"
#import "WKBrowsingContextControllerInternal.h"
#import "WKBrowsingContextHistoryDelegate.h"
#import "WKConnectionInternal.h"
#import "WKNSString.h"
#import "WKNSURL.h"
#import "WKNavigationDataInternal.h"
#import "WKRetainPtr.h"
#import "WKStringCF.h"
#import "WebCertificateInfo.h"
#import "WebFrameProxy.h"
#import "WebProcessPool.h"
#import <wtf/RetainPtr.h>
#import <wtf/WeakObjCPtr.h>

#if PLATFORM(IOS_FAMILY)
#import "WKAPICast.h"
#import "WKGeolocationProviderIOS.h"
#import <WebCore/WebCoreThreadSystemInterface.h>
#endif

ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGIN
@implementation WKProcessGroup {
ALLOW_DEPRECATED_IMPLEMENTATIONS_END
RefPtr<WebKit::WebProcessPool> _processPool;

WeakObjCPtr<id <WKProcessGroupDelegate>> _delegate;

#if PLATFORM(IOS_FAMILY)
RetainPtr<WKGeolocationProviderIOS> _geolocationProvider;
#endif // PLATFORM(IOS_FAMILY)
}

static void didCreateConnection(WKContextRef, WKConnectionRef connectionRef, const void* clientInfo)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
auto processGroup = (__bridge WKProcessGroup *)clientInfo;
ALLOW_DEPRECATED_DECLARATIONS_END
auto delegate = processGroup->_delegate.get();

if ([delegate respondsToSelector:@selector(processGroup:didCreateConnectionToWebProcessPlugIn:)])
[delegate processGroup:processGroup didCreateConnectionToWebProcessPlugIn:wrapper(*WebKit::toImpl(connectionRef))];
}

static void setUpConnectionClient(WKProcessGroup *processGroup, WKContextRef contextRef)
{
WKContextConnectionClientV0 connectionClient;
memset(&connectionClient, 0, sizeof(connectionClient));

connectionClient.base.version = 0;
connectionClient.base.clientInfo = (__bridge CFTypeRef)processGroup;
connectionClient.didCreateConnection = didCreateConnection;

WKContextSetConnectionClient(contextRef, &connectionClient.base);
}

static WKTypeRef getInjectedBundleInitializationUserData(WKContextRef, const void* clientInfo)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
auto processGroup = (__bridge WKProcessGroup *)clientInfo;
ALLOW_DEPRECATED_DECLARATIONS_END
auto delegate = processGroup->_delegate.get();

if ([delegate respondsToSelector:@selector(processGroupWillCreateConnectionToWebProcessPlugIn:)]) {
RetainPtr<id> initializationUserData = [delegate processGroupWillCreateConnectionToWebProcessPlugIn:processGroup];

return toAPI(&WebKit::ObjCObjectGraph::create(initializationUserData.get()).leakRef());
}

return 0;
}

static void setUpInjectedBundleClient(WKProcessGroup *processGroup, WKContextRef contextRef)
{
WKContextInjectedBundleClientV1 injectedBundleClient;
memset(&injectedBundleClient, 0, sizeof(injectedBundleClient));

injectedBundleClient.base.version = 1;
injectedBundleClient.base.clientInfo = (__bridge void*)processGroup;
injectedBundleClient.getInjectedBundleInitializationUserData = getInjectedBundleInitializationUserData;

WKContextSetInjectedBundleClient(contextRef, &injectedBundleClient.base);
}

static void didNavigateWithNavigationData(WKContextRef, WKPageRef pageRef, WKNavigationDataRef navigationDataRef, WKFrameRef frameRef, const void*)
{
if (!WebKit::toImpl(frameRef)->isMainFrame())
return;

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBrowsingContextController *controller = [WKBrowsingContextController _browsingContextControllerForPageRef:pageRef];
ALLOW_DEPRECATED_DECLARATIONS_END
auto historyDelegate = controller->_historyDelegate.get();

if ([historyDelegate respondsToSelector:@selector(browsingContextController:didNavigateWithNavigationData:)])
[historyDelegate browsingContextController:controller didNavigateWithNavigationData:wrapper(*WebKit::toImpl(navigationDataRef))];
}

static void didPerformClientRedirect(WKContextRef, WKPageRef pageRef, WKURLRef sourceURLRef, WKURLRef destinationURLRef, WKFrameRef frameRef, const void*)
{
if (!WebKit::toImpl(frameRef)->isMainFrame())
return;

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBrowsingContextController *controller = [WKBrowsingContextController _browsingContextControllerForPageRef:pageRef];
ALLOW_DEPRECATED_DECLARATIONS_END
auto historyDelegate = controller->_historyDelegate.get();

if ([historyDelegate respondsToSelector:@selector(browsingContextController:didPerformClientRedirectFromURL:toURL:)])
[historyDelegate browsingContextController:controller didPerformClientRedirectFromURL:wrapper(*WebKit::toImpl(sourceURLRef)) toURL:wrapper(*WebKit::toImpl(destinationURLRef))];
}

static void didPerformServerRedirect(WKContextRef, WKPageRef pageRef, WKURLRef sourceURLRef, WKURLRef destinationURLRef, WKFrameRef frameRef, const void*)
{
if (!WebKit::toImpl(frameRef)->isMainFrame())
return;

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBrowsingContextController *controller = [WKBrowsingContextController _browsingContextControllerForPageRef:pageRef];
ALLOW_DEPRECATED_DECLARATIONS_END
auto historyDelegate = controller->_historyDelegate.get();

if ([historyDelegate respondsToSelector:@selector(browsingContextController:didPerformServerRedirectFromURL:toURL:)])
[historyDelegate browsingContextController:controller didPerformServerRedirectFromURL:wrapper(*WebKit::toImpl(sourceURLRef)) toURL:wrapper(*WebKit::toImpl(destinationURLRef))];
}

static void didUpdateHistoryTitle(WKContextRef, WKPageRef pageRef, WKStringRef titleRef, WKURLRef urlRef, WKFrameRef frameRef, const void*)
{
if (!WebKit::toImpl(frameRef)->isMainFrame())
return;

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBrowsingContextController *controller = [WKBrowsingContextController _browsingContextControllerForPageRef:pageRef];
ALLOW_DEPRECATED_DECLARATIONS_END
auto historyDelegate = controller->_historyDelegate.get();

if ([historyDelegate respondsToSelector:@selector(browsingContextController:didUpdateHistoryTitle:forURL:)])
[historyDelegate browsingContextController:controller didUpdateHistoryTitle:wrapper(*WebKit::toImpl(titleRef)) forURL:wrapper(*WebKit::toImpl(urlRef))];
}

static void setUpHistoryClient(WKProcessGroup *processGroup, WKContextRef contextRef)
{
WKContextHistoryClientV0 historyClient;
memset(&historyClient, 0, sizeof(historyClient));

historyClient.base.version = 0;
historyClient.base.clientInfo = (__bridge CFTypeRef)processGroup;
historyClient.didNavigateWithNavigationData = didNavigateWithNavigationData;
historyClient.didPerformClientRedirect = didPerformClientRedirect;
historyClient.didPerformServerRedirect = didPerformServerRedirect;
historyClient.didUpdateHistoryTitle = didUpdateHistoryTitle;

WKContextSetHistoryClient(contextRef, &historyClient.base);
}

- (id)init
{
return [self initWithInjectedBundleURL:nil];
}

- (id)initWithInjectedBundleURL:(NSURL *)bundleURL
{
self = [super init];
if (!self)
return nil;

auto configuration = API::ProcessPoolConfiguration::create();
configuration->setInjectedBundlePath(bundleURL ? String(bundleURL.path) : String());

_processPool = WebKit::WebProcessPool::create(configuration);

setUpConnectionClient(self, toAPI(_processPool.get()));
setUpInjectedBundleClient(self, toAPI(_processPool.get()));
setUpHistoryClient(self, toAPI(_processPool.get()));

return self;
}

static Vector<WTF::String> toStringVector(NSSet *input)
{
Vector<WTF::String> vector;

NSUInteger size = input.count;
if (!size)
return vector;

vector.reserveInitialCapacity(size);
for (id classObj : input) {
if (auto* string = NSStringFromClass(classObj))
vector.uncheckedAppend(string);
}
return vector;
}

- (id)initWithInjectedBundleURL:(NSURL *)bundleURL andCustomClassesForParameterCoder:(NSSet *)classesForCoder
{
self = [super init];
if (!self)
return nil;

auto configuration = API::ProcessPoolConfiguration::create();
configuration->setInjectedBundlePath(bundleURL ? String(bundleURL.path) : String());
configuration->setCustomClassesForParameterCoder(toStringVector(classesForCoder));

_processPool = WebKit::WebProcessPool::create(configuration);

setUpConnectionClient(self, toAPI(_processPool.get()));
setUpInjectedBundleClient(self, toAPI(_processPool.get()));
setUpHistoryClient(self, toAPI(_processPool.get()));

return self;
}

- (id <WKProcessGroupDelegate>)delegate
{
return _delegate.getAutoreleased();
}

- (void)setDelegate:(id <WKProcessGroupDelegate>)delegate
{
_delegate = delegate;

// If the client can observe when the connection to the WebProcess injected bundle is established, then we cannot
// safely delay the launch of the WebProcess until something is loaded in the web view.
if ([delegate respondsToSelector:@selector(processGroup:didCreateConnectionToWebProcessPlugIn:)])
_processPool->disableDelayedWebProcessLaunch();
}

@end

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGIN
@implementation WKProcessGroup (Private)
ALLOW_DEPRECATED_IMPLEMENTATIONS_END

- (WKContextRef)_contextRef
{
return toAPI(_processPool.get());
}

- (void)_setAllowsSpecificHTTPSCertificate:(NSArray *)certificateChain forHost:(NSString *)host
{
_processPool->allowSpecificHTTPSCertificateForHost(WebKit::WebCertificateInfo::create(WebCore::CertificateInfo((__bridge CFArrayRef)certificateChain)).ptr(), host);
}

#if PLATFORM(IOS_FAMILY)
- (WKGeolocationProviderIOS *)_geolocationProvider
{
if (!_geolocationProvider)
_geolocationProvider = adoptNS([[WKGeolocationProviderIOS alloc] initWithProcessPool:*_processPool.get()]);
return _geolocationProvider.get();
}
#endif // PLATFORM(IOS_FAMILY)

@end
ALLOW_DEPRECATED_DECLARATIONS_END