Expose a way for MiniBrowser to simulate header and footer banners
[WebKit-https.git] / Tools / MiniBrowser / mac / WK2BrowserWindowController.m
1 /*
2  * Copyright (C) 2010-2016 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 "WK2BrowserWindowController.h"
27
28 #if WK_API_ENABLED
29
30 #import "AppDelegate.h"
31 #import "SettingsController.h"
32 #import <WebKit/WKFrameInfo.h>
33 #import <WebKit/WKNavigationActionPrivate.h>
34 #import <WebKit/WKNavigationDelegate.h>
35 #import <WebKit/WKPreferencesPrivate.h>
36 #import <WebKit/WKUIDelegate.h>
37 #import <WebKit/WKWebViewConfigurationPrivate.h>
38 #import <WebKit/WKWebViewPrivate.h>
39 #import <WebKit/WKWebsiteDataStorePrivate.h>
40 #import <WebKit/WebNSURLExtras.h>
41 #import <WebKit/_WKIconLoadingDelegate.h>
42 #import <WebKit/_WKLinkIconParameters.h>
43 #import <WebKit/_WKUserInitiatedAction.h>
44
45 static void* keyValueObservingContext = &keyValueObservingContext;
46 static const int testHeaderBannerHeight = 42;
47 static const int testFooterBannerHeight = 58;
48
49 @interface WK2BrowserWindowController () <WKNavigationDelegate, WKUIDelegate, _WKIconLoadingDelegate>
50 @end
51
52 @implementation WK2BrowserWindowController {
53     WKWebViewConfiguration *_configuration;
54     WKWebView *_webView;
55     BOOL _zoomTextOnly;
56     BOOL _isPrivateBrowsingWindow;
57
58     BOOL _useShrinkToFit;
59 }
60
61 - (void)awakeFromNib
62 {
63     _webView = [[WKWebView alloc] initWithFrame:[containerView bounds] configuration:_configuration];
64     [self didChangeSettings];
65
66     _webView.allowsMagnification = YES;
67     _webView.allowsBackForwardNavigationGestures = YES;
68
69     [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
70     [containerView addSubview:_webView];
71
72     [progressIndicator bind:NSHiddenBinding toObject:_webView withKeyPath:@"loading" options:@{ NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName }];
73     [progressIndicator bind:NSValueBinding toObject:_webView withKeyPath:@"estimatedProgress" options:nil];
74
75     [_webView addObserver:self forKeyPath:@"title" options:0 context:keyValueObservingContext];
76     [_webView addObserver:self forKeyPath:@"URL" options:0 context:keyValueObservingContext];
77
78     _webView.navigationDelegate = self;
79     _webView.UIDelegate = self;
80
81     // This setting installs the new WK2 Icon Loading Delegate and tests that mechanism by
82     // telling WebKit to load every icon referenced by the page.
83     if ([[SettingsController shared] loadsAllSiteIcons])
84         _webView._iconLoadingDelegate = self;
85     
86     _webView._observedRenderingProgressEvents = _WKRenderingProgressEventFirstLayout
87         | _WKRenderingProgressEventFirstVisuallyNonEmptyLayout
88         | _WKRenderingProgressEventFirstPaintWithSignificantArea
89         | _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering
90         | _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering;
91
92     _zoomTextOnly = NO;
93 }
94
95 - (instancetype)initWithConfiguration:(WKWebViewConfiguration *)configuration
96 {
97     if (!(self = [super initWithWindowNibName:@"BrowserWindow"]))
98         return nil;
99
100     _configuration = [configuration copy];
101     _isPrivateBrowsingWindow = !_configuration.websiteDataStore.isPersistent;
102
103     return self;
104 }
105
106 - (void)dealloc
107 {
108     [_webView removeObserver:self forKeyPath:@"title"];
109     [_webView removeObserver:self forKeyPath:@"URL"];
110     
111     [progressIndicator unbind:NSHiddenBinding];
112     [progressIndicator unbind:NSValueBinding];
113
114     [_webView release];
115     [_configuration release];
116
117     [super dealloc];
118 }
119
120 - (IBAction)fetch:(id)sender
121 {
122     [urlText setStringValue:[self addProtocolIfNecessary:[urlText stringValue]]];
123
124     [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL _webkit_URLWithUserTypedString:[urlText stringValue]]]];
125 }
126
127 - (IBAction)showHideWebView:(id)sender
128 {
129     BOOL hidden = ![_webView isHidden];
130     
131     [_webView setHidden:hidden];
132 }
133
134 - (IBAction)removeReinsertWebView:(id)sender
135 {
136     if ([_webView window]) {
137         [_webView retain];
138         [_webView removeFromSuperview]; 
139     } else {
140         [containerView addSubview:_webView];
141         [_webView release];
142     }
143 }
144
145 - (IBAction)setPageScale:(id)sender
146 {
147     CGFloat scale = [self pageScaleForMenuItemTag:[sender tag]];
148     [_webView _setPageScale:scale withOrigin:CGPointZero];
149 }
150
151 - (CGFloat)viewScaleForMenuItemTag:(NSInteger)tag
152 {
153     if (tag == 1)
154         return 1;
155     if (tag == 2)
156         return 0.75;
157     if (tag == 3)
158         return 0.5;
159     if (tag == 4)
160         return 0.25;
161
162     return 1;
163 }
164
165 - (IBAction)setViewScale:(id)sender
166 {
167     CGFloat scale = [self viewScaleForMenuItemTag:[sender tag]];
168     CGFloat oldScale = [_webView _viewScale];
169
170     if (scale == oldScale)
171         return;
172
173     [_webView _setLayoutMode:_WKLayoutModeDynamicSizeComputedFromViewScale];
174
175     NSRect oldFrame = self.window.frame;
176     NSSize newFrameSize = NSMakeSize(oldFrame.size.width * (scale / oldScale), oldFrame.size.height * (scale / oldScale));
177     [self.window setFrame:NSMakeRect(oldFrame.origin.x, oldFrame.origin.y - (newFrameSize.height - oldFrame.size.height), newFrameSize.width, newFrameSize.height) display:NO animate:NO];
178
179     [_webView _setViewScale:scale];
180 }
181
182 static BOOL areEssentiallyEqual(double a, double b)
183 {
184     double tolerance = 0.001;
185     return (fabs(a - b) <= tolerance);
186 }
187
188 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
189 {
190     SEL action = [menuItem action];
191
192     if (action == @selector(zoomIn:))
193         return [self canZoomIn];
194     if (action == @selector(zoomOut:))
195         return [self canZoomOut];
196     if (action == @selector(resetZoom:))
197         return [self canResetZoom];
198     
199     // Disabled until missing WK2 functionality is exposed via API/SPI.
200     if (action == @selector(dumpSourceToConsole:)
201         || action == @selector(find:)
202         || action == @selector(forceRepaint:))
203         return NO;
204     
205     if (action == @selector(showHideWebView:))
206         [menuItem setTitle:[_webView isHidden] ? @"Show Web View" : @"Hide Web View"];
207     else if (action == @selector(removeReinsertWebView:))
208         [menuItem setTitle:[_webView window] ? @"Remove Web View" : @"Insert Web View"];
209     else if (action == @selector(toggleZoomMode:))
210         [menuItem setState:_zoomTextOnly ? NSOnState : NSOffState];
211
212     if (action == @selector(setPageScale:))
213         [menuItem setState:areEssentiallyEqual([_webView _pageScale], [self pageScaleForMenuItemTag:[menuItem tag]])];
214
215     if (action == @selector(setViewScale:))
216         [menuItem setState:areEssentiallyEqual([_webView _viewScale], [self viewScaleForMenuItemTag:[menuItem tag]])];
217
218     return YES;
219 }
220
221 - (IBAction)reload:(id)sender
222 {
223     [_webView reload];
224 }
225
226 - (IBAction)forceRepaint:(id)sender
227 {
228     // FIXME: This doesn't actually force a repaint.
229     [_webView setNeedsDisplay:YES];
230 }
231
232 - (IBAction)goBack:(id)sender
233 {
234     [_webView goBack];
235 }
236
237 - (IBAction)goForward:(id)sender
238 {
239     [_webView goForward];
240 }
241
242 - (IBAction)toggleZoomMode:(id)sender
243 {
244     if (_zoomTextOnly) {
245         _zoomTextOnly = NO;
246         double currentTextZoom = _webView._textZoomFactor;
247         _webView._textZoomFactor = 1;
248         _webView._pageZoomFactor = currentTextZoom;
249     } else {
250         _zoomTextOnly = YES;
251         double currentPageZoom = _webView._pageZoomFactor;
252         _webView._textZoomFactor = currentPageZoom;
253         _webView._pageZoomFactor = 1;
254     }
255 }
256
257 - (IBAction)resetZoom:(id)sender
258 {
259     if (![self canResetZoom])
260         return;
261
262     if (_zoomTextOnly)
263         _webView._textZoomFactor = 1;
264     else
265         _webView._pageZoomFactor = 1;
266 }
267
268 - (BOOL)canResetZoom
269 {
270     return _zoomTextOnly ? (_webView._textZoomFactor != 1) : (_webView._pageZoomFactor != 1);
271 }
272
273 - (IBAction)toggleShrinkToFit:(id)sender
274 {
275     _useShrinkToFit = !_useShrinkToFit;
276     toggleUseShrinkToFitButton.image = _useShrinkToFit ? [NSImage imageNamed:@"NSExitFullScreenTemplate"] : [NSImage imageNamed:@"NSEnterFullScreenTemplate"];
277     [_webView _setLayoutMode:_useShrinkToFit ? _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize : _WKLayoutModeViewSize];
278 }
279
280 - (IBAction)dumpSourceToConsole:(id)sender
281 {
282 }
283
284 - (NSURL *)currentURL
285 {
286     return _webView.URL;
287 }
288
289 - (NSView *)mainContentView
290 {
291     return _webView;
292 }
293
294 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
295 {
296     SEL action = item.action;
297
298     if (action == @selector(goBack:) || action == @selector(goForward:))
299         return [_webView validateUserInterfaceItem:item];
300
301     return YES;
302 }
303
304 - (void)validateToolbar
305 {
306     [toolbar validateVisibleItems];
307 }
308
309 - (BOOL)windowShouldClose:(id)sender
310 {
311     return YES;
312 }
313
314 - (void)windowWillClose:(NSNotification *)notification
315 {
316     [(BrowserAppDelegate *)[[NSApplication sharedApplication] delegate] browserWindowWillClose:self.window];
317     [self autorelease];
318 }
319
320 - (void)applicationTerminating
321 {
322 }
323
324 #define DefaultMinimumZoomFactor (.5)
325 #define DefaultMaximumZoomFactor (3.0)
326 #define DefaultZoomFactorRatio (1.2)
327
328 - (CGFloat)currentZoomFactor
329 {
330     return _zoomTextOnly ? _webView._textZoomFactor : _webView._pageZoomFactor;
331 }
332
333 - (void)setCurrentZoomFactor:(CGFloat)factor
334 {
335     if (_zoomTextOnly)
336         _webView._textZoomFactor = factor;
337     else
338         _webView._pageZoomFactor = factor;
339 }
340
341 - (BOOL)canZoomIn
342 {
343     return self.currentZoomFactor * DefaultZoomFactorRatio < DefaultMaximumZoomFactor;
344 }
345
346 - (void)zoomIn:(id)sender
347 {
348     if (!self.canZoomIn)
349         return;
350
351     self.currentZoomFactor *= DefaultZoomFactorRatio;
352 }
353
354 - (BOOL)canZoomOut
355 {
356     return self.currentZoomFactor / DefaultZoomFactorRatio > DefaultMinimumZoomFactor;
357 }
358
359 - (void)zoomOut:(id)sender
360 {
361     if (!self.canZoomIn)
362         return;
363
364     self.currentZoomFactor /= DefaultZoomFactorRatio;
365 }
366
367 - (void)didChangeSettings
368 {
369     SettingsController *settings = [SettingsController shared];
370     WKPreferences *preferences = _webView.configuration.preferences;
371
372     preferences._tiledScrollingIndicatorVisible = settings.tiledScrollingIndicatorVisible;
373     preferences._compositingBordersVisible = settings.layerBordersVisible;
374     preferences._compositingRepaintCountersVisible = settings.layerBordersVisible;
375     preferences._simpleLineLayoutDebugBordersEnabled = settings.simpleLineLayoutDebugBordersEnabled;
376     preferences._acceleratedDrawingEnabled = settings.acceleratedDrawingEnabled;
377     preferences._resourceUsageOverlayVisible = settings.resourceUsageOverlayVisible;
378     preferences._displayListDrawingEnabled = settings.displayListDrawingEnabled;
379     preferences._visualViewportEnabled = settings.visualViewportEnabled;
380     preferences._largeImageAsyncDecodingEnabled = settings.largeImageAsyncDecodingEnabled;
381     preferences._animatedImageAsyncDecodingEnabled = settings.animatedImageAsyncDecodingEnabled;
382
383     _webView.configuration.websiteDataStore._resourceLoadStatisticsEnabled = settings.resourceLoadStatisticsEnabled;
384
385     BOOL useTransparentWindows = settings.useTransparentWindows;
386     if (useTransparentWindows != !_webView._drawsBackground) {
387         [self.window setOpaque:!useTransparentWindows];
388         [self.window setBackgroundColor:[NSColor clearColor]];
389         [self.window setHasShadow:!useTransparentWindows];
390
391         _webView._drawsBackground = !useTransparentWindows;
392
393         [self.window display];
394     }
395
396     BOOL usePaginatedMode = settings.usePaginatedMode;
397     if (usePaginatedMode != (_webView._paginationMode != _WKPaginationModeUnpaginated)) {
398         if (usePaginatedMode) {
399             _webView._paginationMode = _WKPaginationModeLeftToRight;
400             _webView._pageLength = _webView.bounds.size.width / 2;
401             _webView._gapBetweenPages = 10;
402         } else
403             _webView._paginationMode = _WKPaginationModeUnpaginated;
404     }
405     
406     NSUInteger visibleOverlayRegions = 0;
407     if (settings.nonFastScrollableRegionOverlayVisible)
408         visibleOverlayRegions |= _WKNonFastScrollableRegion;
409     if (settings.wheelEventHandlerRegionOverlayVisible)
410         visibleOverlayRegions |= _WKWheelEventHandlerRegion;
411     
412     preferences._visibleDebugOverlayRegions = visibleOverlayRegions;
413
414     [_webView _setHeaderBannerHeight:[settings isSpaceReservedForBanners] ? testHeaderBannerHeight : 0];
415     [_webView _setFooterBannerHeight:[settings isSpaceReservedForBanners] ? testFooterBannerHeight : 0];
416 }
417
418 - (void)updateTitle:(NSString *)title
419 {
420     if (!title) {
421         NSURL *url = _webView.URL;
422         title = url.lastPathComponent ?: url._web_userVisibleString;
423     }
424
425     self.window.title = [NSString stringWithFormat:@"%@%@ [WK2 %d]", _isPrivateBrowsingWindow ? @"🙈 " : @"", title, _webView._webProcessIdentifier];
426 }
427
428 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
429 {
430     if (context != keyValueObservingContext || object != _webView)
431         return;
432
433     if ([keyPath isEqualToString:@"title"])
434         [self updateTitle:_webView.title];
435     else if ([keyPath isEqualToString:@"URL"])
436         [self updateTextFieldFromURL:_webView.URL];
437 }
438
439 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
440 {
441     WK2BrowserWindowController *controller = [[WK2BrowserWindowController alloc] initWithConfiguration:configuration];
442     [controller awakeFromNib];
443     [controller.window makeKeyAndOrderFront:self];
444
445     return controller->_webView;
446 }
447
448 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
449 {
450     NSAlert* alert = [[NSAlert alloc] init];
451
452     [alert setMessageText:[NSString stringWithFormat:@"JavaScript alert dialog from %@.", [frame.request.URL absoluteString]]];
453     [alert setInformativeText:message];
454     [alert addButtonWithTitle:@"OK"];
455
456     [alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
457         completionHandler();
458         [alert release];
459     }];
460 }
461
462 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
463 {
464     NSAlert* alert = [[NSAlert alloc] init];
465
466     [alert setMessageText:[NSString stringWithFormat:@"JavaScript confirm dialog from %@.", [frame.request.URL  absoluteString]]];
467     [alert setInformativeText:message];
468     
469     [alert addButtonWithTitle:@"OK"];
470     [alert addButtonWithTitle:@"Cancel"];
471
472     [alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
473         completionHandler(response == NSAlertFirstButtonReturn);
474         [alert release];
475     }];
476 }
477
478 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *result))completionHandler
479 {
480     NSAlert* alert = [[NSAlert alloc] init];
481
482     [alert setMessageText:[NSString stringWithFormat:@"JavaScript prompt dialog from %@.", [frame.request.URL absoluteString]]];
483     [alert setInformativeText:prompt];
484     
485     [alert addButtonWithTitle:@"OK"];
486     [alert addButtonWithTitle:@"Cancel"];
487     
488     NSTextField* input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
489     [input setStringValue:defaultText];
490     [alert setAccessoryView:input];
491     
492     [alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
493         [input validateEditing];
494         completionHandler(response == NSAlertFirstButtonReturn ? [input stringValue] : nil);
495         [alert release];
496     }];
497 }
498
499 #if __has_feature(objc_generics)
500 - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * URLs))completionHandler
501 #else
502 - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray *URLs))completionHandler
503 #endif
504 {
505     NSOpenPanel *openPanel = [NSOpenPanel openPanel];
506
507     openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
508
509     [openPanel beginSheetModalForWindow:webView.window completionHandler:^(NSInteger result) {
510         if (result == NSFileHandlingPanelOKButton)
511             completionHandler(openPanel.URLs);
512         else
513             completionHandler(nil);
514     }];
515 }
516
517 - (void)updateTextFieldFromURL:(NSURL *)URL
518 {
519     if (!URL)
520         return;
521
522     if (!URL.absoluteString.length)
523         return;
524
525     urlText.stringValue = [URL _web_userVisibleString];
526 }
527
528 - (void)loadURLString:(NSString *)urlString
529 {
530     // FIXME: We shouldn't have to set the url text here.
531     [urlText setStringValue:urlString];
532     [self fetch:nil];
533 }
534
535 - (IBAction)performFindPanelAction:(id)sender
536 {
537     [findPanelWindow makeKeyAndOrderFront:sender];
538 }
539
540 - (IBAction)find:(id)sender
541 {
542 }
543
544 static NSSet *dataTypes()
545 {
546     return [WKWebsiteDataStore allWebsiteDataTypes];
547 }
548
549 - (IBAction)fetchWebsiteData:(id)sender
550 {
551     [_configuration.websiteDataStore _fetchDataRecordsOfTypes:dataTypes() withOptions:_WKWebsiteDataStoreFetchOptionComputeSizes completionHandler:^(NSArray *websiteDataRecords) {
552         NSLog(@"did fetch website data %@.", websiteDataRecords);
553     }];
554 }
555
556 - (IBAction)fetchAndClearWebsiteData:(id)sender
557 {
558     [_configuration.websiteDataStore fetchDataRecordsOfTypes:dataTypes() completionHandler:^(NSArray *websiteDataRecords) {
559         [_configuration.websiteDataStore removeDataOfTypes:dataTypes() forDataRecords:websiteDataRecords completionHandler:^{
560             [_configuration.websiteDataStore fetchDataRecordsOfTypes:dataTypes() completionHandler:^(NSArray *websiteDataRecords) {
561                 NSLog(@"did clear website data, after clearing data is %@.", websiteDataRecords);
562             }];
563         }];
564     }];
565 }
566
567 - (IBAction)clearWebsiteData:(id)sender
568 {
569     [_configuration.websiteDataStore removeDataOfTypes:dataTypes() modifiedSince:[NSDate distantPast] completionHandler:^{
570         NSLog(@"Did clear website data.");
571     }];
572 }
573
574 - (IBAction)printWebView:(id)sender
575 {
576     [[_webView _printOperationWithPrintInfo:[NSPrintInfo sharedPrintInfo]] runOperationModalForWindow:self.window delegate:nil didRunSelector:nil contextInfo:nil];
577 }
578
579 #pragma mark WKNavigationDelegate
580
581 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
582 {
583     LOG(@"decidePolicyForNavigationAction");
584
585     if (navigationAction._canHandleRequest) {
586         decisionHandler(WKNavigationActionPolicyAllow);
587         return;
588     }
589
590     if (navigationAction._userInitiatedAction && !navigationAction._userInitiatedAction.isConsumed) {
591         [navigationAction._userInitiatedAction consume];
592         [[NSWorkspace sharedWorkspace] openURL:navigationAction.request.URL];
593     }
594
595     decisionHandler(WKNavigationActionPolicyCancel);
596 }
597
598 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
599 {
600     LOG(@"decidePolicyForNavigationResponse");
601     decisionHandler(WKNavigationResponsePolicyAllow);
602 }
603
604 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
605 {
606     LOG(@"didStartProvisionalNavigation: %@", navigation);
607 }
608
609 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
610 {
611     LOG(@"didReceiveServerRedirectForProvisionalNavigation: %@", navigation);
612 }
613
614 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
615 {
616     LOG(@"didFailProvisionalNavigation: %@navigation, error: %@", navigation, error);
617 }
618
619 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
620 {
621     LOG(@"didCommitNavigation: %@", navigation);
622     [self updateTitle:nil];
623 }
624
625 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
626 {
627     LOG(@"didFinishNavigation: %@", navigation);
628     
629     // Banner heights don't persist across page loads (oddly, since Page stores them), so reset on every page load.
630     if ([[SettingsController shared] isSpaceReservedForBanners]) {
631         [_webView _setHeaderBannerHeight:testHeaderBannerHeight];
632         [_webView _setFooterBannerHeight:testFooterBannerHeight];
633     }
634 }
635
636 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler
637 {
638     LOG(@"didReceiveAuthenticationChallenge: %@", challenge);
639     completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
640 }
641
642 - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
643 {
644     LOG(@"didFailNavigation: %@, error %@", navigation, error);
645 }
646
647 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
648 {
649     NSLog(@"WebContent process crashed; reloading");
650     [self reload:nil];
651 }
652
653 - (void)_webView:(WKWebView *)webView renderingProgressDidChange:(_WKRenderingProgressEvents)progressEvents
654 {
655     if (progressEvents & _WKRenderingProgressEventFirstLayout)
656         LOG(@"renderingProgressDidChange: %@", @"first layout");
657
658     if (progressEvents & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
659         LOG(@"renderingProgressDidChange: %@", @"first visually non-empty layout");
660
661     if (progressEvents & _WKRenderingProgressEventFirstPaintWithSignificantArea)
662         LOG(@"renderingProgressDidChange: %@", @"first paint with significant area");
663
664     if (progressEvents & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering)
665         LOG(@"renderingProgressDidChange: %@", @"first layout after suppressed incremental rendering");
666
667     if (progressEvents & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering)
668         LOG(@"renderingProgressDidChange: %@", @"first paint after suppressed incremental rendering");
669 }
670
671 - (void)webView:(WKWebView *)webView shouldLoadIconWithParameters:(_WKLinkIconParameters *)parameters completionHandler:(void (^)(void (^)(NSData*)))completionHandler
672 {
673     completionHandler(^void (NSData *data) {
674         LOG(@"Icon URL %@ received icon data of length %u", parameters.url, (unsigned)data.length);
675     });
676 }
677
678 @end
679
680 #endif // WK_API_ENABLED