6416bd6cf7185c6d994ce6d3e41d4da180bf6a11
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / mac / WKBrowsingContextController.mm
1 /*
2  * Copyright (C) 2011 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 #import "ObjCObjectGraph.h"
30 #import "WKBackForwardListInternal.h"
31 #import "WKBackForwardListItemInternal.h"
32 #import "WKErrorCF.h"
33 #import "WKFrame.h"
34 #import "WKFramePolicyListener.h"
35 #import "WKNSArray.h"
36 #import "WKPagePrivate.h"
37 #import "WKRetainPtr.h"
38 #import "WKStringCF.h"
39 #import "WKURLCF.h"
40 #import "WKURLRequest.h"
41 #import "WKURLRequestNS.h"
42 #import "WKURLResponse.h"
43 #import "WKURLResponseNS.h"
44 #import "WebContext.h"
45 #import "WebData.h"
46 #import "WebPageProxy.h"
47 #import <wtf/ObjcRuntimeExtras.h>
48 #import <wtf/RetainPtr.h>
49
50 #import "WKBrowsingContextHandleInternal.h"
51 #import "WKBrowsingContextLoadDelegate.h"
52 #import "WKBrowsingContextPolicyDelegate.h"
53
54 using namespace WebKit;
55
56 static inline NSString *autoreleased(WKStringRef string)
57 {
58     return string ? CFBridgingRelease(WKStringCopyCFString(kCFAllocatorDefault, adoptWK(string).get())) : nil;
59 }
60
61 static inline NSURL *autoreleased(WKURLRef url)
62 {
63     return url ? CFBridgingRelease(WKURLCopyCFURL(kCFAllocatorDefault, adoptWK(url).get())) : nil;
64 }
65
66 static inline NSURLRequest *autoreleased(WKURLRequestRef urlRequest)
67 {
68     return urlRequest ? CFBridgingRelease(WKURLRequestCopyNSURLRequest(adoptWK(urlRequest).get())) : nil;
69 }
70
71 static inline NSURLResponse *autoreleased(WKURLResponseRef urlResponse)
72 {
73     return urlResponse ? CFBridgingRelease(WKURLResponseCopyNSURLResponse(adoptWK(urlResponse).get())) : nil;
74 }
75
76 NSString *WKActionIsMainFrameKey = @"WKActionIsMainFrameKey";
77 NSString *WKActionNavigationTypeKey = @"WKActionNavigationTypeKey";
78 NSString *WKActionMouseButtonKey = @"WKActionMouseButtonKey";
79 NSString *WKActionModifierFlagsKey = @"WKActionModifierFlagsKey";
80 NSString *WKActionURLRequestKey = @"WKActionURLRequestKey";
81 NSString *WKActionURLResponseKey = @"WKActionURLResponseKey";
82 NSString *WKActionFrameNameKey = @"WKActionFrameNameKey";
83
84 @interface WKBrowsingContextControllerData : NSObject {
85 @public
86     // Underlying WKPageRef.
87     WKRetainPtr<WKPageRef> _pageRef;
88     
89     // Delegate for load callbacks.
90     id<WKBrowsingContextLoadDelegate> _loadDelegate;
91
92 #if WK_API_ENABLED
93     // Delegate for policy callbacks.
94     id<WKBrowsingContextPolicyDelegate> _policyDelegate;
95 #endif
96 }
97 @end
98
99 @implementation WKBrowsingContextControllerData
100 @end
101
102
103 @implementation WKBrowsingContextController
104
105 - (void)dealloc
106 {
107     WKPageSetPageLoaderClient(_data->_pageRef.get(), nullptr);
108
109 #if WK_API_ENABLED
110     WKPageSetPagePolicyClient(_data->_pageRef.get(), nullptr);
111 #endif
112
113     [_data release];
114     [super dealloc];
115 }
116
117 - (WKPageRef)_pageRef
118 {
119     return _data->_pageRef.get();
120 }
121
122 #pragma mark Delegates
123
124 - (id<WKBrowsingContextLoadDelegate>)loadDelegate
125 {
126     return _data->_loadDelegate;
127 }
128
129 - (void)setLoadDelegate:(id<WKBrowsingContextLoadDelegate>)loadDelegate
130 {
131     _data->_loadDelegate = loadDelegate;
132 }
133
134 #if WK_API_ENABLED
135 - (id<WKBrowsingContextPolicyDelegate>)policyDelegate
136 {
137     return _data->_policyDelegate;
138 }
139
140 - (void)setPolicyDelegate:(id<WKBrowsingContextPolicyDelegate>)policyDelegate
141 {
142     _data->_policyDelegate = policyDelegate;
143 }
144 #endif
145
146 #pragma mark Loading
147
148 + (void)registerSchemeForCustomProtocol:(NSString *)scheme
149 {
150     if (!scheme)
151         return;
152
153     NSString *lowercaseScheme = [scheme lowercaseString];
154     [[WKBrowsingContextController customSchemes] addObject:lowercaseScheme];
155     [[NSNotificationCenter defaultCenter] postNotificationName:SchemeForCustomProtocolRegisteredNotificationName object:lowercaseScheme];
156 }
157
158 + (void)unregisterSchemeForCustomProtocol:(NSString *)scheme
159 {
160     if (!scheme)
161         return;
162
163     NSString *lowercaseScheme = [scheme lowercaseString];
164     [[WKBrowsingContextController customSchemes] removeObject:lowercaseScheme];
165     [[NSNotificationCenter defaultCenter] postNotificationName:SchemeForCustomProtocolUnregisteredNotificationName object:lowercaseScheme];
166 }
167
168 - (void)loadRequest:(NSURLRequest *)request
169 {
170     [self loadRequest:request userData:nil];
171 }
172
173 - (void)loadRequest:(NSURLRequest *)request userData:(id)userData
174 {
175     WKRetainPtr<WKURLRequestRef> wkRequest = adoptWK(WKURLRequestCreateWithNSURLRequest(request));
176
177     RefPtr<ObjCObjectGraph> wkUserData;
178     if (userData)
179         wkUserData = ObjCObjectGraph::create(userData);
180
181     WKPageLoadURLRequestWithUserData(self._pageRef, wkRequest.get(), (WKTypeRef)wkUserData.get());
182 }
183
184 - (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory
185 {
186     [self loadFileURL:URL restrictToFilesWithin:allowedDirectory userData:nil];
187 }
188
189 - (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory userData:(id)userData
190 {
191     if (![URL isFileURL] || (allowedDirectory && ![allowedDirectory isFileURL]))
192         [NSException raise:NSInvalidArgumentException format:@"Attempted to load a non-file URL"];
193
194     WKRetainPtr<WKURLRef> wkURL = adoptWK(WKURLCreateWithCFURL((CFURLRef)URL));
195     WKRetainPtr<WKURLRef> wkAllowedDirectory = adoptWK(WKURLCreateWithCFURL((CFURLRef)allowedDirectory));
196     
197     RefPtr<ObjCObjectGraph> wkUserData;
198     if (userData)
199         wkUserData = ObjCObjectGraph::create(userData);
200
201     WKPageLoadFileWithUserData(self._pageRef, wkURL.get(), wkAllowedDirectory.get(), (WKTypeRef)wkUserData.get());
202 }
203
204 - (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL
205 {
206     [self loadHTMLString:HTMLString baseURL:baseURL userData:nil];
207 }
208
209 - (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL userData:(id)userData
210 {
211     WKRetainPtr<WKStringRef> wkHTMLString;
212     if (HTMLString)
213         wkHTMLString = adoptWK(WKStringCreateWithCFString((CFStringRef)HTMLString));
214
215     WKRetainPtr<WKURLRef> wkBaseURL;
216     if (baseURL)
217         wkBaseURL = adoptWK(WKURLCreateWithCFURL((CFURLRef)baseURL));
218
219     RefPtr<ObjCObjectGraph> wkUserData;
220     if (userData)
221         wkUserData = ObjCObjectGraph::create(userData);
222
223     WKPageLoadHTMLStringWithUserData(self._pageRef, wkHTMLString.get(), wkBaseURL.get(), (WKTypeRef)wkUserData.get());
224 }
225
226 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
227 {
228     [self loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL userData:nil];
229 }
230
231 static void releaseNSData(unsigned char*, const void* data)
232 {
233     [(NSData *)data release];
234 }
235
236 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL userData:(id)userData
237 {
238     RefPtr<WebData> wkData;
239     if (data) {
240         [data retain];
241         wkData = WebData::createWithoutCopying((const unsigned char*)[data bytes], [data length], releaseNSData, data);
242     }
243
244     WKRetainPtr<WKStringRef> wkMIMEType;
245     if (MIMEType)
246         wkMIMEType = adoptWK(WKStringCreateWithCFString((CFStringRef)MIMEType));
247
248     WKRetainPtr<WKStringRef> wkEncodingName;
249     if (encodingName)
250         wkEncodingName = adoptWK(WKStringCreateWithCFString((CFStringRef)encodingName));
251
252     WKRetainPtr<WKURLRef> wkBaseURL;
253     if (baseURL)
254         wkBaseURL = adoptWK(WKURLCreateWithCFURL((CFURLRef)baseURL));
255
256     RefPtr<ObjCObjectGraph> wkUserData;
257     if (userData)
258         wkUserData = ObjCObjectGraph::create(userData);
259
260     WKPageLoadDataWithUserData(self._pageRef, toAPI(wkData.get()), wkMIMEType.get(), wkEncodingName.get(), wkBaseURL.get(), (WKTypeRef)wkUserData.get());
261 }
262
263 - (void)stopLoading
264 {
265     WKPageStopLoading(self._pageRef);
266 }
267
268 - (void)reload
269 {
270     WKPageReload(self._pageRef);
271 }
272
273 - (void)reloadFromOrigin
274 {
275     WKPageReloadFromOrigin(self._pageRef);
276 }
277
278 #pragma mark Back/Forward
279
280 - (void)goForward
281 {
282     WKPageGoForward(self._pageRef);
283 }
284
285 - (BOOL)canGoForward
286 {
287     return WKPageCanGoForward(self._pageRef);
288 }
289
290 - (void)goBack
291 {
292     WKPageGoBack(self._pageRef);
293 }
294
295 - (BOOL)canGoBack
296 {
297     return WKPageCanGoBack(self._pageRef);
298 }
299
300 #if WK_API_ENABLED
301 - (void)goToBackForwardListItem:(WKBackForwardListItem *)item
302 {
303     toImpl(self._pageRef)->goToBackForwardItem(&item._item);
304 }
305
306 - (WKBackForwardList *)backForwardList
307 {
308     WebBackForwardList* list = toImpl(self._pageRef)->backForwardList();
309     if (!list)
310         return nil;
311
312     return wrapper(*list);
313 }
314 #endif // WK_API_ENABLED
315
316 #pragma mark Active Load Introspection
317
318 - (NSURL *)activeURL
319 {
320     return autoreleased(WKPageCopyActiveURL(self._pageRef));
321 }
322
323 - (NSURL *)provisionalURL
324 {
325     return autoreleased(WKPageCopyProvisionalURL(self._pageRef));
326 }
327
328 - (NSURL *)committedURL
329 {
330     return autoreleased(WKPageCopyCommittedURL(self._pageRef));
331 }
332
333 - (NSURL *)unreachableURL
334 {
335     const String& unreachableURL = toImpl(_data->_pageRef.get())->unreachableURL();
336     if (!unreachableURL)
337         return nil;
338
339     return !unreachableURL ? nil : [NSURL URLWithString:unreachableURL];
340 }
341
342 - (double)estimatedProgress
343 {
344     return toImpl(self._pageRef)->estimatedProgress();
345 }
346
347 #pragma mark Active Document Introspection
348
349 - (NSString *)title
350 {
351     return autoreleased(WKPageCopyTitle(self._pageRef));
352 }
353
354 #pragma mark Zoom
355
356 - (CGFloat)textZoom
357 {
358     return WKPageGetTextZoomFactor(self._pageRef);
359 }
360
361 - (void)setTextZoom:(CGFloat)textZoom
362 {
363     return WKPageSetTextZoomFactor(self._pageRef, textZoom);
364 }
365
366 - (CGFloat)pageZoom
367 {
368     return WKPageGetPageZoomFactor(self._pageRef);
369 }
370
371 - (void)setPageZoom:(CGFloat)pageZoom
372 {
373     return WKPageSetPageZoomFactor(self._pageRef, pageZoom);
374 }
375
376 @end
377
378 @implementation WKBrowsingContextController (Private)
379
380 - (void)setPaginationMode:(WKBrowsingContextPaginationMode)paginationMode
381 {
382     WKPaginationMode mode;
383     switch (paginationMode) {
384     case WKPaginationModeUnpaginated:
385         mode = kWKPaginationModeUnpaginated;
386         break;
387     case WKPaginationModeLeftToRight:
388         mode = kWKPaginationModeLeftToRight;
389         break;
390     case WKPaginationModeRightToLeft:
391         mode = kWKPaginationModeRightToLeft;
392         break;
393     case WKPaginationModeTopToBottom:
394         mode = kWKPaginationModeTopToBottom;
395         break;
396     case WKPaginationModeBottomToTop:
397         mode = kWKPaginationModeBottomToTop;
398         break;
399     default:
400         return;
401     }
402
403     WKPageSetPaginationMode(self._pageRef, mode);
404 }
405
406 - (WKBrowsingContextPaginationMode)paginationMode
407 {
408     switch (WKPageGetPaginationMode(self._pageRef)) {
409     case kWKPaginationModeUnpaginated:
410         return WKPaginationModeUnpaginated;
411     case kWKPaginationModeLeftToRight:
412         return WKPaginationModeLeftToRight;
413     case kWKPaginationModeRightToLeft:
414         return WKPaginationModeRightToLeft;
415     case kWKPaginationModeTopToBottom:
416         return WKPaginationModeTopToBottom;
417     case kWKPaginationModeBottomToTop:
418         return WKPaginationModeBottomToTop;
419     }
420
421     ASSERT_NOT_REACHED();
422     return WKPaginationModeUnpaginated;
423 }
424
425 - (void)setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
426 {
427     WKPageSetPaginationBehavesLikeColumns(self._pageRef, behavesLikeColumns);
428 }
429
430 - (BOOL)paginationBehavesLikeColumns
431 {
432     return WKPageGetPaginationBehavesLikeColumns(self._pageRef);
433 }
434
435 - (void)setPageLength:(CGFloat)pageLength
436 {
437     WKPageSetPageLength(self._pageRef, pageLength);
438 }
439
440 - (CGFloat)pageLength
441 {
442     return WKPageGetPageLength(self._pageRef);
443 }
444
445 - (void)setGapBetweenPages:(CGFloat)gapBetweenPages
446 {
447     WKPageSetGapBetweenPages(self._pageRef, gapBetweenPages);
448 }
449
450 - (CGFloat)gapBetweenPages
451 {
452     return WKPageGetGapBetweenPages(self._pageRef);
453 }
454
455 - (NSUInteger)pageCount
456 {
457     return WKPageGetPageCount(self._pageRef);
458 }
459
460 #if WK_API_ENABLED
461
462 - (WKBrowsingContextHandle *)handle
463 {
464     return [[[WKBrowsingContextHandle alloc] _initWithPageID:toImpl(self._pageRef)->pageID()] autorelease];
465 }
466
467 #endif
468
469 @end
470
471 @implementation WKBrowsingContextController (Internal)
472
473 static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
474 {
475     if (!WKFrameIsMainFrame(frame))
476         return;
477
478     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
479     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProvisionalLoad:)])
480         [browsingContext.loadDelegate browsingContextControllerDidStartProvisionalLoad:browsingContext];
481 }
482
483 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
484 {
485     if (!WKFrameIsMainFrame(frame))
486         return;
487
488     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
489     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:)])
490         [browsingContext.loadDelegate browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:browsingContext];
491 }
492
493 static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
494 {
495     if (!WKFrameIsMainFrame(frame))
496         return;
497
498     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
499     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFailProvisionalLoad:withError:)]) {
500         RetainPtr<CFErrorRef> cfError = adoptCF(WKErrorCopyCFError(kCFAllocatorDefault, error));
501         [browsingContext.loadDelegate browsingContextControllerDidFailProvisionalLoad:browsingContext withError:(NSError *)cfError.get()];
502     }
503 }
504
505 static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
506 {
507     if (!WKFrameIsMainFrame(frame))
508         return;
509
510     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
511     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidCommitLoad:)])
512         [browsingContext.loadDelegate browsingContextControllerDidCommitLoad:browsingContext];
513 }
514
515 static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
516 {
517     if (!WKFrameIsMainFrame(frame))
518         return;
519
520     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
521     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishLoad:)])
522         [browsingContext.loadDelegate browsingContextControllerDidFinishLoad:browsingContext];
523 }
524
525 static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
526 {
527     if (!WKFrameIsMainFrame(frame))
528         return;
529
530     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
531     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFailLoad:withError:)]) {
532         RetainPtr<CFErrorRef> cfError = adoptCF(WKErrorCopyCFError(kCFAllocatorDefault, error));
533         [browsingContext.loadDelegate browsingContextControllerDidFailLoad:browsingContext withError:(NSError *)cfError.get()];
534     }
535 }
536
537 static void didStartProgress(WKPageRef page, const void* clientInfo)
538 {
539     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
540     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProgress:)])
541         [browsingContext.loadDelegate browsingContextControllerDidStartProgress:browsingContext];
542 }
543
544 static void didChangeProgress(WKPageRef page, const void* clientInfo)
545 {
546     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
547     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextController:estimatedProgressChangedTo:)])
548         [browsingContext.loadDelegate browsingContextController:browsingContext estimatedProgressChangedTo:toImpl(page)->estimatedProgress()];
549 }
550
551 static void didFinishProgress(WKPageRef page, const void* clientInfo)
552 {
553     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
554     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishProgress:)])
555         [browsingContext.loadDelegate browsingContextControllerDidFinishProgress:browsingContext];
556 }
557
558 #if WK_API_ENABLED
559 static void didChangeBackForwardList(WKPageRef page, WKBackForwardListItemRef addedItem, WKArrayRef removedItems, const void *clientInfo)
560 {
561     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
562     if (![browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidChangeBackForwardList:addedItem:removedItems:)])
563         return;
564
565     WKBackForwardListItem *added = addedItem ? wrapper(*toImpl(addedItem)) : nil;
566     NSArray *removed = removedItems ? wrapper(*toImpl(removedItems)) : nil;
567     [browsingContext.loadDelegate browsingContextControllerDidChangeBackForwardList:browsingContext addedItem:added removedItems:removed];
568 }
569 #endif // WK_API_ENABLED
570
571 static void setUpPageLoaderClient(WKBrowsingContextController *browsingContext, WKPageRef pageRef)
572 {
573     WKPageLoaderClient loaderClient;
574     memset(&loaderClient, 0, sizeof(loaderClient));
575
576     loaderClient.version = kWKPageLoaderClientCurrentVersion;
577     loaderClient.clientInfo = browsingContext;
578     loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
579     loaderClient.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
580     loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
581     loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
582     loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
583     loaderClient.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;
584
585     loaderClient.didStartProgress = didStartProgress;
586     loaderClient.didChangeProgress = didChangeProgress;
587     loaderClient.didFinishProgress = didFinishProgress;
588
589 #if WK_API_ENABLED
590     loaderClient.didChangeBackForwardList = didChangeBackForwardList;
591 #endif
592
593     WKPageSetPageLoaderClient(pageRef, &loaderClient);
594 }
595
596 #if WK_API_ENABLED
597 static WKPolicyDecisionHandler makePolicyDecisionBlock(WKFramePolicyListenerRef listener)
598 {
599     WKRetain(listener); // Released in the decision handler below.
600
601     return [[^(WKPolicyDecision decision) {
602         switch (decision) {
603         case WKPolicyDecisionCancel:
604             WKFramePolicyListenerIgnore(listener);                    
605             break;
606         
607         case WKPolicyDecisionAllow:
608             WKFramePolicyListenerUse(listener);
609             break;
610         
611         case WKPolicyDecisionBecomeDownload:
612             WKFramePolicyListenerDownload(listener);
613             break;
614         };
615
616         WKRelease(listener); // Retained in the context above.
617     } copy] autorelease];
618 }
619
620 static void setUpPagePolicyClient(WKBrowsingContextController *browsingContext, WKPageRef pageRef)
621 {
622     WKPagePolicyClient policyClient;
623     memset(&policyClient, 0, sizeof(policyClient));
624
625     policyClient.version = kWKPagePolicyClientCurrentVersion;
626     policyClient.clientInfo = browsingContext;
627
628     policyClient.decidePolicyForNavigationAction = [](WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
629     {
630         WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
631         if ([browsingContext.policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForNavigationAction:decisionHandler:)]) {
632             NSDictionary *actionDictionary = @{
633                 WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
634                 WKActionNavigationTypeKey: @(navigationType),
635                 WKActionModifierFlagsKey: @(modifiers),
636                 WKActionMouseButtonKey: @(mouseButton),
637                 WKActionURLRequestKey: autoreleased(request)
638             };
639             
640             [browsingContext.policyDelegate browsingContextController:browsingContext decidePolicyForNavigationAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
641         } else
642             WKFramePolicyListenerUse(listener);
643     };
644
645     policyClient.decidePolicyForNewWindowAction = [](WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKStringRef frameName, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
646     {
647         WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
648         if ([browsingContext.policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForNewWindowAction:decisionHandler:)]) {
649             NSDictionary *actionDictionary = @{
650                 WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
651                 WKActionNavigationTypeKey: @(navigationType),
652                 WKActionModifierFlagsKey: @(modifiers),
653                 WKActionMouseButtonKey: @(mouseButton),
654                 WKActionURLRequestKey: autoreleased(request),
655                 WKActionFrameNameKey: toImpl(frameName)->wrapper()
656             };
657             
658             [browsingContext.policyDelegate browsingContextController:browsingContext decidePolicyForNewWindowAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
659         } else
660             WKFramePolicyListenerUse(listener);
661     };
662
663     policyClient.decidePolicyForResponse = [](WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
664     {
665         WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
666         if ([browsingContext.policyDelegate respondsToSelector:@selector(browsingContextController:decidePolicyForResponseAction:decisionHandler:)]) {
667             NSDictionary *actionDictionary = @{
668                 WKActionIsMainFrameKey: @(WKFrameIsMainFrame(frame)),
669                 WKActionURLRequestKey: autoreleased(request),
670                 WKActionURLResponseKey: autoreleased(response)
671             };
672
673             [browsingContext.policyDelegate browsingContextController:browsingContext decidePolicyForResponseAction:actionDictionary decisionHandler:makePolicyDecisionBlock(listener)];
674         } else
675             WKFramePolicyListenerUse(listener);
676     };
677
678     WKPageSetPagePolicyClient(pageRef, &policyClient);
679 }
680 #endif
681
682 /* This should only be called from associate view. */
683
684 - (id)_initWithPageRef:(WKPageRef)pageRef
685 {
686     self = [super init];
687     if (!self)
688         return nil;
689
690     _data = [[WKBrowsingContextControllerData alloc] init];
691     _data->_pageRef = pageRef;
692
693     setUpPageLoaderClient(self, pageRef);
694
695 #if WK_API_ENABLED
696     setUpPagePolicyClient(self, pageRef);
697 #endif
698
699     return self;
700 }
701
702 + (WKBrowsingContextController *)_browsingContextControllerForPageRef:(WKPageRef)pageRef
703 {
704     return (WKBrowsingContextController *)WebKit::toImpl(pageRef)->loaderClient().client().clientInfo;
705 }
706
707 + (NSMutableSet *)customSchemes
708 {
709     static NSMutableSet *customSchemes = [[NSMutableSet alloc] init];
710     return customSchemes;
711 }
712  
713 @end