fdeae42eeae3edd0280380a236dae8d1e4afb487
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / Cocoa / WKBrowsingContextController.mm
1 /*
2  * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKBrowsingContextControllerInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "WeakObjCPtr.h"
32 #import "ObjCObjectGraph.h"
33 #import "WKBackForwardListInternal.h"
34 #import "WKBackForwardListItemInternal.h"
35 #import "WKErrorRecoveryAttempting.h"
36 #import "WKFrame.h"
37 #import "WKFramePolicyListener.h"
38 #import "WKNSArray.h"
39 #import "WKNSError.h"
40 #import "WKNSURLAuthenticationChallenge.h"
41 #import "WKNSURLExtras.h"
42 #import "WKNSURLProtectionSpace.h"
43 #import "WKRetainPtr.h"
44 #import "WKURLRequestNS.h"
45 #import "WKURLResponseNS.h"
46 #import "WebContext.h"
47 #import "WebData.h"
48 #import "WebPageProxy.h"
49
50 #import "WKBrowsingContextGroupInternal.h"
51 #import "WKBrowsingContextHandleInternal.h"
52 #import "WKBrowsingContextLoadDelegatePrivate.h"
53 #import "WKBrowsingContextPolicyDelegate.h"
54 #import "WKProcessGroupInternal.h"
55
56 using namespace WebCore;
57 using namespace WebKit;
58
59 class PageLoadStateObserver : public PageLoadState::Observer {
60 public:
61     PageLoadStateObserver(WKBrowsingContextController *controller)
62         : m_controller(controller)
63     {
64     }
65
66 private:
67     virtual void willChangeIsLoading() OVERRIDE
68     {
69         [m_controller willChangeValueForKey:@"loading"];
70     }
71
72     virtual void didChangeIsLoading() OVERRIDE
73     {
74         [m_controller didChangeValueForKey:@"loading"];
75     }
76
77     virtual void willChangeTitle() OVERRIDE
78     {
79         [m_controller willChangeValueForKey:@"title"];
80     }
81
82     virtual void didChangeTitle() OVERRIDE
83     {
84         [m_controller didChangeValueForKey:@"title"];
85     }
86
87     virtual void willChangeActiveURL() OVERRIDE
88     {
89         [m_controller willChangeValueForKey:@"activeURL"];
90     }
91
92     virtual void didChangeActiveURL() OVERRIDE
93     {
94         [m_controller didChangeValueForKey:@"activeURL"];
95     }
96
97     virtual void willChangeEstimatedProgress() OVERRIDE
98     {
99         [m_controller willChangeValueForKey:@"estimatedProgress"];
100     }
101
102     virtual void didChangeEstimatedProgress() OVERRIDE
103     {
104         [m_controller didChangeValueForKey:@"estimatedProgress"];
105     }
106
107     WKBrowsingContextController *m_controller;
108 };
109
110 NSString * const WKActionIsMainFrameKey = @"WKActionIsMainFrameKey";
111 NSString * const WKActionNavigationTypeKey = @"WKActionNavigationTypeKey";
112 NSString * const WKActionMouseButtonKey = @"WKActionMouseButtonKey";
113 NSString * const WKActionModifierFlagsKey = @"WKActionModifierFlagsKey";
114 NSString * const WKActionOriginalURLRequestKey = @"WKActionOriginalURLRequestKey";
115 NSString * const WKActionURLRequestKey = @"WKActionURLRequestKey";
116 NSString * const WKActionURLResponseKey = @"WKActionURLResponseKey";
117 NSString * const WKActionFrameNameKey = @"WKActionFrameNameKey";
118 NSString * const WKActionOriginatingFrameURLKey = @"WKActionOriginatingFrameURLKey";
119 NSString * const WKActionCanShowMIMETypeKey = @"WKActionCanShowMIMETypeKey";
120
121 static NSString * const frameErrorKey = @"WKBrowsingContextFrameErrorKey";
122
123 @interface WKBrowsingContextController () <WKErrorRecoveryAttempting>
124 @end
125
126 @implementation WKBrowsingContextController {
127     API::ObjectStorage<WebPageProxy> _page;
128     std::unique_ptr<PageLoadStateObserver> _pageLoadStateObserver;
129
130     WeakObjCPtr<id <WKBrowsingContextLoadDelegate>> _loadDelegate;
131     WeakObjCPtr<id <WKBrowsingContextPolicyDelegate>> _policyDelegate;
132 }
133
134 - (void)dealloc
135 {
136     _page->pageLoadState().removeObserver(*_pageLoadStateObserver);
137     _page->~WebPageProxy();
138
139     [super dealloc];
140 }
141
142 - (void)_finishInitialization
143 {
144     _pageLoadStateObserver = std::make_unique<PageLoadStateObserver>(self);
145     _page->pageLoadState().addObserver(*_pageLoadStateObserver);
146 }
147
148 - (WKProcessGroup *)processGroup
149 {
150     return wrapper(_page->process().context());
151 }
152
153 - (WKBrowsingContextGroup *)browsingContextGroup
154 {
155     return wrapper(_page->pageGroup());
156 }
157
158 #pragma mark Loading
159
160 + (void)registerSchemeForCustomProtocol:(NSString *)scheme
161 {
162     if (!scheme)
163         return;
164
165     NSString *lowercaseScheme = [scheme lowercaseString];
166     [[WKBrowsingContextController customSchemes] addObject:lowercaseScheme];
167     [[NSNotificationCenter defaultCenter] postNotificationName:SchemeForCustomProtocolRegisteredNotificationName object:lowercaseScheme];
168 }
169
170 + (void)unregisterSchemeForCustomProtocol:(NSString *)scheme
171 {
172     if (!scheme)
173         return;
174
175     NSString *lowercaseScheme = [scheme lowercaseString];
176     [[WKBrowsingContextController customSchemes] removeObject:lowercaseScheme];
177     [[NSNotificationCenter defaultCenter] postNotificationName:SchemeForCustomProtocolUnregisteredNotificationName object:lowercaseScheme];
178 }
179
180 - (void)loadRequest:(NSURLRequest *)request
181 {
182     [self loadRequest:request userData:nil];
183 }
184
185 - (void)loadRequest:(NSURLRequest *)request userData:(id)userData
186 {
187     RefPtr<WebURLRequest> wkURLRequest = WebURLRequest::create(request);
188
189     RefPtr<ObjCObjectGraph> wkUserData;
190     if (userData)
191         wkUserData = ObjCObjectGraph::create(userData);
192
193     _page->loadURLRequest(wkURLRequest.get(), wkUserData.get());
194 }
195
196 - (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory
197 {
198     [self loadFileURL:URL restrictToFilesWithin:allowedDirectory userData:nil];
199 }
200
201 - (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory userData:(id)userData
202 {
203     if (![URL isFileURL] || (allowedDirectory && ![allowedDirectory isFileURL]))
204         [NSException raise:NSInvalidArgumentException format:@"Attempted to load a non-file URL"];
205
206     RefPtr<ObjCObjectGraph> wkUserData;
207     if (userData)
208         wkUserData = ObjCObjectGraph::create(userData);
209
210     _page->loadFile([URL _web_originalDataAsWTFString], [allowedDirectory _web_originalDataAsWTFString], wkUserData.get());
211 }
212
213 - (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL
214 {
215     [self loadHTMLString:HTMLString baseURL:baseURL userData:nil];
216 }
217
218 - (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL userData:(id)userData
219 {
220     RefPtr<ObjCObjectGraph> wkUserData;
221     if (userData)
222         wkUserData = ObjCObjectGraph::create(userData);
223
224     _page->loadHTMLString(HTMLString, [baseURL _web_originalDataAsWTFString], wkUserData.get());
225 }
226
227 - (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
228 {
229     _page->loadAlternateHTMLString(string, [baseURL _web_originalDataAsWTFString], [unreachableURL _web_originalDataAsWTFString]);
230 }
231
232 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
233 {
234     [self loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL userData:nil];
235 }
236
237 static void releaseNSData(unsigned char*, const void* data)
238 {
239     [(NSData *)data release];
240 }
241
242 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL userData:(id)userData
243 {
244     RefPtr<WebData> wkData;
245     if (data) {
246         [data retain];
247         wkData = WebData::createWithoutCopying((const unsigned char*)[data bytes], [data length], releaseNSData, data);
248     }
249
250     RefPtr<ObjCObjectGraph> wkUserData;
251     if (userData)
252         wkUserData = ObjCObjectGraph::create(userData);
253
254     _page->loadData(wkData.get(), MIMEType, encodingName, [baseURL _web_originalDataAsWTFString], wkUserData.get());
255 }
256
257 - (void)stopLoading
258 {
259     _page->stopLoading();
260 }
261
262 - (void)reload
263 {
264     _page->reload(false);
265 }
266
267 - (void)reloadFromOrigin
268 {
269     _page->reload(true);
270 }
271
272 #pragma mark Back/Forward
273
274 - (void)goForward
275 {
276     _page->goForward();
277 }
278
279 - (BOOL)canGoForward
280 {
281     return _page->canGoForward();
282 }
283
284 - (void)goBack
285 {
286     _page->goBack();
287 }
288
289 - (BOOL)canGoBack
290 {
291     return _page->canGoBack();
292 }
293
294 - (void)goToBackForwardListItem:(WKBackForwardListItem *)item
295 {
296     _page->goToBackForwardItem(&item._item);
297 }
298
299 - (WKBackForwardList *)backForwardList
300 {
301     return wrapper(_page->backForwardList());
302 }
303
304 #pragma mark Active Load Introspection
305
306 - (BOOL)isLoading
307 {
308     return _page->pageLoadState().isLoading();
309 }
310
311 - (NSURL *)activeURL
312 {
313     return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
314 }
315
316 - (NSURL *)provisionalURL
317 {
318     return [NSURL _web_URLWithWTFString:_page->pageLoadState().provisionalURL()];
319 }
320
321 - (NSURL *)committedURL
322 {
323     return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
324 }
325
326 - (NSURL *)unreachableURL
327 {
328     return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
329 }
330
331 - (double)estimatedProgress
332 {
333     return _page->estimatedProgress();
334 }
335
336 #pragma mark Active Document Introspection
337
338 - (NSString *)title
339 {
340     return _page->pageLoadState().title();
341 }
342
343 #pragma mark Zoom
344
345 - (CGFloat)textZoom
346 {
347     return _page->textZoomFactor();
348 }
349
350 - (void)setTextZoom:(CGFloat)textZoom
351 {
352     _page->setTextZoomFactor(textZoom);
353 }
354
355 - (CGFloat)pageZoom
356 {
357     return _page->pageZoomFactor();
358 }
359
360 - (void)setPageZoom:(CGFloat)pageZoom
361 {
362     _page->setPageZoomFactor(pageZoom);
363 }
364
365 static NSError *createErrorWithRecoveryAttempter(WKErrorRef wkError, WKFrameRef frame, WKBrowsingContextController *browsingContext)
366 {
367     NSError *error = wrapper(*toImpl(wkError));
368     NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
369         browsingContext, WKRecoveryAttempterErrorKey,
370         toImpl(frame)->wrapper(), frameErrorKey,
371     nil];
372
373     if (NSDictionary *originalUserInfo = error.userInfo)
374         [userInfo addEntriesFromDictionary:originalUserInfo];
375
376     return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:userInfo];
377 }
378
379 static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
380 {
381     if (!WKFrameIsMainFrame(frame))
382         return;
383
384     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
385     auto loadDelegate = browsingContext->_loadDelegate.get();
386
387     if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProvisionalLoad:)])
388         [loadDelegate browsingContextControllerDidStartProvisionalLoad:browsingContext];
389 }
390
391 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
392 {
393     if (!WKFrameIsMainFrame(frame))
394         return;
395
396     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
397     auto loadDelegate = browsingContext->_loadDelegate.get();
398
399     if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:)])
400         [loadDelegate browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:browsingContext];
401 }
402
403 static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
404 {
405     if (!WKFrameIsMainFrame(frame))
406         return;
407
408     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
409     auto loadDelegate = browsingContext->_loadDelegate.get();
410
411     if ([loadDelegate respondsToSelector:@selector(browsingContextController:didFailProvisionalLoadWithError:)]) {
412         RetainPtr<NSError> nsError = adoptNS(createErrorWithRecoveryAttempter(error, frame, browsingContext));
413         [loadDelegate browsingContextController:browsingContext didFailProvisionalLoadWithError:nsError.get()];
414     }
415 }
416
417 static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
418 {
419     if (!WKFrameIsMainFrame(frame))
420         return;
421
422     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
423     auto loadDelegate = browsingContext->_loadDelegate.get();
424
425     if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidCommitLoad:)])
426         [loadDelegate browsingContextControllerDidCommitLoad:browsingContext];
427 }
428
429 static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
430 {
431     if (!WKFrameIsMainFrame(frame))
432         return;
433
434     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
435     auto loadDelegate = browsingContext->_loadDelegate.get();
436
437     if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishLoad:)])
438         [loadDelegate browsingContextControllerDidFinishLoad:browsingContext];
439 }
440
441 static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
442 {
443     if (!WKFrameIsMainFrame(frame))
444         return;
445
446     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
447     auto loadDelegate = browsingContext->_loadDelegate.get();
448
449     if ([loadDelegate respondsToSelector:@selector(browsingContextController:didFailLoadWithError:)]) {
450         RetainPtr<NSError> nsError = adoptNS(createErrorWithRecoveryAttempter(error, frame, browsingContext));
451         [loadDelegate browsingContextController:browsingContext didFailLoadWithError:nsError.get()];
452     }
453 }
454
455 static bool canAuthenticateAgainstProtectionSpaceInFrame(WKPageRef page, WKFrameRef frame, WKProtectionSpaceRef protectionSpace, const void *clientInfo)
456 {
457     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
458     auto loadDelegate = browsingContext->_loadDelegate.get();
459
460     if ([loadDelegate respondsToSelector:@selector(browsingContextController:canAuthenticateAgainstProtectionSpace:)])
461         return [(id <WKBrowsingContextLoadDelegatePrivate>)loadDelegate browsingContextController:browsingContext canAuthenticateAgainstProtectionSpace:wrapper(*toImpl(protectionSpace))];
462
463     return false;
464 }
465
466 static void didReceiveAuthenticationChallengeInFrame(WKPageRef page, WKFrameRef frame, WKAuthenticationChallengeRef authenticationChallenge, const void *clientInfo)
467 {
468     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
469     auto loadDelegate = browsingContext->_loadDelegate.get();
470
471     if ([loadDelegate respondsToSelector:@selector(browsingContextController:didReceiveAuthenticationChallenge:)])
472         [(id <WKBrowsingContextLoadDelegatePrivate>)loadDelegate browsingContextController:browsingContext didReceiveAuthenticationChallenge:wrapper(*toImpl(authenticationChallenge))];
473 }
474
475 static void didStartProgress(WKPageRef page, const void* clientInfo)
476 {
477     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
478     auto loadDelegate = browsingContext->_loadDelegate.get();
479
480     if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProgress:)])
481         [loadDelegate browsingContextControllerDidStartProgress:browsingContext];
482 }
483
484 static void didChangeProgress(WKPageRef page, const void* clientInfo)
485 {
486     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
487     auto loadDelegate = browsingContext->_loadDelegate.get();
488
489     if ([loadDelegate respondsToSelector:@selector(browsingContextController:estimatedProgressChangedTo:)])
490         [loadDelegate browsingContextController:browsingContext estimatedProgressChangedTo:toImpl(page)->estimatedProgress()];
491 }
492
493 static void didFinishProgress(WKPageRef page, const void* clientInfo)
494 {
495     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
496     auto loadDelegate = browsingContext->_loadDelegate.get();
497
498     if ([loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishProgress:)])
499         [loadDelegate browsingContextControllerDidFinishProgress:browsingContext];
500 }
501
502 static void didChangeBackForwardList(WKPageRef page, WKBackForwardListItemRef addedItem, WKArrayRef removedItems, const void *clientInfo)
503 {
504     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
505     auto loadDelegate = browsingContext->_loadDelegate.get();
506
507     if (![loadDelegate respondsToSelector:@selector(browsingContextControllerDidChangeBackForwardList:addedItem:removedItems:)])
508         return;
509
510     WKBackForwardListItem *added = addedItem ? wrapper(*toImpl(addedItem)) : nil;
511     NSArray *removed = removedItems ? wrapper(*toImpl(removedItems)) : nil;
512     [loadDelegate browsingContextControllerDidChangeBackForwardList:browsingContext addedItem:added removedItems:removed];
513 }
514
515 static void setUpPageLoaderClient(WKBrowsingContextController *browsingContext, WebPageProxy& page)
516 {
517     WKPageLoaderClientV3 loaderClient;
518     memset(&loaderClient, 0, sizeof(loaderClient));
519
520     loaderClient.base.version = 3;
521     loaderClient.base.clientInfo = browsingContext;
522     loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
523     loaderClient.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
524     loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
525     loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
526     loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
527     loaderClient.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;
528
529     loaderClient.canAuthenticateAgainstProtectionSpaceInFrame = canAuthenticateAgainstProtectionSpaceInFrame;
530     loaderClient.didReceiveAuthenticationChallengeInFrame = didReceiveAuthenticationChallengeInFrame;
531
532     loaderClient.didStartProgress = didStartProgress;
533     loaderClient.didChangeProgress = didChangeProgress;
534     loaderClient.didFinishProgress = didFinishProgress;
535     loaderClient.didChangeBackForwardList = didChangeBackForwardList;
536
537     page.initializeLoaderClient(&loaderClient.base);
538 }
539
540 static WKPolicyDecisionHandler makePolicyDecisionBlock(WKFramePolicyListenerRef listener)
541 {
542     WKRetain(listener); // Released in the decision handler below.
543
544     return [[^(WKPolicyDecision decision) {
545         switch (decision) {
546         case WKPolicyDecisionCancel:
547             WKFramePolicyListenerIgnore(listener);                    
548             break;
549         
550         case WKPolicyDecisionAllow:
551             WKFramePolicyListenerUse(listener);
552             break;
553         
554         case WKPolicyDecisionBecomeDownload:
555             WKFramePolicyListenerDownload(listener);
556             break;
557         };
558
559         WKRelease(listener); // Retained in the context above.
560     } copy] autorelease];
561 }
562
563 static void setUpPagePolicyClient(WKBrowsingContextController *browsingContext, WebPageProxy& page)
564 {
565     WKPagePolicyClientInternal policyClient;
566     memset(&policyClient, 0, sizeof(policyClient));
567
568     policyClient.base.version = 2;
569     policyClient.base.clientInfo = browsingContext;
570
571     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)
572     {
573         WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
574         auto policyDelegate = browsingContext->_policyDelegate.get();
575
576         if ([policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForNavigationAction:decisionHandler:)]) {
577             NSDictionary *actionDictionary = @{
578                 WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
579                 WKActionNavigationTypeKey: @(navigationType),
580                 WKActionModifierFlagsKey: @(modifiers),
581                 WKActionMouseButtonKey: @(mouseButton),
582                 WKActionOriginalURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(originalRequest)).get(),
583                 WKActionURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(request)).get()
584             };
585
586             if (originatingFrame) {
587                 actionDictionary = [[actionDictionary mutableCopy] autorelease];
588                 [(NSMutableDictionary *)actionDictionary setObject:[NSURL _web_URLWithWTFString:toImpl(originatingFrame)->url()] forKey:WKActionOriginatingFrameURLKey];
589             }
590             
591             [policyDelegate browsingContextController:browsingContext decidePolicyForNavigationAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
592         } else
593             WKFramePolicyListenerUse(listener);
594     };
595
596     policyClient.decidePolicyForNewWindowAction = [](WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKStringRef frameName, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
597     {
598         WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
599         auto policyDelegate = browsingContext->_policyDelegate.get();
600
601         if ([policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForNewWindowAction:decisionHandler:)]) {
602             NSDictionary *actionDictionary = @{
603                 WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
604                 WKActionNavigationTypeKey: @(navigationType),
605                 WKActionModifierFlagsKey: @(modifiers),
606                 WKActionMouseButtonKey: @(mouseButton),
607                 WKActionURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(request)).get(),
608                 WKActionFrameNameKey: toImpl(frameName)->wrapper()
609             };
610             
611             [policyDelegate browsingContextController:browsingContext decidePolicyForNewWindowAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
612         } else
613             WKFramePolicyListenerUse(listener);
614     };
615
616     policyClient.decidePolicyForResponse = [](WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, bool canShowMIMEType, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
617     {
618         WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
619         auto policyDelegate = browsingContext->_policyDelegate.get();
620
621         if ([policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForResponseAction:decisionHandler:)]) {
622             NSDictionary *actionDictionary = @{
623                 WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
624                 WKActionURLRequestKey: adoptNS(WKURLRequestCopyNSURLRequest(request)).get(),
625                 WKActionURLResponseKey: adoptNS(WKURLResponseCopyNSURLResponse(response)).get(),
626                 WKActionCanShowMIMETypeKey: @(canShowMIMEType),
627             };
628
629             [policyDelegate browsingContextController:browsingContext decidePolicyForResponseAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
630         } else
631             WKFramePolicyListenerUse(listener);
632     };
633
634     page.initializePolicyClient(&policyClient.base);
635 }
636
637 - (id <WKBrowsingContextLoadDelegate>)loadDelegate
638 {
639     return _loadDelegate.getAutoreleased();
640 }
641
642 - (void)setLoadDelegate:(id <WKBrowsingContextLoadDelegate>)loadDelegate
643 {
644     _loadDelegate = loadDelegate;
645
646     if (loadDelegate)
647         setUpPageLoaderClient(self, *_page);
648     else
649         _page->initializeLoaderClient(nullptr);
650 }
651
652 - (id <WKBrowsingContextPolicyDelegate>)policyDelegate
653 {
654     return _policyDelegate.getAutoreleased();
655 }
656
657 - (void)setPolicyDelegate:(id <WKBrowsingContextPolicyDelegate>)policyDelegate
658 {
659     _policyDelegate = policyDelegate;
660
661     if (policyDelegate)
662         setUpPagePolicyClient(self, *_page);
663     else
664         _page->initializePolicyClient(nullptr);
665 }
666
667 - (id <WKBrowsingContextHistoryDelegate>)historyDelegate
668 {
669     return _historyDelegate.getAutoreleased();
670 }
671
672 - (void)setHistoryDelegate:(id <WKBrowsingContextHistoryDelegate>)historyDelegate
673 {
674     _historyDelegate = historyDelegate;
675 }
676
677 + (NSMutableSet *)customSchemes
678 {
679     static NSMutableSet *customSchemes = [[NSMutableSet alloc] init];
680     return customSchemes;
681 }
682
683 #pragma mark WKErrorRecoveryAttempting
684
685 - (BOOL)attemptRecoveryFromError:(NSError *)error
686 {
687     NSDictionary *userInfo = error.userInfo;
688
689     NSString *failingURLString = userInfo[NSURLErrorFailingURLStringErrorKey];
690     if (!failingURLString)
691         return NO;
692
693     NSObject <WKObject> *frame = userInfo[frameErrorKey];
694     if (![frame conformsToProtocol:@protocol(WKObject)])
695         return NO;
696
697     if (frame._apiObject.type() != API::Object::Type::Frame)
698         return NO;
699
700     WebFrameProxy& webFrame = *static_cast<WebFrameProxy*>(&frame._apiObject);
701     webFrame.loadURL(failingURLString);
702
703     return YES;
704 }
705
706 #pragma mark WKObject protocol implementation
707
708 - (API::Object&)_apiObject
709 {
710     return *reinterpret_cast<API::Object*>(&_page);
711 }
712
713 @end
714
715 @implementation WKBrowsingContextController (Private)
716
717 - (WKPageRef)_pageRef
718 {
719     return toAPI(_page.get());
720 }
721
722 - (void)setPaginationMode:(WKBrowsingContextPaginationMode)paginationMode
723 {
724     Pagination::Mode mode;
725     switch (paginationMode) {
726     case WKPaginationModeUnpaginated:
727         mode = Pagination::Unpaginated;
728         break;
729     case WKPaginationModeLeftToRight:
730         mode = Pagination::LeftToRightPaginated;
731         break;
732     case WKPaginationModeRightToLeft:
733         mode = Pagination::RightToLeftPaginated;
734         break;
735     case WKPaginationModeTopToBottom:
736         mode = Pagination::TopToBottomPaginated;
737         break;
738     case WKPaginationModeBottomToTop:
739         mode = Pagination::BottomToTopPaginated;
740         break;
741     default:
742         return;
743     }
744
745     _page->setPaginationMode(mode);
746 }
747
748 - (WKBrowsingContextPaginationMode)paginationMode
749 {
750     switch (_page->paginationMode()) {
751     case Pagination::Unpaginated:
752         return WKPaginationModeUnpaginated;
753     case Pagination::LeftToRightPaginated:
754         return WKPaginationModeLeftToRight;
755     case Pagination::RightToLeftPaginated:
756         return WKPaginationModeRightToLeft;
757     case Pagination::TopToBottomPaginated:
758         return WKPaginationModeTopToBottom;
759     case Pagination::BottomToTopPaginated:
760         return WKPaginationModeBottomToTop;
761     }
762
763     ASSERT_NOT_REACHED();
764     return WKPaginationModeUnpaginated;
765 }
766
767 - (void)setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
768 {
769     _page->setPaginationBehavesLikeColumns(behavesLikeColumns);
770 }
771
772 - (BOOL)paginationBehavesLikeColumns
773 {
774     return _page->paginationBehavesLikeColumns();
775 }
776
777 - (void)setPageLength:(CGFloat)pageLength
778 {
779     _page->setPageLength(pageLength);
780 }
781
782 - (CGFloat)pageLength
783 {
784     return _page->pageLength();
785 }
786
787 - (void)setGapBetweenPages:(CGFloat)gapBetweenPages
788 {
789     _page->setGapBetweenPages(gapBetweenPages);
790 }
791
792 - (CGFloat)gapBetweenPages
793 {
794     return _page->gapBetweenPages();
795 }
796
797 - (NSUInteger)pageCount
798 {
799     return _page->pageCount();
800 }
801
802 - (WKBrowsingContextHandle *)handle
803 {
804     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
805 }
806
807 @end
808
809 #endif // WK_API_ENABLED