WKBrowsingContextController.mm   [plain text]

/*
* Copyright (C) 2011, 2012, 2013 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 "WKBrowsingContextControllerInternal.h"

#if WK_API_ENABLED

#import "APIData.h"
#import "ObjCObjectGraph.h"
#import "PageLoadStateObserver.h"
#import "RemoteObjectRegistry.h"
#import "RemoteObjectRegistryMessages.h"
#import "WKBackForwardListInternal.h"
#import "WKBackForwardListItemInternal.h"
#import "WKBrowsingContextGroupInternal.h"
#import "WKBrowsingContextHandleInternal.h"
#import "WKBrowsingContextLoadDelegatePrivate.h"
#import "WKBrowsingContextPolicyDelegate.h"
#import "WKFrame.h"
#import "WKFramePolicyListener.h"
#import "WKNSArray.h"
#import "WKNSData.h"
#import "WKNSError.h"
#import "WKNSURLAuthenticationChallenge.h"
#import "WKNSURLExtras.h"
#import "WKPagePolicyClientInternal.h"
#import "WKProcessGroupPrivate.h"
#import "WKRetainPtr.h"
#import "WKURLRequestNS.h"
#import "WKURLResponseNS.h"
#import "WKViewInternal.h"
#import "WebCertificateInfo.h"
#import "WebPageProxy.h"
#import "WebProcessPool.h"
#import "WebProtectionSpace.h"
#import "_WKRemoteObjectRegistryInternal.h"
#import <wtf/NeverDestroyed.h>
#import <wtf/WeakObjCPtr.h>

using namespace WebCore;
using namespace WebKit;

NSString * const WKActionIsMainFrameKey = @"WKActionIsMainFrameKey";
NSString * const WKActionNavigationTypeKey = @"WKActionNavigationTypeKey";
NSString * const WKActionMouseButtonKey = @"WKActionMouseButtonKey";
NSString * const WKActionModifierFlagsKey = @"WKActionModifierFlagsKey";
NSString * const WKActionOriginalURLRequestKey = @"WKActionOriginalURLRequestKey";
NSString * const WKActionURLRequestKey = @"WKActionURLRequestKey";
NSString * const WKActionURLResponseKey = @"WKActionURLResponseKey";
NSString * const WKActionFrameNameKey = @"WKActionFrameNameKey";
NSString * const WKActionOriginatingFrameURLKey = @"WKActionOriginatingFrameURLKey";
NSString * const WKActionCanShowMIMETypeKey = @"WKActionCanShowMIMETypeKey";

@implementation WKBrowsingContextController {
RefPtr<WebPageProxy> _page;
std::unique_ptr<PageLoadStateObserver> _pageLoadStateObserver;

WeakObjCPtr<id <WKBrowsingContextLoadDelegate>> _loadDelegate;
WeakObjCPtr<id <WKBrowsingContextPolicyDelegate>> _policyDelegate;
}

static HashMap<WebPageProxy*, WKBrowsingContextController *>& browsingContextControllerMap()
{
static NeverDestroyed<HashMap<WebPageProxy*, WKBrowsingContextController *>> browsingContextControllerMap;
return browsingContextControllerMap;
}

- (void)dealloc
{
ASSERT(browsingContextControllerMap().get(_page.get()) == self);
browsingContextControllerMap().remove(_page.get());

_page->pageLoadState().removeObserver(*_pageLoadStateObserver);

[super dealloc];
}

#pragma mark Loading

+ (void)registerSchemeForCustomProtocol:(NSString *)scheme
{
WebProcessPool::registerGlobalURLSchemeAsHavingCustomProtocolHandlers(scheme);
}

+ (void)unregisterSchemeForCustomProtocol:(NSString *)scheme
{
WebProcessPool::unregisterGlobalURLSchemeAsHavingCustomProtocolHandlers(scheme);
}

- (void)loadRequest:(NSURLRequest *)request
{
[self loadRequest:request userData:nil];
}

- (void)loadRequest:(NSURLRequest *)request userData:(id)userData
{
RefPtr<ObjCObjectGraph> wkUserData;
if (userData)
wkUserData = ObjCObjectGraph::create(userData);

_page->loadRequest(request, ShouldOpenExternalURLsPolicy::ShouldNotAllow, wkUserData.get());
}

- (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory
{
[self loadFileURL:URL restrictToFilesWithin:allowedDirectory userData:nil];
}

- (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory userData:(id)userData
{
if (![URL isFileURL] || (allowedDirectory && ![allowedDirectory isFileURL]))
[NSException raise:NSInvalidArgumentException format:@"Attempted to load a non-file URL"];

RefPtr<ObjCObjectGraph> wkUserData;
if (userData)
wkUserData = ObjCObjectGraph::create(userData);

_page->loadFile([URL _web_originalDataAsWTFString], [allowedDirectory _web_originalDataAsWTFString], wkUserData.get());
}

- (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL
{
[self loadHTMLString:HTMLString baseURL:baseURL userData:nil];
}

- (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL userData:(id)userData
{
RefPtr<ObjCObjectGraph> wkUserData;
if (userData)
wkUserData = ObjCObjectGraph::create(userData);

_page->loadHTMLString(HTMLString, [baseURL _web_originalDataAsWTFString], wkUserData.get());
}

- (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
{
_page->loadAlternateHTMLString(string, baseURL, unreachableURL);
}

- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
{
[self loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL userData:nil];
}

- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL userData:(id)userData
{
RefPtr<API::Data> apiData;
if (data) {
// FIXME: This should copy the data.
apiData = API::Data::createWithoutCopying(data);
}

RefPtr<ObjCObjectGraph> wkUserData;
if (userData)
wkUserData = ObjCObjectGraph::create(userData);

_page->loadData(apiData.get(), MIMEType, encodingName, [baseURL _web_originalDataAsWTFString], wkUserData.get());
}

- (void)stopLoading
{
_page->stopLoading();
}

- (void)reload
{
_page->reload({ });
}

- (void)reloadFromOrigin
{
_page->reload(WebCore::ReloadOption::FromOrigin);
}

- (NSString *)applicationNameForUserAgent
{
const String& applicationName = _page->applicationNameForUserAgent();
return !applicationName ? nil : (NSString *)applicationName;
}

- (void)setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
{
_page->setApplicationNameForUserAgent(applicationNameForUserAgent);
}

- (NSString *)customUserAgent
{
const String& customUserAgent = _page->customUserAgent();
return !customUserAgent ? nil : (NSString *)customUserAgent;
}

- (void)setCustomUserAgent:(NSString *)customUserAgent
{
_page->setCustomUserAgent(customUserAgent);
}

#pragma mark Back/Forward

- (void)goForward
{
_page->goForward();
}

- (BOOL)canGoForward
{
return !!_page->backForwardList().forwardItem();
}

- (void)goBack
{
_page->goBack();
}

- (BOOL)canGoBack
{
return !!_page->backForwardList().backItem();
}

- (void)goToBackForwardListItem:(WKBackForwardListItem *)item
{
_page->goToBackForwardItem(item._item);
}

- (WKBackForwardList *)backForwardList
{
return wrapper(_page->backForwardList());
}

#pragma mark Active Load Introspection

- (BOOL)isLoading
{
return _page->pageLoadState().isLoading();
}

- (NSURL *)activeURL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
}

- (NSURL *)provisionalURL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().provisionalURL()];
}

- (NSURL *)committedURL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
}

- (NSURL *)unreachableURL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
}

- (BOOL)hasOnlySecureContent
{
return _page->pageLoadState().hasOnlySecureContent();
}

- (double)estimatedProgress
{
return _page->estimatedProgress();
}

#pragma mark Active Document Introspection

- (NSString *)title
{
return _page->pageLoadState().title();
}

- (NSArray *)certificateChain
{
if (WebFrameProxy* mainFrame = _page->mainFrame())
return mainFrame->certificateInfo() ? (__bridge NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;

return nil;
}

#pragma mark Zoom

- (CGFloat)textZoom
{
return _page->textZoomFactor();
}

- (void)setTextZoom:(CGFloat)textZoom
{
_page->setTextZoomFactor(textZoom);
}

- (CGFloat)pageZoom
{
return _page->pageZoomFactor();
}

- (void)setPageZoom:(CGFloat)pageZoom
{
_page->setPageZoomFactor(pageZoom);
}

static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
{
if (!WKFrameIsMainFrame(frame))
return;

auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProvisionalLoad:)])
[loadDelegate browsingContextControllerDidStartProvisionalLoad:browsingContext];
}

static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
{
if (!WKFrameIsMainFrame(frame))
return;

auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:)])
[loadDelegate browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:browsingContext];
}

static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
{
if (!WKFrameIsMainFrame(frame))
return;

auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextController:didFailProvisionalLoadWithError:)])
[loadDelegate browsingContextController:browsingContext didFailProvisionalLoadWithError:wrapper(*toImpl(error))];
}

static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
{
if (!WKFrameIsMainFrame(frame))
return;

auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidCommitLoad:)])
[loadDelegate browsingContextControllerDidCommitLoad:browsingContext];
}

static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
{
if (!WKFrameIsMainFrame(frame))
return;

auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishLoad:)])
[loadDelegate browsingContextControllerDidFinishLoad:browsingContext];
}

static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
{
if (!WKFrameIsMainFrame(frame))
return;

auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextController:didFailLoadWithError:)])
[loadDelegate browsingContextController:browsingContext didFailLoadWithError:wrapper(*toImpl(error))];
}

static bool canAuthenticateAgainstProtectionSpaceInFrame(WKPageRef page, WKFrameRef frame, WKProtectionSpaceRef protectionSpace, const void *clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextController:canAuthenticateAgainstProtectionSpace:)])
return [(id <WKBrowsingContextLoadDelegatePrivate>)loadDelegate browsingContextController:browsingContext canAuthenticateAgainstProtectionSpace:toImpl(protectionSpace)->protectionSpace().nsSpace()];

return false;
}

static void didReceiveAuthenticationChallengeInFrame(WKPageRef page, WKFrameRef frame, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextController:didReceiveAuthenticationChallenge:)])
[(id <WKBrowsingContextLoadDelegatePrivate>)loadDelegate browsingContextController:browsingContext didReceiveAuthenticationChallenge:wrapper(*toImpl(authenticationChallenge))];
}

static void didStartProgress(WKPageRef page, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProgress:)])
[loadDelegate browsingContextControllerDidStartProgress:browsingContext];
}

static void didChangeProgress(WKPageRef page, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextController:estimatedProgressChangedTo:)])
[loadDelegate browsingContextController:browsingContext estimatedProgressChangedTo:toImpl(page)->estimatedProgress()];
}

static void didFinishProgress(WKPageRef page, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishProgress:)])
[loadDelegate browsingContextControllerDidFinishProgress:browsingContext];
}

static void didChangeBackForwardList(WKPageRef page, WKBackForwardListItemRef addedItem, WKArrayRef removedItems, const void *clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if (![loadDelegate respondsToSelector:@selector(browsingContextControllerDidChangeBackForwardList:addedItem:removedItems:)])
return;

WKBackForwardListItem *added = addedItem ? wrapper(*toImpl(addedItem)) : nil;
NSArray *removed = removedItems ? wrapper(*toImpl(removedItems)) : nil;
[loadDelegate browsingContextControllerDidChangeBackForwardList:browsingContext addedItem:added removedItems:removed];
}

static void processDidCrash(WKPageRef page, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto loadDelegate = browsingContext->_loadDelegate.get();

if ([loadDelegate respondsToSelector:@selector(browsingContextControllerWebProcessDidCrash:)])
[(id <WKBrowsingContextLoadDelegatePrivate>)loadDelegate browsingContextControllerWebProcessDidCrash:browsingContext];
}

static void setUpPageLoaderClient(WKBrowsingContextController *browsingContext, WebPageProxy& page)
{
WKPageLoaderClientV4 loaderClient;
memset(&loaderClient, 0, sizeof(loaderClient));

loaderClient.base.version = 4;
loaderClient.base.clientInfo = (__bridge void*)browsingContext;
loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
loaderClient.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
loaderClient.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;

loaderClient.canAuthenticateAgainstProtectionSpaceInFrame = canAuthenticateAgainstProtectionSpaceInFrame;
loaderClient.didReceiveAuthenticationChallengeInFrame = didReceiveAuthenticationChallengeInFrame;

loaderClient.didStartProgress = didStartProgress;
loaderClient.didChangeProgress = didChangeProgress;
loaderClient.didFinishProgress = didFinishProgress;
loaderClient.didChangeBackForwardList = didChangeBackForwardList;

loaderClient.processDidCrash = processDidCrash;

WKPageSetPageLoaderClient(toAPI(&page), &loaderClient.base);
}

static WKPolicyDecisionHandler makePolicyDecisionBlock(WKFramePolicyListenerRef listener)
{
WKRetain(listener); // Released in the decision handler below.

return [[^(WKPolicyDecision decision) {
switch (decision) {
case WKPolicyDecisionCancel:
WKFramePolicyListenerIgnore(listener);
break;

case WKPolicyDecisionAllow:
WKFramePolicyListenerUse(listener);
break;

case WKPolicyDecisionBecomeDownload:
WKFramePolicyListenerDownload(listener);
break;
};

WKRelease(listener); // Retained in the context above.
} copy] autorelease];
}

static void setUpPagePolicyClient(WKBrowsingContextController *browsingContext, WebPageProxy& page)
{
WKPagePolicyClientInternal policyClient;
memset(&policyClient, 0, sizeof(policyClient));

policyClient.base.version = 2;
policyClient.base.clientInfo = (__bridge void*)browsingContext;

policyClient.decidePolicyForNavigationAction = [](WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKFrameRef originatingFrame, WKURLRequestRef originalRequest, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto policyDelegate = browsingContext->_policyDelegate.get();

if ([policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForNavigationAction:decisionHandler:)]) {
NSDictionary *actionDictionary = @{
WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
WKActionNavigationTypeKey: @(navigationType),
WKActionModifierFlagsKey: @(modifiers),
WKActionMouseButtonKey: @(mouseButton),
WKActionOriginalURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(originalRequest)).get(),
WKActionURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(request)).get()
};

if (originatingFrame) {
actionDictionary = [[actionDictionary mutableCopy] autorelease];
[(NSMutableDictionary *)actionDictionary setObject:[NSURL _web_URLWithWTFString:toImpl(originatingFrame)->url()] forKey:WKActionOriginatingFrameURLKey];
}

[policyDelegate browsingContextController:browsingContext decidePolicyForNavigationAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
} else
WKFramePolicyListenerUse(listener);
};

policyClient.decidePolicyForNewWindowAction = [](WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKStringRef frameName, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto policyDelegate = browsingContext->_policyDelegate.get();

if ([policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForNewWindowAction:decisionHandler:)]) {
NSDictionary *actionDictionary = @{
WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
WKActionNavigationTypeKey: @(navigationType),
WKActionModifierFlagsKey: @(modifiers),
WKActionMouseButtonKey: @(mouseButton),
WKActionURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(request)).get(),
WKActionFrameNameKey: toImpl(frameName)->wrapper()
};

[policyDelegate browsingContextController:browsingContext decidePolicyForNewWindowAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
} else
WKFramePolicyListenerUse(listener);
};

policyClient.decidePolicyForResponse = [](WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, bool canShowMIMEType, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
{
auto browsingContext = (__bridge WKBrowsingContextController *)clientInfo;
auto policyDelegate = browsingContext->_policyDelegate.get();

if ([policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForResponseAction:decisionHandler:)]) {
NSDictionary *actionDictionary = @{
WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
WKActionURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(request)).get(),
WKActionURLResponseKey: adoptNS(WKURLResponseCopyNSURLResponse(response)).get(),
WKActionCanShowMIMETypeKey: @(canShowMIMEType),
};

[policyDelegate browsingContextController:browsingContext decidePolicyForResponseAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
} else
WKFramePolicyListenerUse(listener);
};

WKPageSetPagePolicyClient(toAPI(&page), &policyClient.base);
}

- (id <WKBrowsingContextLoadDelegate>)loadDelegate
{
return _loadDelegate.getAutoreleased();
}

- (void)setLoadDelegate:(id <WKBrowsingContextLoadDelegate>)loadDelegate
{
_loadDelegate = loadDelegate;

if (loadDelegate)
setUpPageLoaderClient(self, *_page);
else
WKPageSetPageLoaderClient(toAPI(_page.get()), nullptr);
}

- (id <WKBrowsingContextPolicyDelegate>)policyDelegate
{
return _policyDelegate.getAutoreleased();
}

- (void)setPolicyDelegate:(id <WKBrowsingContextPolicyDelegate>)policyDelegate
{
_policyDelegate = policyDelegate;

if (policyDelegate)
setUpPagePolicyClient(self, *_page);
else
WKPageSetPagePolicyClient(toAPI(_page.get()), nullptr);
}

- (id <WKBrowsingContextHistoryDelegate>)historyDelegate
{
return _historyDelegate.getAutoreleased();
}

- (void)setHistoryDelegate:(id <WKBrowsingContextHistoryDelegate>)historyDelegate
{
_historyDelegate = historyDelegate;
}

+ (NSMutableSet *)customSchemes
{
static NSMutableSet *customSchemes = [[NSMutableSet alloc] init];
return customSchemes;
}

- (instancetype)_initWithPageRef:(WKPageRef)pageRef
{
if (!(self = [super init]))
return nil;

_page = toImpl(pageRef);

_pageLoadStateObserver = std::make_unique<PageLoadStateObserver>(self);
_page->pageLoadState().addObserver(*_pageLoadStateObserver);

ASSERT(!browsingContextControllerMap().contains(_page.get()));
browsingContextControllerMap().set(_page.get(), self);

return self;
}

+ (WKBrowsingContextController *)_browsingContextControllerForPageRef:(WKPageRef)pageRef
{
return browsingContextControllerMap().get(toImpl(pageRef));
}

@end

@implementation WKBrowsingContextController (Private)

- (WKPageRef)_pageRef
{
return toAPI(_page.get());
}

- (void)setPaginationMode:(WKBrowsingContextPaginationMode)paginationMode
{
Pagination::Mode mode;
switch (paginationMode) {
case WKPaginationModeUnpaginated:
mode = Pagination::Unpaginated;
break;
case WKPaginationModeLeftToRight:
mode = Pagination::LeftToRightPaginated;
break;
case WKPaginationModeRightToLeft:
mode = Pagination::RightToLeftPaginated;
break;
case WKPaginationModeTopToBottom:
mode = Pagination::TopToBottomPaginated;
break;
case WKPaginationModeBottomToTop:
mode = Pagination::BottomToTopPaginated;
break;
default:
return;
}

_page->setPaginationMode(mode);
}

- (WKBrowsingContextPaginationMode)paginationMode
{
switch (_page->paginationMode()) {
case Pagination::Unpaginated:
return WKPaginationModeUnpaginated;
case Pagination::LeftToRightPaginated:
return WKPaginationModeLeftToRight;
case Pagination::RightToLeftPaginated:
return WKPaginationModeRightToLeft;
case Pagination::TopToBottomPaginated:
return WKPaginationModeTopToBottom;
case Pagination::BottomToTopPaginated:
return WKPaginationModeBottomToTop;
}

ASSERT_NOT_REACHED();
return WKPaginationModeUnpaginated;
}

- (void)setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
{
_page->setPaginationBehavesLikeColumns(behavesLikeColumns);
}

- (BOOL)paginationBehavesLikeColumns
{
return _page->paginationBehavesLikeColumns();
}

- (void)setPageLength:(CGFloat)pageLength
{
_page->setPageLength(pageLength);
}

- (CGFloat)pageLength
{
return _page->pageLength();
}

- (void)setGapBetweenPages:(CGFloat)gapBetweenPages
{
_page->setGapBetweenPages(gapBetweenPages);
}

- (CGFloat)gapBetweenPages
{
return _page->gapBetweenPages();
}

- (void)setPaginationLineGridEnabled:(BOOL)lineGridEnabled
{
_page->setPaginationLineGridEnabled(lineGridEnabled);
}

- (BOOL)paginationLineGridEnabled
{
return _page->paginationLineGridEnabled();
}

- (NSUInteger)pageCount
{
return _page->pageCount();
}

- (WKBrowsingContextHandle *)handle
{
return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
}

- (_WKRemoteObjectRegistry *)_remoteObjectRegistry
{
#if WK_API_ENABLED && !TARGET_OS_IPHONE
return _page->remoteObjectRegistry();
#else
return nil;
#endif
}

- (pid_t)processIdentifier
{
return _page->processIdentifier();
}

- (BOOL)_webProcessIsResponsive
{
return _page->process().isResponsive();
}

@end

#endif // WK_API_ENABLED