[Cocoa] Add load delegate method for didChangeBackForwardList
[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 - (WKBackForwardList *)backForwardList
260 {
261     WebBackForwardList* list = toImpl(self._pageRef)->backForwardList();
262     if (!list)
263         return nil;
264
265     return [[[WKBackForwardList alloc] _initWithList:*list] autorelease];
266 }
267 #endif // WK_API_ENABLED
268
269 #pragma mark Active Load Introspection
270
271 - (NSURL *)activeURL
272 {
273     return autoreleased(WKPageCopyActiveURL(self._pageRef));
274 }
275
276 - (NSURL *)provisionalURL
277 {
278     return autoreleased(WKPageCopyProvisionalURL(self._pageRef));
279 }
280
281 - (NSURL *)committedURL
282 {
283     return autoreleased(WKPageCopyCommittedURL(self._pageRef));
284 }
285
286 - (double)estimatedProgress
287 {
288     return toImpl(self._pageRef)->estimatedProgress();
289 }
290
291 #pragma mark Active Document Introspection
292
293 - (NSString *)title
294 {
295     return autoreleased(WKPageCopyTitle(self._pageRef));
296 }
297
298 #pragma mark Zoom
299
300 - (CGFloat)textZoom
301 {
302     return WKPageGetTextZoomFactor(self._pageRef);
303 }
304
305 - (void)setTextZoom:(CGFloat)textZoom
306 {
307     return WKPageSetTextZoomFactor(self._pageRef, textZoom);
308 }
309
310 - (CGFloat)pageZoom
311 {
312     return WKPageGetPageZoomFactor(self._pageRef);
313 }
314
315 - (void)setPageZoom:(CGFloat)pageZoom
316 {
317     return WKPageSetPageZoomFactor(self._pageRef, pageZoom);
318 }
319
320 @end
321
322 @implementation WKBrowsingContextController (Private)
323
324 - (void)setPaginationMode:(WKBrowsingContextPaginationMode)paginationMode
325 {
326     WKPaginationMode mode;
327     switch (paginationMode) {
328     case WKPaginationModeUnpaginated:
329         mode = kWKPaginationModeUnpaginated;
330         break;
331     case WKPaginationModeLeftToRight:
332         mode = kWKPaginationModeLeftToRight;
333         break;
334     case WKPaginationModeRightToLeft:
335         mode = kWKPaginationModeRightToLeft;
336         break;
337     case WKPaginationModeTopToBottom:
338         mode = kWKPaginationModeTopToBottom;
339         break;
340     case WKPaginationModeBottomToTop:
341         mode = kWKPaginationModeBottomToTop;
342         break;
343     default:
344         return;
345     }
346
347     WKPageSetPaginationMode(self._pageRef, mode);
348 }
349
350 - (WKBrowsingContextPaginationMode)paginationMode
351 {
352     switch (WKPageGetPaginationMode(self._pageRef)) {
353     case kWKPaginationModeUnpaginated:
354         return WKPaginationModeUnpaginated;
355     case kWKPaginationModeLeftToRight:
356         return WKPaginationModeLeftToRight;
357     case kWKPaginationModeRightToLeft:
358         return WKPaginationModeRightToLeft;
359     case kWKPaginationModeTopToBottom:
360         return WKPaginationModeTopToBottom;
361     case kWKPaginationModeBottomToTop:
362         return WKPaginationModeBottomToTop;
363     }
364
365     ASSERT_NOT_REACHED();
366     return WKPaginationModeUnpaginated;
367 }
368
369 - (void)setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
370 {
371     WKPageSetPaginationBehavesLikeColumns(self._pageRef, behavesLikeColumns);
372 }
373
374 - (BOOL)paginationBehavesLikeColumns
375 {
376     return WKPageGetPaginationBehavesLikeColumns(self._pageRef);
377 }
378
379 - (void)setPageLength:(CGFloat)pageLength
380 {
381     WKPageSetPageLength(self._pageRef, pageLength);
382 }
383
384 - (CGFloat)pageLength
385 {
386     return WKPageGetPageLength(self._pageRef);
387 }
388
389 - (void)setGapBetweenPages:(CGFloat)gapBetweenPages
390 {
391     WKPageSetGapBetweenPages(self._pageRef, gapBetweenPages);
392 }
393
394 - (CGFloat)gapBetweenPages
395 {
396     return WKPageGetGapBetweenPages(self._pageRef);
397 }
398
399 - (NSUInteger)pageCount
400 {
401     return WKPageGetPageCount(self._pageRef);
402 }
403
404 @end
405
406 @implementation WKBrowsingContextController (Internal)
407
408 static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
409 {
410     if (!WKFrameIsMainFrame(frame))
411         return;
412
413     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
414     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProvisionalLoad:)])
415         [browsingContext.loadDelegate browsingContextControllerDidStartProvisionalLoad:browsingContext];
416 }
417
418 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
419 {
420     if (!WKFrameIsMainFrame(frame))
421         return;
422
423     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
424     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:)])
425         [browsingContext.loadDelegate browsingContextControllerDidReceiveServerRedirectForProvisionalLoad:browsingContext];
426 }
427
428 static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
429 {
430     if (!WKFrameIsMainFrame(frame))
431         return;
432
433     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
434     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFailProvisionalLoad:withError:)]) {
435         RetainPtr<CFErrorRef> cfError = adoptCF(WKErrorCopyCFError(kCFAllocatorDefault, error));
436         [browsingContext.loadDelegate browsingContextControllerDidFailProvisionalLoad:browsingContext withError:(NSError *)cfError.get()];
437     }
438 }
439
440 static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
441 {
442     if (!WKFrameIsMainFrame(frame))
443         return;
444
445     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
446     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidCommitLoad:)])
447         [browsingContext.loadDelegate browsingContextControllerDidCommitLoad:browsingContext];
448 }
449
450 static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void* clientInfo)
451 {
452     if (!WKFrameIsMainFrame(frame))
453         return;
454
455     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
456     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishLoad:)])
457         [browsingContext.loadDelegate browsingContextControllerDidFinishLoad:browsingContext];
458 }
459
460 static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void* clientInfo)
461 {
462     if (!WKFrameIsMainFrame(frame))
463         return;
464
465     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
466     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFailLoad:withError:)]) {
467         RetainPtr<CFErrorRef> cfError = adoptCF(WKErrorCopyCFError(kCFAllocatorDefault, error));
468         [browsingContext.loadDelegate browsingContextControllerDidFailLoad:browsingContext withError:(NSError *)cfError.get()];
469     }
470 }
471
472 static void didStartProgress(WKPageRef page, const void* clientInfo)
473 {
474     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
475     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidStartProgress:)])
476         [browsingContext.loadDelegate browsingContextControllerDidStartProgress:browsingContext];
477 }
478
479 static void didChangeProgress(WKPageRef page, const void* clientInfo)
480 {
481     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
482     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextController:estimatedProgressChangedTo:)])
483         [browsingContext.loadDelegate browsingContextController:browsingContext estimatedProgressChangedTo:toImpl(page)->estimatedProgress()];
484 }
485
486 static void didFinishProgress(WKPageRef page, const void* clientInfo)
487 {
488     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
489     if ([browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidFinishProgress:)])
490         [browsingContext.loadDelegate browsingContextControllerDidFinishProgress:browsingContext];
491 }
492
493 static void didChangeBackForwardList(WKPageRef page, WKBackForwardListItemRef addedItem, WKArrayRef removedItems, const void *clientInfo)
494 {
495     WKBrowsingContextController *browsingContext = (WKBrowsingContextController *)clientInfo;
496     if (![browsingContext.loadDelegate respondsToSelector:@selector(browsingContextControllerDidChangedBackForwardList:addedItem:removedItems:)])
497         return;
498
499     WKBackForwardListItem *added = addedItem ? [[WKBackForwardListItem alloc] _initWithItem:*toImpl(addedItem)] : nil;
500     NSArray *removed = removedItems ? [[WKNSArray alloc] web_initWithImmutableArray:*toImpl(removedItems)] : nil;
501     [browsingContext.loadDelegate browsingContextControllerDidChangedBackForwardList:browsingContext addedItem:added removedItems:removed];
502     [added release];
503     [removed release];
504 }
505
506 static void setUpPageLoaderClient(WKBrowsingContextController *browsingContext, WKPageRef pageRef)
507 {
508     WKPageLoaderClient loaderClient;
509     memset(&loaderClient, 0, sizeof(loaderClient));
510
511     loaderClient.version = kWKPageLoaderClientCurrentVersion;
512     loaderClient.clientInfo = browsingContext;
513     loaderClient.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
514     loaderClient.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
515     loaderClient.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
516     loaderClient.didCommitLoadForFrame = didCommitLoadForFrame;
517     loaderClient.didFinishLoadForFrame = didFinishLoadForFrame;
518     loaderClient.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;
519
520     loaderClient.didStartProgress = didStartProgress;
521     loaderClient.didChangeProgress = didChangeProgress;
522     loaderClient.didFinishProgress = didFinishProgress;
523
524     loaderClient.didChangeBackForwardList = didChangeBackForwardList;
525
526     WKPageSetPageLoaderClient(pageRef, &loaderClient);
527 }
528
529
530 /* This should only be called from associate view. */
531
532 - (id)_initWithPageRef:(WKPageRef)pageRef
533 {
534     self = [super init];
535     if (!self)
536         return nil;
537
538     _data = [[WKBrowsingContextControllerData alloc] init];
539     _data->_pageRef = pageRef;
540
541     setUpPageLoaderClient(self, pageRef);
542
543     return self;
544 }
545
546 + (WKBrowsingContextController *)_browsingContextControllerForPageRef:(WKPageRef)pageRef
547 {
548     return (WKBrowsingContextController *)WebKit::toImpl(pageRef)->loaderClient().client().clientInfo;
549 }
550
551 + (NSMutableSet *)customSchemes
552 {
553     static NSMutableSet *customSchemes = [[NSMutableSet alloc] init];
554     return customSchemes;
555 }
556  
557 @end