[Cocoa] Make all API objects have Cocoa wrappers, and delegate refcounting to those...
[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 "WKBrowsingContextController.h"
28 #import "WKBrowsingContextControllerPrivate.h"
29 #import "WKBrowsingContextControllerInternal.h"
30
31 #import "ObjCObjectGraph.h"
32 #import "WKBackForwardListInternal.h"
33 #import "WKBackForwardListItemInternal.h"
34 #import "WKErrorCF.h"
35 #import "WKFrame.h"
36 #import "WKNSArray.h"
37 #import "WKPagePrivate.h"
38 #import "WKRetainPtr.h"
39 #import "WKStringCF.h"
40 #import "WKURLCF.h"
41 #import "WKURLRequest.h"
42 #import "WKURLRequestNS.h"
43 #import "WebContext.h"
44 #import "WebData.h"
45 #import "WebPageProxy.h"
46 #import <wtf/ObjcRuntimeExtras.h>
47 #import <wtf/RetainPtr.h>
48
49 #import "WKBrowsingContextLoadDelegate.h"
50
51 using namespace WebKit;
52
53 static inline NSString *autoreleased(WKStringRef string)
54 {
55     return string ? CFBridgingRelease(WKStringCopyCFString(kCFAllocatorDefault, adoptWK(string).get())) : nil;
56 }
57
58 static inline NSURL *autoreleased(WKURLRef url)
59 {
60     return url ? CFBridgingRelease(WKURLCopyCFURL(kCFAllocatorDefault, adoptWK(url).get())) : nil;
61 }
62
63 @interface WKBrowsingContextControllerData : NSObject {
64 @public
65     // Underlying WKPageRef.
66     WKRetainPtr<WKPageRef> _pageRef;
67     
68     // Delegate for load callbacks.
69     id<WKBrowsingContextLoadDelegate> _loadDelegate;
70 }
71 @end
72
73 @implementation WKBrowsingContextControllerData
74 @end
75
76
77 @implementation WKBrowsingContextController
78
79 - (void)dealloc
80 {
81     WKPageSetPageLoaderClient(_data->_pageRef.get(), 0);
82
83     [_data release];
84     [super dealloc];
85 }
86
87 - (WKPageRef)_pageRef
88 {
89     return _data->_pageRef.get();
90 }
91
92 #pragma mark Delegates
93
94 - (id<WKBrowsingContextLoadDelegate>)loadDelegate
95 {
96     return _data->_loadDelegate;
97 }
98
99 - (void)setLoadDelegate:(id<WKBrowsingContextLoadDelegate>)loadDelegate
100 {
101     _data->_loadDelegate = loadDelegate;
102 }
103
104 #pragma mark Loading
105
106 + (void)registerSchemeForCustomProtocol:(NSString *)scheme
107 {
108     if (!scheme)
109         return;
110
111     NSString *lowercaseScheme = [scheme lowercaseString];
112     [[WKBrowsingContextController customSchemes] addObject:lowercaseScheme];
113     [[NSNotificationCenter defaultCenter] postNotificationName:SchemeForCustomProtocolRegisteredNotificationName object:lowercaseScheme];
114 }
115
116 + (void)unregisterSchemeForCustomProtocol:(NSString *)scheme
117 {
118     if (!scheme)
119         return;
120
121     NSString *lowercaseScheme = [scheme lowercaseString];
122     [[WKBrowsingContextController customSchemes] removeObject:lowercaseScheme];
123     [[NSNotificationCenter defaultCenter] postNotificationName:SchemeForCustomProtocolUnregisteredNotificationName object:lowercaseScheme];
124 }
125
126 - (void)loadRequest:(NSURLRequest *)request
127 {
128     [self loadRequest:request userData:nil];
129 }
130
131 - (void)loadRequest:(NSURLRequest *)request userData:(id)userData
132 {
133     WKRetainPtr<WKURLRequestRef> wkRequest = adoptWK(WKURLRequestCreateWithNSURLRequest(request));
134
135     RefPtr<ObjCObjectGraph> wkUserData;
136     if (userData)
137         wkUserData = ObjCObjectGraph::create(userData);
138
139     WKPageLoadURLRequestWithUserData(self._pageRef, wkRequest.get(), (WKTypeRef)wkUserData.get());
140 }
141
142 - (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory
143 {
144     [self loadFileURL:URL restrictToFilesWithin:allowedDirectory userData:nil];
145 }
146
147 - (void)loadFileURL:(NSURL *)URL restrictToFilesWithin:(NSURL *)allowedDirectory userData:(id)userData
148 {
149     if (![URL isFileURL] || (allowedDirectory && ![allowedDirectory isFileURL]))
150         [NSException raise:NSInvalidArgumentException format:@"Attempted to load a non-file URL"];
151
152     WKRetainPtr<WKURLRef> wkURL = adoptWK(WKURLCreateWithCFURL((CFURLRef)URL));
153     WKRetainPtr<WKURLRef> wkAllowedDirectory = adoptWK(WKURLCreateWithCFURL((CFURLRef)allowedDirectory));
154     
155     RefPtr<ObjCObjectGraph> wkUserData;
156     if (userData)
157         wkUserData = ObjCObjectGraph::create(userData);
158
159     WKPageLoadFileWithUserData(self._pageRef, wkURL.get(), wkAllowedDirectory.get(), (WKTypeRef)wkUserData.get());
160 }
161
162 - (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL
163 {
164     [self loadHTMLString:HTMLString baseURL:baseURL userData:nil];
165 }
166
167 - (void)loadHTMLString:(NSString *)HTMLString baseURL:(NSURL *)baseURL userData:(id)userData
168 {
169     WKRetainPtr<WKStringRef> wkHTMLString;
170     if (HTMLString)
171         wkHTMLString = adoptWK(WKStringCreateWithCFString((CFStringRef)HTMLString));
172
173     WKRetainPtr<WKURLRef> wkBaseURL;
174     if (baseURL)
175         wkBaseURL = adoptWK(WKURLCreateWithCFURL((CFURLRef)baseURL));
176
177     RefPtr<ObjCObjectGraph> wkUserData;
178     if (userData)
179         wkUserData = ObjCObjectGraph::create(userData);
180
181     WKPageLoadHTMLStringWithUserData(self._pageRef, wkHTMLString.get(), wkBaseURL.get(), (WKTypeRef)wkUserData.get());
182 }
183
184 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
185 {
186     [self loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL userData:nil];
187 }
188
189 static void releaseNSData(unsigned char*, const void* data)
190 {
191     [(NSData *)data release];
192 }
193
194 - (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL userData:(id)userData
195 {
196     RefPtr<WebData> wkData;
197     if (data) {
198         [data retain];
199         wkData = WebData::createWithoutCopying((const unsigned char*)[data bytes], [data length], releaseNSData, data);
200     }
201
202     WKRetainPtr<WKStringRef> wkMIMEType;
203     if (MIMEType)
204         wkMIMEType = adoptWK(WKStringCreateWithCFString((CFStringRef)MIMEType));
205
206     WKRetainPtr<WKStringRef> wkEncodingName;
207     if (encodingName)
208         wkEncodingName = adoptWK(WKStringCreateWithCFString((CFStringRef)encodingName));
209
210     WKRetainPtr<WKURLRef> wkBaseURL;
211     if (baseURL)
212         wkBaseURL = adoptWK(WKURLCreateWithCFURL((CFURLRef)baseURL));
213
214     RefPtr<ObjCObjectGraph> wkUserData;
215     if (userData)
216         wkUserData = ObjCObjectGraph::create(userData);
217
218     WKPageLoadDataWithUserData(self._pageRef, toAPI(wkData.get()), wkMIMEType.get(), wkEncodingName.get(), wkBaseURL.get(), (WKTypeRef)wkUserData.get());
219 }
220
221 - (void)stopLoading
222 {
223     WKPageStopLoading(self._pageRef);
224 }
225
226 - (void)reload
227 {
228     WKPageReload(self._pageRef);
229 }
230
231 - (void)reloadFromOrigin
232 {
233     WKPageReloadFromOrigin(self._pageRef);
234 }
235
236 #pragma mark Back/Forward
237
238 - (void)goForward
239 {
240     WKPageGoForward(self._pageRef);
241 }
242
243 - (BOOL)canGoForward
244 {
245     return WKPageCanGoForward(self._pageRef);
246 }
247
248 - (void)goBack
249 {
250     WKPageGoBack(self._pageRef);
251 }
252
253 - (BOOL)canGoBack
254 {
255     return WKPageCanGoBack(self._pageRef);
256 }
257
258 #if WK_API_ENABLED
259 - (void)goToBackForwardListItem:(WKBackForwardListItem *)item
260 {
261     toImpl(self._pageRef)->goToBackForwardItem(&item._item);
262 }
263
264 - (WKBackForwardList *)backForwardList
265 {
266     WebBackForwardList* list = toImpl(self._pageRef)->backForwardList();
267     if (!list)
268         return nil;
269
270     return wrapper(*list);
271 }
272 #endif // WK_API_ENABLED
273
274 #pragma mark Active Load Introspection
275
276 - (NSURL *)activeURL
277 {
278     return autoreleased(WKPageCopyActiveURL(self._pageRef));
279 }
280
281 - (NSURL *)provisionalURL
282 {
283     return autoreleased(WKPageCopyProvisionalURL(self._pageRef));
284 }
285
286 - (NSURL *)committedURL
287 {
288     return autoreleased(WKPageCopyCommittedURL(self._pageRef));
289 }
290
291 - (double)estimatedProgress
292 {
293     return toImpl(self._pageRef)->estimatedProgress();
294 }
295
296 #pragma mark Active Document Introspection
297
298 - (NSString *)title
299 {
300     return autoreleased(WKPageCopyTitle(self._pageRef));
301 }
302
303 #pragma mark Zoom
304
305 - (CGFloat)textZoom
306 {
307     return WKPageGetTextZoomFactor(self._pageRef);
308 }
309
310 - (void)setTextZoom:(CGFloat)textZoom
311 {
312     return WKPageSetTextZoomFactor(self._pageRef, textZoom);
313 }
314
315 - (CGFloat)pageZoom
316 {
317     return WKPageGetPageZoomFactor(self._pageRef);
318 }
319
320 - (void)setPageZoom:(CGFloat)pageZoom
321 {
322     return WKPageSetPageZoomFactor(self._pageRef, pageZoom);
323 }
324
325 @end
326
327 @implementation WKBrowsingContextController (Private)
328
329 - (void)setPaginationMode:(WKBrowsingContextPaginationMode)paginationMode
330 {
331     WKPaginationMode mode;
332     switch (paginationMode) {
333     case WKPaginationModeUnpaginated:
334         mode = kWKPaginationModeUnpaginated;
335         break;
336     case WKPaginationModeLeftToRight:
337         mode = kWKPaginationModeLeftToRight;
338         break;
339     case WKPaginationModeRightToLeft:
340         mode = kWKPaginationModeRightToLeft;
341         break;
342     case WKPaginationModeTopToBottom:
343         mode = kWKPaginationModeTopToBottom;
344         break;
345     case WKPaginationModeBottomToTop:
346         mode = kWKPaginationModeBottomToTop;
347         break;
348     default:
349         return;
350     }
351
352     WKPageSetPaginationMode(self._pageRef, mode);
353 }
354
355 - (WKBrowsingContextPaginationMode)paginationMode
356 {
357     switch (WKPageGetPaginationMode(self._pageRef)) {
358     case kWKPaginationModeUnpaginated:
359         return WKPaginationModeUnpaginated;
360     case kWKPaginationModeLeftToRight:
361         return WKPaginationModeLeftToRight;
362     case kWKPaginationModeRightToLeft:
363         return WKPaginationModeRightToLeft;
364     case kWKPaginationModeTopToBottom:
365         return WKPaginationModeTopToBottom;
366     case kWKPaginationModeBottomToTop:
367         return WKPaginationModeBottomToTop;
368     }
369
370     ASSERT_NOT_REACHED();
371     return WKPaginationModeUnpaginated;
372 }
373
374 - (void)setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
375 {
376     WKPageSetPaginationBehavesLikeColumns(self._pageRef, behavesLikeColumns);
377 }
378
379 - (BOOL)paginationBehavesLikeColumns
380 {
381     return WKPageGetPaginationBehavesLikeColumns(self._pageRef);
382 }
383
384 - (void)setPageLength:(CGFloat)pageLength
385 {
386     WKPageSetPageLength(self._pageRef, pageLength);
387 }
388
389 - (CGFloat)pageLength
390 {
391     return WKPageGetPageLength(self._pageRef);
392 }
393
394 - (void)setGapBetweenPages:(CGFloat)gapBetweenPages
395 {
396     WKPageSetGapBetweenPages(self._pageRef, gapBetweenPages);
397 }
398
399 - (CGFloat)gapBetweenPages
400 {
401     return WKPageGetGapBetweenPages(self._pageRef);
402 }
403
404 - (NSUInteger)pageCount
405 {
406     return WKPageGetPageCount(self._pageRef);
407 }
408
409 @end
410
411 @implementation WKBrowsingContextController (Internal)
412
413 static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
414 {
415     if (!WKFrameIsMainFrame(frame))
416         return;
417
418     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
419     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProvisionalLoad:)])
420         [browsingContext.loadDelegate browsingContextControllerDidStartProvisionalLoad:browsingContext];
421 }
422
423 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
424 {
425     if (!WKFrameIsMainFrame(frame))
426         return;
427
428     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
429     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:)])
430         [browsingContext.loadDelegate browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:browsingContext];
431 }
432
433 static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
434 {
435     if (!WKFrameIsMainFrame(frame))
436         return;
437
438     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
439     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFailProvisionalLoad:withError:)]) {
440         RetainPtr<CFErrorRef> cfError = adoptCF(WKErrorCopyCFError(kCFAllocatorDefault, error));
441         [browsingContext.loadDelegate browsingContextControllerDidFailProvisionalLoad:browsingContext withError:(NSError *)cfError.get()];
442     }
443 }
444
445 static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
446 {
447     if (!WKFrameIsMainFrame(frame))
448         return;
449
450     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
451     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidCommitLoad:)])
452         [browsingContext.loadDelegate browsingContextControllerDidCommitLoad:browsingContext];
453 }
454
455 static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
456 {
457     if (!WKFrameIsMainFrame(frame))
458         return;
459
460     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
461     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishLoad:)])
462         [browsingContext.loadDelegate browsingContextControllerDidFinishLoad:browsingContext];
463 }
464
465 static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
466 {
467     if (!WKFrameIsMainFrame(frame))
468         return;
469
470     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
471     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFailLoad:withError:)]) {
472         RetainPtr<CFErrorRef> cfError = adoptCF(WKErrorCopyCFError(kCFAllocatorDefault, error));
473         [browsingContext.loadDelegate browsingContextControllerDidFailLoad:browsingContext withError:(NSError *)cfError.get()];
474     }
475 }
476
477 static void didStartProgress(WKPageRef page, const void* clientInfo)
478 {
479     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
480     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProgress:)])
481         [browsingContext.loadDelegate browsingContextControllerDidStartProgress:browsingContext];
482 }
483
484 static void didChangeProgress(WKPageRef page, const void* clientInfo)
485 {
486     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
487     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextController:estimatedProgressChangedTo:)])
488         [browsingContext.loadDelegate browsingContextController:browsingContext estimatedProgressChangedTo:toImpl(page)->estimatedProgress()];
489 }
490
491 static void didFinishProgress(WKPageRef page, const void* clientInfo)
492 {
493     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
494     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishProgress:)])
495         [browsingContext.loadDelegate browsingContextControllerDidFinishProgress:browsingContext];
496 }
497
498 #if WK_API_ENABLED
499 static void didChangeBackForwardList(WKPageRef page, WKBackForwardListItemRef addedItem, WKArrayRef removedItems, const void *clientInfo)
500 {
501     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
502     if (![browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidChangeBackForwardList:addedItem:removedItems:)])
503         return;
504
505     WKBackForwardListItem *added = addedItem ? wrapper(*toImpl(addedItem)) : nil;
506     NSArray *removed = removedItems ? wrapper(*toImpl(removedItems)) : nil;
507     [browsingContext.loadDelegate browsingContextControllerDidChangeBackForwardList:browsingContext addedItem:added removedItems:removed];
508 }
509 #endif // WK_API_ENABLED
510
511 static void setUpPageLoaderClient(WKBrowsingContextController *browsingContext, WKPageRef pageRef)
512 {
513     WKPageLoaderClient loaderClient;
514     memset(&loaderClient, 0, sizeof(loaderClient));
515
516     loaderClient.version = kWKPageLoaderClientCurrentVersion;
517     loaderClient.clientInfo = browsingContext;
518     loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
519     loaderClient.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
520     loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
521     loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
522     loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
523     loaderClient.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;
524
525     loaderClient.didStartProgress = didStartProgress;
526     loaderClient.didChangeProgress = didChangeProgress;
527     loaderClient.didFinishProgress = didFinishProgress;
528
529 #if WK_API_ENABLED
530     loaderClient.didChangeBackForwardList = didChangeBackForwardList;
531 #endif
532
533     WKPageSetPageLoaderClient(pageRef, &loaderClient);
534 }
535
536
537 /* This should only be called from associate view. */
538
539 - (id)_initWithPageRef:(WKPageRef)pageRef
540 {
541     self = [super init];
542     if (!self)
543         return nil;
544
545     _data = [[WKBrowsingContextControllerData alloc] init];
546     _data->_pageRef = pageRef;
547
548     setUpPageLoaderClient(self, pageRef);
549
550     return self;
551 }
552
553 + (WKBrowsingContextController *)_browsingContextControllerForPageRef:(WKPageRef)pageRef
554 {
555     return (WKBrowsingContextController *)WebKit::toImpl(pageRef)->loaderClient().client().clientInfo;
556 }
557
558 + (NSMutableSet *)customSchemes
559 {
560     static NSMutableSet *customSchemes = [[NSMutableSet alloc] init];
561     return customSchemes;
562 }
563  
564 @end