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 "BrowserWindowController.h"
27
28#import <WebKit2/WKPagePrivate.h>
29#import <WebKit2/WKStringCF.h>
30#import <WebKit2/WKURLCF.h>
31
32@interface BrowserWindowController ()
33- (void)didStartProgress;
34- (void)didChangeProgress:(double)value;
35- (void)didFinishProgress;
36- (void)didStartProvisionalLoadForFrame:(WKFrameRef)frame;
37- (void)didCommitLoadForFrame:(WKFrameRef)frame;
38- (void)didReceiveServerRedirectForProvisionalLoadForFrame:(WKFrameRef)frame;
39- (void)didFailProvisionalLoadWithErrorForFrame:(WKFrameRef)frame;
40- (void)didFailLoadWithErrorForFrame:(WKFrameRef)frame;
41- (void)didSameDocumentNavigationForFrame:(WKFrameRef)frame;
42@end
43
44@implementation BrowserWindowController
45
46- (id)initWithContext:(WKContextRef)context
47{
48    if ((self = [super initWithWindowNibName:@"BrowserWindow"])) {
49        _context = WKRetain(context);
50        _zoomTextOnly = NO;
51    }
52
53    return self;
54}
55
56- (void)dealloc
57{
58    assert(!_context);
59    [super dealloc];
60}
61
62- (IBAction)fetch:(id)sender
63{
64    CFURLRef cfURL = CFURLCreateWithString(0, (CFStringRef)[urlText stringValue], 0);
65    if (!cfURL)
66        return;
67
68    WKURLRef url = WKURLCreateWithCFURL(cfURL);
69    CFRelease(cfURL);
70
71    WKPageLoadURL(_webView.pageRef, url);
72    WKRelease(url);
73}
74
75- (IBAction)showHideWebView:(id)sender
76{
77    BOOL hidden = ![_webView isHidden];
78
79    [_webView setHidden:hidden];
80}
81
82- (IBAction)removeReinsertWebView:(id)sender
83{
84    if ([_webView window]) {
85        [_webView retain];
86        [_webView removeFromSuperview];
87    } else {
88        [containerView addSubview:_webView];
89        [_webView release];
90    }
91}
92
93- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
94{
95    SEL action = [menuItem action];
96
97    if (action == @selector(zoomIn:))
98        return [self canZoomIn];
99    if (action == @selector(zoomOut:))
100        return [self canZoomOut];
101    if (action == @selector(resetZoom:))
102        return [self canResetZoom];
103
104    if (action == @selector(showHideWebView:))
105        [menuItem setTitle:[_webView isHidden] ? @"Show Web View" : @"Hide Web View"];
106    else if (action == @selector(removeReinsertWebView:))
107        [menuItem setTitle:[_webView window] ? @"Remove Web View" : @"Insert Web View"];
108    else if (action == @selector(toggleZoomMode:))
109        [menuItem setState:_zoomTextOnly ? NSOnState : NSOffState];
110    return YES;
111}
112
113- (IBAction)reload:(id)sender
114{
115    WKPageReload(_webView.pageRef);
116}
117
118- (IBAction)forceRepaint:(id)sender
119{
120    [_webView setNeedsDisplay:YES];
121}
122
123- (IBAction)goBack:(id)sender
124{
125    WKPageGoBack(_webView.pageRef);
126}
127
128- (IBAction)goForward:(id)sender
129{
130    WKPageGoForward(_webView.pageRef);
131}
132
133- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
134{
135    SEL action = [item action];
136
137    if (action == @selector(goBack:))
138        return _webView && WKPageCanGoBack(_webView.pageRef);
139
140    if (action == @selector(goForward:))
141        return _webView && WKPageCanGoForward(_webView.pageRef);
142
143    return YES;
144}
145
146- (void)validateToolbar
147{
148    [toolbar validateVisibleItems];
149}
150
151- (BOOL)windowShouldClose:(id)sender
152{
153    LOG(@"windowShouldClose");
154    BOOL canCloseImmediately = WKPageTryClose(_webView.pageRef);
155    return canCloseImmediately;
156}
157
158- (void)windowWillClose:(NSNotification *)notification
159{
160    WKRelease(_context);
161    _context = 0;
162}
163
164- (void)applicationTerminating
165{
166    WKPageClose(_webView.pageRef);
167    WKRelease(_webView.pageRef);
168}
169
170#define DefaultMinimumZoomFactor (.5)
171#define DefaultMaximumZoomFactor (3.0)
172#define DefaultZoomFactorRatio (1.2)
173
174- (double)currentZoomFactor
175{
176    return _zoomTextOnly ? WKPageGetTextZoomFactor(_webView.pageRef) : WKPageGetPageZoomFactor(_webView.pageRef);
177}
178
179- (void)setCurrentZoomFactor:(double)factor
180{
181    _zoomTextOnly ? WKPageSetTextZoomFactor(_webView.pageRef, factor) : WKPageSetPageZoomFactor(_webView.pageRef, factor);
182}
183
184- (BOOL)canZoomIn
185{
186    return [self currentZoomFactor] * DefaultZoomFactorRatio < DefaultMaximumZoomFactor;
187}
188
189- (void)zoomIn:(id)sender
190{
191    if (![self canZoomIn])
192        return;
193
194    double factor = [self currentZoomFactor] * DefaultZoomFactorRatio;
195    [self setCurrentZoomFactor:factor];
196}
197
198- (BOOL)canZoomOut
199{
200    return [self currentZoomFactor] / DefaultZoomFactorRatio > DefaultMinimumZoomFactor;
201}
202
203- (void)zoomOut:(id)sender
204{
205    if (![self canZoomIn])
206        return;
207
208    double factor = [self currentZoomFactor] / DefaultZoomFactorRatio;
209    [self setCurrentZoomFactor:factor];
210}
211
212- (BOOL)canResetZoom
213{
214    return _zoomTextOnly ? (WKPageGetTextZoomFactor(_webView.pageRef) != 1) : (WKPageGetPageZoomFactor(_webView.pageRef) != 1);
215}
216
217- (void)resetZoom:(id)sender
218{
219    if (![self canResetZoom])
220        return;
221
222    if (_zoomTextOnly)
223        WKPageSetTextZoomFactor(_webView.pageRef, 1);
224    else
225        WKPageSetPageZoomFactor(_webView.pageRef, 1);
226}
227
228- (IBAction)toggleZoomMode:(id)sender
229{
230    if (_zoomTextOnly) {
231        _zoomTextOnly = NO;
232        double currentTextZoom = WKPageGetTextZoomFactor(_webView.pageRef);
233        WKPageSetPageAndTextZoomFactors(_webView.pageRef, currentTextZoom, 1);
234    } else {
235        _zoomTextOnly = YES;
236        double currentPageZoom = WKPageGetPageZoomFactor(_webView.pageRef);
237        WKPageSetPageAndTextZoomFactors(_webView.pageRef, 1, currentPageZoom);
238    }
239}
240
241- (IBAction)dumpSourceToConsole:(id)sender
242{
243    WKPageGetSourceForFrame_b(_webView.pageRef, WKPageGetMainFrame(_webView.pageRef), ^(WKStringRef result, WKErrorRef error) {
244        CFStringRef cfResult = WKStringCopyCFString(0, result);
245        LOG(@"Main frame source\n \"%@\"", (NSString *)cfResult);
246        CFRelease(cfResult);
247    });
248}
249
250// MARK: Loader Client Callbacks
251
252static void didStartProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
253{
254    [(BrowserWindowController *)clientInfo didStartProvisionalLoadForFrame:frame];
255}
256
257static void didReceiveServerRedirectForProvisionalLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
258{
259    [(BrowserWindowController *)clientInfo didReceiveServerRedirectForProvisionalLoadForFrame:frame];
260}
261
262static void didFailProvisionalLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void *clientInfo)
263{
264    [(BrowserWindowController *)clientInfo didFailProvisionalLoadWithErrorForFrame:frame];
265}
266
267static void didCommitLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
268{
269    [(BrowserWindowController *)clientInfo didCommitLoadForFrame:frame];
270}
271
272static void didFinishDocumentLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
273{
274    LOG(@"didFinishDocumentLoadForFrame");
275}
276
277static void didFinishLoadForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
278{
279    LOG(@"didFinishLoadForFrame");
280}
281
282static void didFailLoadWithErrorForFrame(WKPageRef page, WKFrameRef frame, WKErrorRef error, WKTypeRef userData, const void *clientInfo)
283{
284    [(BrowserWindowController *)clientInfo didFailLoadWithErrorForFrame:frame];
285}
286
287static void didSameDocumentNavigationForFrame(WKPageRef page, WKFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef userData, const void *clientInfo)
288{
289    [(BrowserWindowController *)clientInfo didSameDocumentNavigationForFrame:frame];
290}
291
292static void didReceiveTitleForFrame(WKPageRef page, WKStringRef title, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
293{
294    CFStringRef cfTitle = WKStringCopyCFString(0, title);
295    LOG(@"didReceiveTitleForFrame \"%@\"", (NSString *)cfTitle);
296    CFRelease(cfTitle);
297}
298
299static void didFirstLayoutForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
300{
301    LOG(@"didFirstLayoutForFrame");
302}
303
304static void didFirstVisuallyNonEmptyLayoutForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
305{
306    LOG(@"didFirstVisuallyNonEmptyLayoutForFrame");
307}
308
309static void didRemoveFrameFromHierarchy(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
310{
311    LOG(@"didRemoveFrameFromHierarchy");
312}
313
314static void didDisplayInsecureContentForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
315{
316    LOG(@"didDisplayInsecureContentForFrame");
317}
318
319static void didRunInsecureContentForFrame(WKPageRef page, WKFrameRef frame, WKTypeRef userData, const void *clientInfo)
320{
321    LOG(@"didRunInsecureContentForFrame");
322}
323
324static void didStartProgress(WKPageRef page, const void *clientInfo)
325{
326    [(BrowserWindowController *)clientInfo didStartProgress];
327}
328
329static void didChangeProgress(WKPageRef page, const void *clientInfo)
330{
331    [(BrowserWindowController *)clientInfo didChangeProgress:WKPageGetEstimatedProgress(page)];
332}
333
334static void didFinishProgress(WKPageRef page, const void *clientInfo)
335{
336    [(BrowserWindowController *)clientInfo didFinishProgress];
337}
338
339static void didBecomeUnresponsive(WKPageRef page, const void *clientInfo)
340{
341    LOG(@"didBecomeUnresponsive");
342}
343
344static void didBecomeResponsive(WKPageRef page, const void *clientInfo)
345{
346    LOG(@"didBecomeResponsive");
347}
348
349static void processDidExit(WKPageRef page, const void *clientInfo)
350{
351    LOG(@"processDidExit");
352}
353
354static void didChangeBackForwardList(WKPageRef page, WKBackForwardListItemRef addedItem, WKArrayRef removedItems, const void *clientInfo)
355{
356    [(BrowserWindowController *)clientInfo validateToolbar];
357}
358
359// MARK: Policy Client Callbacks
360
361static void decidePolicyForNavigationAction(WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
362{
363    LOG(@"decidePolicyForNavigationAction");
364    WKFramePolicyListenerUse(listener);
365}
366
367static void decidePolicyForNewWindowAction(WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKStringRef frameName, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
368{
369    LOG(@"decidePolicyForNewWindowAction");
370    WKFramePolicyListenerUse(listener);
371}
372
373static void decidePolicyForResponse(WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo)
374{
375    WKFramePolicyListenerUse(listener);
376}
377
378// MARK: UI Client Callbacks
379
380static WKPageRef createNewPage(WKPageRef page, WKDictionaryRef features, WKEventModifiers modifiers, WKEventMouseButton button, const void* clientInfo)
381{
382    LOG(@"createNewPage");
383    BrowserWindowController *controller = [[BrowserWindowController alloc] initWithContext:WKPageGetContext(page)];
384    [controller loadWindow];
385
386    return controller->_webView.pageRef;
387}
388
389static void showPage(WKPageRef page, const void *clientInfo)
390{
391    LOG(@"showPage");
392    [[(BrowserWindowController *)clientInfo window] orderFront:nil];
393}
394
395static void closePage(WKPageRef page, const void *clientInfo)
396{
397    LOG(@"closePage");
398    WKPageClose(page);
399    [[(BrowserWindowController *)clientInfo window] close];
400    WKRelease(page);
401}
402
403static void runJavaScriptAlert(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
404{
405    NSAlert* alert = [[NSAlert alloc] init];
406
407    WKURLRef wkURL = WKFrameCopyURL(frame);
408    CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
409    WKRelease(wkURL);
410
411    [alert setMessageText:[NSString stringWithFormat:@"JavaScript alert dialog from %@.", [(NSURL *)cfURL absoluteString]]];
412    CFRelease(cfURL);
413
414    CFStringRef cfMessage = WKStringCopyCFString(0, message);
415    [alert setInformativeText:(NSString *)cfMessage];
416    CFRelease(cfMessage);
417
418    [alert addButtonWithTitle:@"OK"];
419
420    [alert runModal];
421    [alert release];
422}
423
424static bool runJavaScriptConfirm(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
425{
426    NSAlert* alert = [[NSAlert alloc] init];
427
428    WKURLRef wkURL = WKFrameCopyURL(frame);
429    CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
430    WKRelease(wkURL);
431
432    [alert setMessageText:[NSString stringWithFormat:@"JavaScript confirm dialog from %@.", [(NSURL *)cfURL absoluteString]]];
433    CFRelease(cfURL);
434
435    CFStringRef cfMessage = WKStringCopyCFString(0, message);
436    [alert setInformativeText:(NSString *)cfMessage];
437    CFRelease(cfMessage);
438
439    [alert addButtonWithTitle:@"OK"];
440    [alert addButtonWithTitle:@"Cancel"];
441
442    NSInteger button = [alert runModal];
443    [alert release];
444
445    return button == NSAlertFirstButtonReturn;
446}
447
448static WKStringRef runJavaScriptPrompt(WKPageRef page, WKStringRef message, WKStringRef defaultValue, WKFrameRef frame, const void* clientInfo)
449{
450    NSAlert* alert = [[NSAlert alloc] init];
451
452    WKURLRef wkURL = WKFrameCopyURL(frame);
453    CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
454    WKRelease(wkURL);
455
456    [alert setMessageText:[NSString stringWithFormat:@"JavaScript prompt dialog from %@.", [(NSURL *)cfURL absoluteString]]];
457    CFRelease(cfURL);
458
459    CFStringRef cfMessage = WKStringCopyCFString(0, message);
460    [alert setInformativeText:(NSString *)cfMessage];
461    CFRelease(cfMessage);
462
463    [alert addButtonWithTitle:@"OK"];
464    [alert addButtonWithTitle:@"Cancel"];
465
466    NSTextField* input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
467    CFStringRef cfDefaultValue = WKStringCopyCFString(0, defaultValue);
468    [input setStringValue:(NSString *)cfDefaultValue];
469    CFRelease(cfDefaultValue);
470
471    [alert setAccessoryView:input];
472
473    NSInteger button = [alert runModal];
474
475    NSString* result = nil;
476    if (button == NSAlertFirstButtonReturn) {
477        [input validateEditing];
478        result = [input stringValue];
479    }
480
481    [alert release];
482
483    if (!result)
484        return 0;
485    return WKStringCreateWithCFString((CFStringRef)result);
486}
487
488static void setStatusText(WKPageRef page, WKStringRef text, const void* clientInfo)
489{
490    LOG(@"setStatusText");
491}
492
493static void mouseDidMoveOverElement(WKPageRef page, WKEventModifiers modifiers, WKTypeRef userData, const void *clientInfo)
494{
495    LOG(@"mouseDidMoveOverElement");
496}
497
498static WKRect getWindowFrame(WKPageRef page, const void* clientInfo)
499{
500    NSRect rect = [[(BrowserWindowController *)clientInfo window] frame];
501    WKRect wkRect;
502    wkRect.origin.x = rect.origin.x;
503    wkRect.origin.y = rect.origin.y;
504    wkRect.size.width = rect.size.width;
505    wkRect.size.height = rect.size.height;
506    return wkRect;
507}
508
509static void setWindowFrame(WKPageRef page, WKRect rect, const void* clientInfo)
510{
511    [[(BrowserWindowController *)clientInfo window] setFrame:NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height) display:YES];
512}
513
514static bool runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, const void* clientInfo)
515{
516    NSAlert *alert = [[NSAlert alloc] init];
517
518    WKURLRef wkURL = WKFrameCopyURL(frame);
519    CFURLRef cfURL = WKURLCopyCFURL(0, wkURL);
520    WKRelease(wkURL);
521
522    [alert setMessageText:[NSString stringWithFormat:@"BeforeUnload confirm dialog from %@.", [(NSURL *)cfURL absoluteString]]];
523    CFRelease(cfURL);
524
525    CFStringRef cfMessage = WKStringCopyCFString(0, message);
526    [alert setInformativeText:(NSString *)cfMessage];
527    CFRelease(cfMessage);
528
529    [alert addButtonWithTitle:@"OK"];
530    [alert addButtonWithTitle:@"Cancel"];
531
532    NSInteger button = [alert runModal];
533    [alert release];
534
535    return button == NSAlertFirstButtonReturn;
536}
537
538static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef listener, const void* clientInfo)
539{
540    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
541    [openPanel setAllowsMultipleSelection:WKOpenPanelParametersGetAllowsMultipleFiles(parameters)];
542
543    WKRetain(listener);
544
545    [openPanel beginSheetModalForWindow:[(BrowserWindowController *)clientInfo window] completionHandler:^(NSInteger result) {
546        if (result == NSFileHandlingPanelOKButton) {
547            WKMutableArrayRef fileURLs = WKMutableArrayCreate();
548
549            NSURL *nsURL;
550            for (nsURL in [openPanel URLs]) {
551                WKURLRef wkURL = WKURLCreateWithCFURL((CFURLRef)nsURL);
552                WKArrayAppendItem(fileURLs, wkURL);
553                WKRelease(wkURL);
554            }
555
556            WKOpenPanelResultListenerChooseFiles(listener, fileURLs);
557
558            WKRelease(fileURLs);
559        } else
560            WKOpenPanelResultListenerCancel(listener);
561
562        WKRelease(listener);
563    }];
564}
565
566- (void)awakeFromNib
567{
568    _webView = [[WKView alloc] initWithFrame:[containerView frame] contextRef:_context];
569
570    [containerView addSubview:_webView];
571    [_webView setFrame:[containerView frame]];
572
573    [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
574
575    WKPageLoaderClient loadClient = {
576        0,      /* version */
577        self,   /* clientInfo */
578        didStartProvisionalLoadForFrame,
579        didReceiveServerRedirectForProvisionalLoadForFrame,
580        didFailProvisionalLoadWithErrorForFrame,
581        didCommitLoadForFrame,
582        didFinishDocumentLoadForFrame,
583        didFinishLoadForFrame,
584        didFailLoadWithErrorForFrame,
585        didSameDocumentNavigationForFrame,
586        didReceiveTitleForFrame,
587        didFirstLayoutForFrame,
588        didFirstVisuallyNonEmptyLayoutForFrame,
589        didRemoveFrameFromHierarchy,
590        didDisplayInsecureContentForFrame,
591        didRunInsecureContentForFrame,
592        0, // canAuthenticateAgainstProtectionSpaceInFrame
593        0, // didReceiveAuthenticationChallengeInFrame
594        didStartProgress,
595        didChangeProgress,
596        didFinishProgress,
597        didBecomeUnresponsive,
598        didBecomeResponsive,
599        processDidExit,
600        didChangeBackForwardList,
601        0 // shouldGoToBackForwardItem
602    };
603    WKPageSetPageLoaderClient(_webView.pageRef, &loadClient);
604
605    WKPagePolicyClient policyClient = {
606        0,          /* version */
607        self,       /* clientInfo */
608        decidePolicyForNavigationAction,
609        decidePolicyForNewWindowAction,
610        decidePolicyForResponse,
611        0           /* unableToImplementPolicy */
612    };
613    WKPageSetPagePolicyClient(_webView.pageRef, &policyClient);
614
615    WKPageUIClient uiClient = {
616        0,          /* version */
617        self,       /* clientInfo */
618        createNewPage,
619        showPage,
620        closePage,
621        0,          /* takeFocus */
622        0,          /* focus */
623        0,          /* unfocus */
624        runJavaScriptAlert,
625        runJavaScriptConfirm,
626        runJavaScriptPrompt,
627        setStatusText,
628        mouseDidMoveOverElement,
629        0,          /* missingPluginButtonClicked */
630        0,          /* didNotHandleKeyEvent */
631        0,          /* toolbarsAreVisible */
632        0,          /* setToolbarsAreVisible */
633        0,          /* menuBarIsVisible */
634        0,          /* setMenuBarIsVisible */
635        0,          /* statusBarIsVisible */
636        0,          /* setStatusBarIsVisible */
637        0,          /* isResizable */
638        0,          /* setIsResizable */
639        getWindowFrame,
640        setWindowFrame,
641        runBeforeUnloadConfirmPanel,
642        0,          /* didDraw */
643        0,          /* pageDidScroll */
644        0,          /* exceededDatabaseQuota */
645        runOpenPanel,
646        0,          /* decidePolicyForGeolocationPermissionRequest */
647        0, // headerHeight
648        0, // footerHeight
649        0, // drawHeader
650        0, // drawFooter
651        0, // printFrame
652        0, // showModal
653        0, // didCompleteRubberBandForMainFrame
654        0, // saveDataToFileInDownloadsFolder
655    };
656    WKPageSetPageUIClient(_webView.pageRef, &uiClient);
657}
658
659- (void)didStartProgress
660{
661    [progressIndicator setDoubleValue:0.0];
662    [progressIndicator setHidden:NO];
663}
664
665- (void)didChangeProgress:(double)value
666{
667    [progressIndicator setDoubleValue:value];
668}
669
670- (void)didFinishProgress
671{
672    [progressIndicator setHidden:YES];
673    [progressIndicator setDoubleValue:1.0];
674}
675
676- (void)updateProvisionalURLForFrame:(WKFrameRef)frame
677{
678    static WKURLRef emptyURL = 0;
679    if (!emptyURL)
680        emptyURL = WKURLCreateWithUTF8CString("");
681
682    WKURLRef url = WKFrameCopyProvisionalURL(frame);
683    if (WKURLIsEqual(url, emptyURL)) {
684        WKRelease(url);
685        return;
686    }
687
688    CFURLRef cfSourceURL = WKURLCopyCFURL(0, url);
689    WKRelease(url);
690
691    [urlText setStringValue:(NSString*)CFURLGetString(cfSourceURL)];
692    CFRelease(cfSourceURL);
693}
694
695- (void)didStartProvisionalLoadForFrame:(WKFrameRef)frame
696{
697    if (!WKFrameIsMainFrame(frame))
698        return;
699
700    [self updateProvisionalURLForFrame:frame];
701}
702
703- (void)didReceiveServerRedirectForProvisionalLoadForFrame:(WKFrameRef)frame
704{
705    if (!WKFrameIsMainFrame(frame))
706        return;
707
708    [self updateProvisionalURLForFrame:frame];
709}
710
711- (void)didFailProvisionalLoadWithErrorForFrame:(WKFrameRef)frame
712{
713    if (!WKFrameIsMainFrame(frame))
714        return;
715
716    [self updateProvisionalURLForFrame:frame];
717}
718
719- (void)didFailLoadWithErrorForFrame:(WKFrameRef)frame
720{
721    if (!WKFrameIsMainFrame(frame))
722        return;
723
724    [self updateProvisionalURLForFrame:frame];
725}
726
727- (void)didSameDocumentNavigationForFrame:(WKFrameRef)frame
728{
729}
730
731- (void)didCommitLoadForFrame:(WKFrameRef)frame
732{
733}
734
735- (void)loadURLString:(NSString *)urlString
736{
737    // FIXME: We shouldn't have to set the url text here.
738    [urlText setStringValue:urlString];
739    [self fetch:nil];
740}
741
742- (IBAction)performFindPanelAction:(id)sender
743{
744    [findPanelWindow makeKeyAndOrderFront:sender];
745}
746
747- (IBAction)find:(id)sender
748{
749    WKStringRef string = WKStringCreateWithCFString((CFStringRef)[sender stringValue]);
750
751    WKPageFindString(_webView.pageRef, string, kWKFindOptionsCaseInsensitive | kWKFindOptionsWrapAround | kWKFindOptionsShowFindIndicator | kWKFindOptionsShowOverlay, 100);
752}
753
754@end
755