blob: e359f231109f6a61e85213428e3302b2245e7ec3 [file] [log] [blame] [edit]
/*
* Copyright (C) 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "WK2BrowserWindowController.h"
#if WK_API_ENABLED
#import "AppDelegate.h"
#import "SettingsController.h"
#import <WebKit/WKFrameInfo.h>
#import <WebKit/WKNavigationDelegate.h>
#import <WebKit/WKPreferencesPrivate.h>
#import <WebKit/WKUIDelegate.h>
#import <WebKit/WKWebView.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/_WKWebsiteDataStore.h>
static void* keyValueObservingContext = &keyValueObservingContext;
@interface WK2BrowserWindowController () <WKNavigationDelegate, WKUIDelegate>
@end
@implementation WK2BrowserWindowController {
WKWebViewConfiguration *_configuration;
WKWebView *_webView;
BOOL _zoomTextOnly;
BOOL _isPrivateBrowsingWindow;
}
- (void)awakeFromNib
{
_webView = [[WKWebView alloc] initWithFrame:[containerView bounds] configuration:_configuration];
[self didChangeSettings];
_webView.allowsMagnification = YES;
_webView.allowsBackForwardNavigationGestures = YES;
[_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[containerView addSubview:_webView];
[progressIndicator bind:NSHiddenBinding toObject:_webView withKeyPath:@"loading" options:@{ NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName }];
[progressIndicator bind:NSValueBinding toObject:_webView withKeyPath:@"estimatedProgress" options:nil];
[_webView addObserver:self forKeyPath:@"title" options:0 context:keyValueObservingContext];
[_webView addObserver:self forKeyPath:@"URL" options:0 context:keyValueObservingContext];
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
_zoomTextOnly = NO;
}
- (instancetype)initWithConfiguration:(WKWebViewConfiguration *)configuration
{
if (!(self = [super initWithWindowNibName:@"BrowserWindow"]))
return nil;
_configuration = [configuration copy];
_isPrivateBrowsingWindow = _configuration._websiteDataStore.isNonPersistent;
return self;
}
- (void)dealloc
{
[_webView removeObserver:self forKeyPath:@"title"];
[_webView removeObserver:self forKeyPath:@"URL"];
[progressIndicator unbind:NSHiddenBinding];
[progressIndicator unbind:NSValueBinding];
[_webView release];
[_configuration release];
[super dealloc];
}
- (IBAction)fetch:(id)sender
{
[urlText setStringValue:[self addProtocolIfNecessary:[urlText stringValue]]];
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[urlText stringValue] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];
}
- (IBAction)showHideWebView:(id)sender
{
BOOL hidden = ![_webView isHidden];
[_webView setHidden:hidden];
}
- (IBAction)removeReinsertWebView:(id)sender
{
if ([_webView window]) {
[_webView retain];
[_webView removeFromSuperview];
} else {
[containerView addSubview:_webView];
[_webView release];
}
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
SEL action = [menuItem action];
if (action == @selector(zoomIn:))
return [self canZoomIn];
if (action == @selector(zoomOut:))
return [self canZoomOut];
if (action == @selector(resetZoom:))
return [self canResetZoom];
// Disabled until missing WK2 functionality is exposed via API/SPI.
if (action == @selector(dumpSourceToConsole:)
|| action == @selector(find:)
|| action == @selector(forceRepaint:))
return NO;
if (action == @selector(showHideWebView:))
[menuItem setTitle:[_webView isHidden] ? @"Show Web View" : @"Hide Web View"];
else if (action == @selector(removeReinsertWebView:))
[menuItem setTitle:[_webView window] ? @"Remove Web View" : @"Insert Web View"];
else if (action == @selector(toggleZoomMode:))
[menuItem setState:_zoomTextOnly ? NSOnState : NSOffState];
return YES;
}
- (IBAction)reload:(id)sender
{
[_webView reload];
}
- (IBAction)forceRepaint:(id)sender
{
// FIXME: This doesn't actually force a repaint.
[_webView setNeedsDisplay:YES];
}
- (IBAction)goBack:(id)sender
{
[_webView goBack];
}
- (IBAction)goForward:(id)sender
{
[_webView goForward];
}
- (IBAction)toggleZoomMode:(id)sender
{
if (_zoomTextOnly) {
_zoomTextOnly = NO;
double currentTextZoom = _webView._textZoomFactor;
_webView._textZoomFactor = 1;
_webView._pageZoomFactor = currentTextZoom;
} else {
_zoomTextOnly = YES;
double currentPageZoom = _webView._pageZoomFactor;
_webView._textZoomFactor = currentPageZoom;
_webView._pageZoomFactor = 1;
}
}
- (IBAction)resetZoom:(id)sender
{
if (![self canResetZoom])
return;
if (_zoomTextOnly)
_webView._textZoomFactor = 1;
else
_webView._pageZoomFactor = 1;
}
- (BOOL)canResetZoom
{
return _zoomTextOnly ? (_webView._textZoomFactor != 1) : (_webView._pageZoomFactor != 1);
}
- (IBAction)dumpSourceToConsole:(id)sender
{
}
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
{
SEL action = item.action;
if (action == @selector(goBack:) || action == @selector(goForward:))
return [_webView validateUserInterfaceItem:item];
return YES;
}
- (void)validateToolbar
{
[toolbar validateVisibleItems];
}
- (BOOL)windowShouldClose:(id)sender
{
return YES;
}
- (void)windowWillClose:(NSNotification *)notification
{
[(BrowserAppDelegate *)[[NSApplication sharedApplication] delegate] browserWindowWillClose:self.window];
[self autorelease];
}
- (void)applicationTerminating
{
}
#define DefaultMinimumZoomFactor (.5)
#define DefaultMaximumZoomFactor (3.0)
#define DefaultZoomFactorRatio (1.2)
- (CGFloat)currentZoomFactor
{
return _zoomTextOnly ? _webView._textZoomFactor : _webView._pageZoomFactor;
}
- (void)setCurrentZoomFactor:(CGFloat)factor
{
if (_zoomTextOnly)
_webView._textZoomFactor = factor;
else
_webView._pageZoomFactor = factor;
}
- (BOOL)canZoomIn
{
return self.currentZoomFactor * DefaultZoomFactorRatio < DefaultMaximumZoomFactor;
}
- (void)zoomIn:(id)sender
{
if (!self.canZoomIn)
return;
self.currentZoomFactor *= DefaultZoomFactorRatio;
}
- (BOOL)canZoomOut
{
return self.currentZoomFactor / DefaultZoomFactorRatio > DefaultMinimumZoomFactor;
}
- (void)zoomOut:(id)sender
{
if (!self.canZoomIn)
return;
self.currentZoomFactor /= DefaultZoomFactorRatio;
}
- (void)didChangeSettings
{
SettingsController *settings = [SettingsController shared];
WKPreferences *preferences = _webView.configuration.preferences;
preferences._tiledScrollingIndicatorVisible = settings.tiledScrollingIndicatorVisible;
preferences._compositingBordersVisible = settings.layerBordersVisible;
preferences._compositingRepaintCountersVisible = settings.layerBordersVisible;
preferences._simpleLineLayoutDebugBordersEnabled = settings.simpleLineLayoutDebugBordersEnabled;
BOOL useTransparentWindows = settings.useTransparentWindows;
if (useTransparentWindows != _webView._drawsTransparentBackground) {
[self.window setOpaque:!useTransparentWindows];
[self.window setHasShadow:!useTransparentWindows];
_webView._drawsTransparentBackground = useTransparentWindows;
[self.window display];
}
BOOL usePaginatedMode = settings.usePaginatedMode;
if (usePaginatedMode != (_webView._paginationMode != _WKPaginationModeUnpaginated)) {
if (usePaginatedMode) {
_webView._paginationMode = _WKPaginationModeLeftToRight;
_webView._pageLength = _webView.bounds.size.width / 2;
_webView._gapBetweenPages = 10;
} else
_webView._paginationMode = _WKPaginationModeUnpaginated;
}
NSUInteger visibleOverlayRegions = 0;
if (settings.nonFastScrollableRegionOverlayVisible)
visibleOverlayRegions |= _WKNonFastScrollableRegion;
if (settings.wheelEventHandlerRegionOverlayVisible)
visibleOverlayRegions |= _WKWheelEventHandlerRegion;
preferences._visibleDebugOverlayRegions = visibleOverlayRegions;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context != keyValueObservingContext || object != _webView)
return;
if ([keyPath isEqualToString:@"title"])
self.window.title = [NSString stringWithFormat:@"%@%@ [WK2 %d]", _isPrivateBrowsingWindow ? @"🙈 " : @"", _webView.title, _webView._webProcessIdentifier];
else if ([keyPath isEqualToString:@"URL"])
[self updateTextFieldFromURL:_webView.URL];
}
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
{
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithFormat:@"JavaScript alert dialog from %@.", [frame.request.URL absoluteString]]];
[alert setInformativeText:message];
[alert addButtonWithTitle:@"OK"];
[alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
completionHandler();
[alert release];
}];
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
{
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithFormat:@"JavaScript confirm dialog from %@.", [frame.request.URL absoluteString]]];
[alert setInformativeText:message];
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
[alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
completionHandler(response == NSAlertFirstButtonReturn);
[alert release];
}];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *result))completionHandler
{
NSAlert* alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithFormat:@"JavaScript prompt dialog from %@.", [frame.request.URL absoluteString]]];
[alert setInformativeText:prompt];
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
NSTextField* input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
[input setStringValue:defaultText];
[alert setAccessoryView:input];
[alert beginSheetModalForWindow:self.window completionHandler:^void (NSModalResponse response) {
[input validateEditing];
completionHandler(response == NSAlertFirstButtonReturn ? [input stringValue] : nil);
[alert release];
}];
}
- (void)updateTextFieldFromURL:(NSURL *)URL
{
if (!URL)
return;
if (!URL.absoluteString.length)
return;
urlText.stringValue = [[URL absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
- (void)loadURLString:(NSString *)urlString
{
// FIXME: We shouldn't have to set the url text here.
[urlText setStringValue:urlString];
[self fetch:nil];
}
- (IBAction)performFindPanelAction:(id)sender
{
[findPanelWindow makeKeyAndOrderFront:sender];
}
- (IBAction)find:(id)sender
{
}
- (IBAction)clearWebsiteData:(id)sender
{
[_configuration._websiteDataStore removeDataOfTypes:WKWebsiteDataTypeAll modifiedSince:[NSDate distantPast] completionHandler:^{
NSLog(@"Did clear website data.");
}];
}
#pragma mark WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
LOG(@"decidePolicyForNavigationResponse");
decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
LOG(@"didStartProvisionalNavigation: %@", navigation);
}
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
{
LOG(@"didReceiveServerRedirectForProvisionalNavigation: %@", navigation);
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
LOG(@"didFailProvisionalNavigation: %@navigation, error: %@", navigation, error);
}
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
{
LOG(@"didCommitNavigation: %@", navigation);
}
- (void)webView:(WKWebView *)webView didFinishLoadingNavigation:(WKNavigation *)navigation
{
LOG(@"didFinishLoadingNavigation: %@", navigation);
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
LOG(@"didFailNavigation: %@, error %@", navigation, error);
}
- (void)_webViewWebProcessDidCrash:(WKWebView *)webView
{
NSLog(@"WebContent process crashed; reloading");
[self reload:nil];
}
@end
#endif // WK_API_ENABLED