1/*
2 * Copyright (C) 2010 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 "AppDelegate.h"
27
28#import "BrowserWindowController.h"
29#import "BrowserStatisticsWindowController.h"
30
31#import <WebKit2/WKContextPrivate.h>
32#import <WebKit2/WKStringCF.h>
33#import <WebKit2/WKURLCF.h>
34
35static NSString *defaultURL = @"http://www.webkit.org/";
36
37@implementation BrowserAppDelegate
38
39void didRecieveMessageFromInjectedBundle(WKContextRef context, WKStringRef messageName, WKTypeRef messageBody, const void *clientInfo)
40{
41    CFStringRef cfMessageName = WKStringCopyCFString(0, messageName);
42
43    WKTypeID typeID = WKGetTypeID(messageBody);
44    if (typeID == WKStringGetTypeID()) {
45        CFStringRef cfMessageBody = WKStringCopyCFString(0, (WKStringRef)messageBody);
46        LOG(@"ContextInjectedBundleClient - didRecieveMessage - MessageName: %@ MessageBody %@", cfMessageName, cfMessageBody);
47        CFRelease(cfMessageBody);
48    } else {
49        LOG(@"ContextInjectedBundleClient - didRecieveMessage - MessageName: %@ (MessageBody Unhandeled)\n", cfMessageName);
50    }
51
52    CFRelease(cfMessageName);
53
54    WKStringRef newMessageName = WKStringCreateWithCFString(CFSTR("Response"));
55    WKStringRef newMessageBody = WKStringCreateWithCFString(CFSTR("Roger that!"));
56
57    WKContextPostMessageToInjectedBundle(context, newMessageName, newMessageBody);
58
59    WKRelease(newMessageName);
60    WKRelease(newMessageBody);
61}
62
63// MARK: History Client Callbacks
64
65static void didNavigateWithNavigationData(WKContextRef context, WKPageRef page, WKNavigationDataRef navigationData, WKFrameRef frame, const void *clientInfo)
66{
67    WKStringRef wkTitle = WKNavigationDataCopyTitle(navigationData);
68    CFStringRef title = WKStringCopyCFString(0, wkTitle);
69    WKRelease(wkTitle);
70
71    WKURLRef wkURL = WKNavigationDataCopyURL(navigationData);
72    CFURLRef url = WKURLCopyCFURL(0, wkURL);
73    WKRelease(wkURL);
74
75    LOG(@"HistoryClient - didNavigateWithNavigationData - title: %@ - url: %@", title, url);
76    CFRelease(title);
77    CFRelease(url);
78}
79
80static void didPerformClientRedirect(WKContextRef context, WKPageRef page, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void *clientInfo)
81{
82    CFURLRef cfSourceURL = WKURLCopyCFURL(0, sourceURL);
83    CFURLRef cfDestinationURL = WKURLCopyCFURL(0, destinationURL);
84    LOG(@"HistoryClient - didPerformClientRedirect - sourceURL: %@ - destinationURL: %@", cfSourceURL, cfDestinationURL);
85    CFRelease(cfSourceURL);
86    CFRelease(cfDestinationURL);
87}
88
89static void didPerformServerRedirect(WKContextRef context, WKPageRef page, WKURLRef sourceURL, WKURLRef destinationURL, WKFrameRef frame, const void *clientInfo)
90{
91    CFURLRef cfSourceURL = WKURLCopyCFURL(0, sourceURL);
92    CFURLRef cfDestinationURL = WKURLCopyCFURL(0, destinationURL);
93    LOG(@"HistoryClient - didPerformServerRedirect - sourceURL: %@ - destinationURL: %@", cfSourceURL, cfDestinationURL);
94    CFRelease(cfSourceURL);
95    CFRelease(cfDestinationURL);
96}
97
98static void didUpdateHistoryTitle(WKContextRef context, WKPageRef page, WKStringRef title, WKURLRef URL, WKFrameRef frame, const void *clientInfo)
99{
100    CFStringRef cfTitle = WKStringCopyCFString(0, title);
101    CFURLRef cfURL = WKURLCopyCFURL(0, URL);
102    LOG(@"HistoryClient - didUpdateHistoryTitle - title: %@ - URL: %@", cfTitle, cfURL);
103    CFRelease(cfTitle);
104    CFRelease(cfURL);
105}
106
107static void populateVisitedLinks(WKContextRef context, const void *clientInfo)
108{
109    LOG(@"HistoryClient - populateVisitedLinks");
110}
111
112- (id)init
113{
114    self = [super init];
115    if (self) {
116        if ([NSEvent modifierFlags] & NSShiftKeyMask)
117            _currentProcessModel = kProcessModelSharedSecondaryThread;
118        else
119            _currentProcessModel = kProcessModelSharedSecondaryProcess;
120
121        WKContextHistoryClient historyClient = {
122            0,
123            self,
124            didNavigateWithNavigationData,
125            didPerformClientRedirect,
126            didPerformServerRedirect,
127            didUpdateHistoryTitle,
128            populateVisitedLinks
129        };
130
131        _threadContext = WKContextGetSharedThreadContext();
132        WKContextSetHistoryClient(_threadContext, &historyClient);
133        WKContextSetCacheModel(_threadContext, kWKCacheModelPrimaryWebBrowser);
134
135        CFStringRef bundlePathCF = (CFStringRef)[[NSBundle mainBundle] pathForAuxiliaryExecutable:@"WebBundle.bundle"];
136        WKStringRef bundlePath = WKStringCreateWithCFString(bundlePathCF);
137
138        _processContext = WKContextCreateWithInjectedBundlePath(bundlePath);
139
140        WKContextInjectedBundleClient bundleClient = {
141            0,      /* version */
142            0,      /* clientInfo */
143            didRecieveMessageFromInjectedBundle,
144            0
145        };
146        WKContextSetInjectedBundleClient(_processContext, &bundleClient);
147        WKContextSetHistoryClient(_processContext, &historyClient);
148        WKContextSetCacheModel(_processContext, kWKCacheModelPrimaryWebBrowser);
149
150        WKRelease(bundlePath);
151    }
152
153    return self;
154}
155
156- (IBAction)newWindow:(id)sender
157{
158    BrowserWindowController *controller = [[BrowserWindowController alloc] initWithContext:[self getCurrentContext]];
159    [[controller window] makeKeyAndOrderFront:sender];
160
161    [controller loadURLString:defaultURL];
162}
163
164- (WKContextRef)getCurrentContext
165{
166    return (_currentProcessModel == kProcessModelSharedSecondaryThread) ? _threadContext : _processContext;
167}
168
169- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
170{
171    if ([menuItem action] == @selector(setSharedProcessProcessModel:))
172        [menuItem setState:_currentProcessModel == kProcessModelSharedSecondaryProcess ? NSOnState : NSOffState];
173    else if ([menuItem action] == @selector(setSharedThreadProcessModel:))
174        [menuItem setState:_currentProcessModel == kProcessModelSharedSecondaryThread ? NSOnState : NSOffState];
175    return YES;
176}
177
178- (void)_setProcessModel:(ProcessModel)processModel
179{
180    if (processModel == _currentProcessModel)
181        return;
182
183    _currentProcessModel = processModel;
184}
185
186- (IBAction)setSharedProcessProcessModel:(id)sender
187{
188    [self _setProcessModel:kProcessModelSharedSecondaryProcess];
189}
190
191- (IBAction)setSharedThreadProcessModel:(id)sender
192{
193    [self _setProcessModel:kProcessModelSharedSecondaryThread];
194}
195
196- (IBAction)showStatisticsWindow:(id)sender
197{
198    static BrowserStatisticsWindowController* windowController;
199    if (!windowController)
200        windowController = [[BrowserStatisticsWindowController alloc] initWithThreadedWKContextRef:_threadContext
201                                                                               processWKContextRef:_processContext];
202
203    [[windowController window] makeKeyAndOrderFront:self];
204}
205
206- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
207{
208    [self newWindow:self];
209}
210
211- (void)applicationWillTerminate:(NSNotification *)notification
212{
213    NSArray* windows = [NSApp windows];
214    for (NSWindow* window in windows) {
215        id delegate = [window delegate];
216        if ([delegate isKindOfClass:[BrowserWindowController class]]) {
217            BrowserWindowController *controller = (BrowserWindowController *)delegate;
218            [controller applicationTerminating];
219        }
220    }
221
222    WKRelease(_processContext);
223    _processContext = 0;
224}
225
226- (BrowserWindowController *)frontmostBrowserWindowController
227{
228    NSArray* windows = [NSApp windows];
229    for (NSWindow* window in windows) {
230        id delegate = [window delegate];
231        if ([delegate isKindOfClass:[BrowserWindowController class]])
232            return (BrowserWindowController *)delegate;
233    }
234
235    return 0;
236}
237
238- (IBAction)openDocument:(id)sender
239{
240    NSOpenPanel *openPanel = [[NSOpenPanel openPanel] retain];
241    [openPanel beginForDirectory:nil
242        file:nil
243        types:nil
244        modelessDelegate:self
245        didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
246        contextInfo:0];
247}
248
249- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
250{
251    [sheet autorelease];
252    if (returnCode != NSOKButton || ![[sheet filenames] count])
253        return;
254
255    NSString* filePath = [[sheet filenames] objectAtIndex:0];
256
257    BrowserWindowController *controller = [self frontmostBrowserWindowController];
258    if (!controller) {
259        controller = [[BrowserWindowController alloc] initWithContext:[self getCurrentContext]];
260        [[controller window] makeKeyAndOrderFront:self];
261    }
262
263    [controller loadURLString:[[NSURL fileURLWithPath:filePath] absoluteString]];
264}
265
266@end
267