2020-04-22 20:52:17 +03:00
/ *
* Copyright ( C ) 2010 -2016 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 "BrowserWindowController.h"
# import "AppDelegate.h"
2021-03-26 19:44:31 +03:00
# import < SecurityInterface / SFCertificateTrustPanel . h >
2020-04-22 20:52:17 +03:00
# import < WebKit / WKFrameInfo . h >
# import < WebKit / WKNavigationActionPrivate . h >
# import < WebKit / WKNavigationDelegatePrivate . h >
# import < WebKit / WKPreferencesPrivate . h >
# import < WebKit / WKUIDelegate . h >
# import < WebKit / WKUIDelegatePrivate . h >
# import < WebKit / WKWebViewConfigurationPrivate . h >
# import < WebKit / WKWebViewPrivate . h >
# import < WebKit / WKWebViewPrivateForTesting . h >
# import < WebKit / WKWebsiteDataStorePrivate . h >
# import < WebKit / WebNSURLExtras . h >
# import < WebKit / _WKIconLoadingDelegate . h >
# import < WebKit / _WKInspector . h >
# import < WebKit / _WKLinkIconParameters . h >
# import < WebKit / _WKUserInitiatedAction . h >
static void * keyValueObservingContext = & keyValueObservingContext ;
@ interface PlaywrightNSTextFinder : NSTextFinder
@ property ( nonatomic , copy ) dispatch_block _t hideInterfaceCallback ;
@ end
@ implementation PlaywrightNSTextFinder
- ( void ) performAction : ( NSTextFinderAction ) op
{
[ super performAction : op ] ;
if ( op = = NSTextFinderActionHideFindInterface && _hideInterfaceCallback )
_hideInterfaceCallback ( ) ;
}
@ end
@ interface BrowserWindowController ( ) < NSTextFinderBarContainer , WKNavigationDelegate , WKUIDelegate , _WKIconLoadingDelegate , NSSharingServicePickerDelegate , NSSharingServiceDelegate >
@ end
@ implementation BrowserWindowController {
IBOutlet NSProgressIndicator * progressIndicator ;
IBOutlet NSButton * reloadButton ;
2021-03-26 19:44:31 +03:00
IBOutlet NSButton * lockButton ;
2020-04-22 20:52:17 +03:00
IBOutlet NSButton * backButton ;
IBOutlet NSButton * forwardButton ;
IBOutlet NSButton * share ;
IBOutlet NSToolbar * toolbar ;
IBOutlet NSTextField * urlText ;
IBOutlet NSView * containerView ;
IBOutlet NSButton * toggleUseShrinkToFitButton ;
WKWebViewConfiguration * _configuration ;
WKWebView * _webView ;
BOOL _zoomTextOnly ;
BOOL _isPrivateBrowsingWindow ;
NSAlert * _alert ;
BOOL _useShrinkToFit ;
PlaywrightNSTextFinder * _textFinder ;
NSView * _textFindBarView ;
BOOL _findBarVisible ;
}
- ( id ) initWithWindow : ( NSWindow * ) window
{
self = [ super initWithWindow : window ] ;
return self ;
}
- ( void ) windowDidLoad
{
2021-03-26 19:44:31 +03:00
# if __MAC _OS _X _VERSION _MIN _REQUIRED >= 110000
// FIXME : We should probably adopt the default unified style , but we ' d need
// somewhere to put the window / page title .
self . window . toolbarStyle = NSWindowToolbarStyleExpanded ;
reloadButton . image = [ NSImage imageWithSystemSymbolName : @ "arrow.clockwise" accessibilityDescription : @ "Reload" ] ;
// FIXME : Should these be localized ?
backButton . image = [ NSImage imageWithSystemSymbolName : @ "chevron.left" accessibilityDescription : @ "Go back" ] ;
forwardButton . image = [ NSImage imageWithSystemSymbolName : @ "chevron.right" accessibilityDescription : @ "Go forward" ] ;
share . image = [ NSImage imageWithSystemSymbolName : @ "square.and.arrow.up" accessibilityDescription : @ "Share" ] ;
toggleUseShrinkToFitButton . image = [ NSImage imageWithSystemSymbolName : @ "arrow.up.left.and.arrow.down.right" accessibilityDescription : @ "Use Shrink to fit" ] ;
# endif
2020-04-22 20:52:17 +03:00
[ share sendActionOn : NSEventMaskLeftMouseDown ] ;
[ super windowDidLoad ] ;
}
- ( IBAction ) openLocation : ( id ) sender
{
[ [ self window ] makeFirstResponder : urlText ] ;
}
- ( NSString * ) addProtocolIfNecessary : ( NSString * ) address
{
if ( [ address rangeOfString : @ "://" ] . length > 0 )
return address ;
if ( [ address hasPrefix : @ "data:" ] )
return address ;
if ( [ address hasPrefix : @ "about:" ] )
return address ;
return [ @ "http://" stringByAppendingString : address ] ;
}
- ( IBAction ) share : ( id ) sender
{
NSSharingServicePicker * picker = [ [ NSSharingServicePicker alloc ] initWithItems : @ [ self . currentURL ] ] ;
picker . delegate = self ;
[ picker showRelativeToRect : NSZeroRect ofView : sender preferredEdge : NSRectEdgeMinY ] ;
}
- ( IBAction ) showHideWebView : ( id ) sender
{
self . mainContentView . hidden = ! self . mainContentView . isHidden ;
}
- ( CGFloat ) pageScaleForMenuItemTag : ( NSInteger ) tag
{
if ( tag = = 1 )
return 1 ;
if ( tag = = 2 )
return 1.25 ;
if ( tag = = 3 )
return 1.5 ;
if ( tag = = 4 )
return 2.0 ;
return 1 ;
}
- ( void ) awakeFromNib
{
_webView = [ [ WKWebView alloc ] initWithFrame : [ containerView bounds ] configuration : _configuration ] ;
2021-03-31 09:50:53 +03:00
_webView . _windowOcclusionDetectionEnabled = NO ;
2020-04-22 20:52:17 +03:00
_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 ] ;
2021-03-26 19:44:31 +03:00
[ _webView addObserver : self forKeyPath : @ "hasOnlySecureContent" options : 0 context : keyValueObservingContext ] ;
2020-04-22 20:52:17 +03:00
_webView . navigationDelegate = self ;
_webView . UIDelegate = self ;
_webView . _observedRenderingProgressEvents = _WKRenderingProgressEventFirstLayout
| _WKRenderingProgressEventFirstVisuallyNonEmptyLayout
| _WKRenderingProgressEventFirstPaintWithSignificantArea
| _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering
| _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering ;
_webView . customUserAgent = @ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/605.1.15" ;
_webView . _usePlatformFindUI = NO ;
_textFinder = [ [ PlaywrightNSTextFinder alloc ] init ] ;
_textFinder . incrementalSearchingEnabled = YES ;
_textFinder . incrementalSearchingShouldDimContentView = NO ;
_textFinder . client = _webView ;
_textFinder . findBarContainer = self ;
_textFinder . hideInterfaceCallback = ^ {
[ _webView _hideFindUI ] ;
} ;
_zoomTextOnly = NO ;
}
- ( instancetype ) initWithConfiguration : ( WKWebViewConfiguration * ) configuration
{
if ( ! ( self = [ super initWithWindowNibName : @ "BrowserWindow" ] ) )
return nil ;
_configuration = [ configuration copy ] ;
_isPrivateBrowsingWindow = ! _configuration . websiteDataStore . isPersistent ;
self . window . styleMask & = ~ NSWindowStyleMaskFullSizeContentView ;
[ self . window makeKeyAndOrderFront : nil ] ;
return self ;
}
- ( void ) dealloc
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
2021-03-26 19:44:31 +03:00
[ _webView removeObserver : self forKeyPath : @ "title" ] ;
[ _webView removeObserver : self forKeyPath : @ "URL" ] ;
[ _webView removeObserver : self forKeyPath : @ "hasOnlySecureContent" ] ;
2020-04-22 20:52:17 +03:00
[ progressIndicator unbind : NSHiddenBinding ] ;
[ progressIndicator unbind : NSValueBinding ] ;
[ _textFinder release ] ;
[ _webView release ] ;
[ _configuration release ] ;
[ super dealloc ] ;
}
- ( IBAction ) fetch : ( id ) sender
{
[ urlText setStringValue : [ self addProtocolIfNecessary : urlText . stringValue ] ] ;
NSURL * url = [ NSURL _webkit _URLWithUserTypedString : urlText . stringValue ] ;
[ _webView loadRequest : [ NSURLRequest requestWithURL : url ] ] ;
}
- ( IBAction ) setPageScale : ( id ) sender
{
CGFloat scale = [ self pageScaleForMenuItemTag : [ sender tag ] ] ;
[ _webView _setPageScale : scale withOrigin : CGPointZero ] ;
}
- ( CGFloat ) viewScaleForMenuItemTag : ( NSInteger ) tag
{
if ( tag = = 1 )
return 1 ;
if ( tag = = 2 )
return 0.75 ;
if ( tag = = 3 )
return 0.5 ;
if ( tag = = 4 )
return 0.25 ;
return 1 ;
}
- ( IBAction ) setViewScale : ( id ) sender
{
CGFloat scale = [ self viewScaleForMenuItemTag : [ sender tag ] ] ;
CGFloat oldScale = [ _webView _viewScale ] ;
if ( scale = = oldScale )
return ;
[ _webView _setLayoutMode : _WKLayoutModeDynamicSizeComputedFromViewScale ] ;
NSRect oldFrame = self . window . frame ;
NSSize newFrameSize = NSMakeSize ( oldFrame . size . width * ( scale / oldScale ) , oldFrame . size . height * ( scale / oldScale ) ) ;
[ self . window setFrame : NSMakeRect ( oldFrame . origin . x , oldFrame . origin . y - ( newFrameSize . height - oldFrame . size . height ) , newFrameSize . width , newFrameSize . height ) display : NO animate : NO ] ;
[ _webView _setViewScale : scale ] ;
}
static BOOL areEssentiallyEqual ( double a , double b )
{
double tolerance = 0.001 ;
return ( fabs ( a - b ) <= tolerance ) ;
}
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-implementations"
- ( BOOL ) validateMenuItem : ( NSMenuItem * ) menuItem
# pragma GCC diagnostic pop
{
SEL action = menuItem . action ;
if ( action = = @ selector ( saveAsPDF : ) )
return YES ;
if ( action = = @ selector ( saveAsWebArchive : ) )
return YES ;
if ( action = = @ selector ( zoomIn : ) )
return [ self canZoomIn ] ;
if ( action = = @ selector ( zoomOut : ) )
return [ self canZoomOut ] ;
if ( action = = @ selector ( resetZoom : ) )
return [ self canResetZoom ] ;
2021-03-26 19:44:31 +03:00
if ( action = = @ selector ( toggleFullWindowWebView : ) )
[ menuItem setTitle : [ self webViewFillsWindow ] ? @ "Inset Web View" : @ "Fit Web View to Window" ] ;
else if ( action = = @ selector ( toggleZoomMode : ) )
2020-04-22 20:52:17 +03:00
[ menuItem setState : _zoomTextOnly ? NSControlStateValueOn : NSControlStateValueOff ] ;
else if ( action = = @ selector ( showHideWebInspector : ) )
[ menuItem setTitle : _webView . _inspector . isVisible ? @ "Close Web Inspector" : @ "Show Web Inspector" ] ;
if ( action = = @ selector ( setPageScale : ) )
[ menuItem setState : areEssentiallyEqual ( [ _webView _pageScale ] , [ self pageScaleForMenuItemTag : [ menuItem tag ] ] ) ] ;
if ( action = = @ selector ( setViewScale : ) )
[ menuItem setState : areEssentiallyEqual ( [ _webView _viewScale ] , [ self viewScaleForMenuItemTag : [ menuItem tag ] ] ) ] ;
return YES ;
}
- ( IBAction ) reload : ( id ) sender
{
[ _webView reload ] ;
}
2021-03-26 19:44:31 +03:00
- ( IBAction ) showCertificate : ( id ) sender
{
if ( _webView . serverTrust )
[ [ SFCertificateTrustPanel sharedCertificateTrustPanel ] beginSheetForWindow : self . window modalDelegate : nil didEndSelector : nil contextInfo : NULL trust : _webView . serverTrust message : @ "TLS Certificate Details" ] ;
}
2020-04-22 20:52:17 +03:00
- ( IBAction ) goBack : ( id ) sender
{
[ _webView goBack ] ;
}
- ( IBAction ) goForward : ( id ) sender
{
[ _webView goForward ] ;
}
2021-03-26 19:44:31 +03:00
- ( IBAction ) toggleFullWindowWebView : ( id ) sender
{
BOOL newFillWindow = ! [ self webViewFillsWindow ] ;
[ self setWebViewFillsWindow : newFillWindow ] ;
}
- ( BOOL ) webViewFillsWindow
{
return NSEqualRects ( containerView . bounds , self . mainContentView . frame ) ;
}
- ( void ) setWebViewFillsWindow : ( BOOL ) fillWindow
{
if ( fillWindow )
[ self . mainContentView setFrame : containerView . bounds ] ;
else {
const CGFloat viewInset = 100.0 f ;
NSRect viewRect = NSInsetRect ( containerView . bounds , viewInset , viewInset ) ;
// Make it not vertically centered , to reveal y - flipping bugs .
viewRect = NSOffsetRect ( viewRect , 0 , -25 ) ;
[ self . mainContentView setFrame : viewRect ] ;
}
}
2020-04-22 20:52:17 +03:00
- ( IBAction ) toggleZoomMode : ( id ) sender
{
if ( _zoomTextOnly ) {
_zoomTextOnly = NO ;
double currentTextZoom = _webView . _textZoomFactor ;
_webView . _textZoomFactor = 1 ;
_webView . pageZoom = currentTextZoom ;
} else {
_zoomTextOnly = YES ;
double currentPageZoom = _webView . _pageZoomFactor ;
_webView . _textZoomFactor = currentPageZoom ;
_webView . pageZoom = 1 ;
}
}
- ( IBAction ) resetZoom : ( id ) sender
{
if ( ! [ self canResetZoom ] )
return ;
if ( _zoomTextOnly )
_webView . _textZoomFactor = 1 ;
else
_webView . pageZoom = 1 ;
}
- ( BOOL ) canResetZoom
{
return _zoomTextOnly ? ( _webView . _textZoomFactor ! = 1 ) : ( _webView . pageZoom ! = 1 ) ;
}
2021-03-26 19:44:31 +03:00
- ( IBAction ) toggleShrinkToFit : ( id ) sender
{
_useShrinkToFit = ! _useShrinkToFit ;
toggleUseShrinkToFitButton . image = _useShrinkToFit ? [ NSImage imageNamed : @ "NSExitFullScreenTemplate" ] : [ NSImage imageNamed : @ "NSEnterFullScreenTemplate" ] ;
[ _webView _setLayoutMode : _useShrinkToFit ? _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize : _WKLayoutModeViewSize ] ;
}
2020-04-22 20:52:17 +03:00
- ( IBAction ) showHideWebInspector : ( id ) sender
{
_WKInspector * inspector = _webView . _inspector ;
if ( inspector . isVisible )
[ inspector hide ] ;
else
[ inspector show ] ;
}
- ( NSURL * ) currentURL
{
return _webView . URL ;
}
- ( NSView * ) mainContentView
{
return _webView ;
}
- ( 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
{
[ _webView removeFromSuperview ] ;
_textFinder . hideInterfaceCallback = nil ;
[ self release ] ;
2020-04-23 07:49:00 +03:00
// Post two events ( don ' t ask me why ! ) to spin event loop and drain
// automatically created autorelease pools that will release our window .
// See https : // www . mikeash . com / pyblog / more - fun - with - autorelease . html
// for some discussion .
NSEvent * event1 = [ NSEvent
otherEventWithType : NSEventTypeApplicationDefined
location : NSMakePoint ( 0 , 0 )
modifierFlags : 0
timestamp : [ [ NSDate date ] timeIntervalSince1970 ]
windowNumber : 0
context : nil
subtype : 0
data1 : 0
data2 : 0 ] ;
[ NSApp postEvent : event1 atStart : YES ] ;
NSEvent * event2 = [ NSEvent
otherEventWithType : NSEventTypeApplicationDefined
location : NSMakePoint ( 0 , 0 )
modifierFlags : 0
timestamp : [ [ NSDate date ] timeIntervalSince1970 ]
windowNumber : 0
context : nil
subtype : 0
data1 : 0
data2 : 0 ] ;
[ NSApp postEvent : event2 atStart : NO ] ;
2020-04-22 20:52:17 +03:00
}
- ( void ) webViewDidClose : ( WKWebView * ) webView {
2020-11-04 05:58:57 +03:00
[ self webView : webView handleJavaScriptDialog : false value : nil ] ;
2020-04-22 20:52:17 +03:00
[ self . window close ] ;
}
2020-07-24 02:16:31 +03:00
- ( void ) _webView : ( WKWebView * ) webView getWindowFrameWithCompletionHandler : ( void ( ^ ) ( CGRect ) ) completionHandler
{
completionHandler ( [ self . window frame ] ) ;
}
2020-04-22 20:52:17 +03:00
# define DefaultMinimumZoomFactor ( .5 )
# define DefaultMaximumZoomFactor ( 3.0 )
# define DefaultZoomFactorRatio ( 1.2 )
- ( CGFloat ) currentZoomFactor
{
return _zoomTextOnly ? _webView . _textZoomFactor : _webView . pageZoom ;
}
- ( void ) setCurrentZoomFactor : ( CGFloat ) factor
{
if ( _zoomTextOnly )
_webView . _textZoomFactor = factor ;
else
_webView . pageZoom = 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 ) updateTitle : ( NSString * ) title
{
if ( ! title ) {
NSURL * url = _webView . URL ;
title = url . lastPathComponent ? : url . _web _userVisibleString ;
}
2021-06-25 09:26:48 +03:00
self . window . title = [ NSString stringWithFormat : @ "🎭 Playwright: %@" , title ] ;
2020-04-22 20:52:17 +03:00
}
- ( void ) observeValueForKeyPath : ( NSString * ) keyPath ofObject : ( id ) object change : ( NSDictionary * ) change context : ( void * ) context
{
if ( context ! = keyValueObservingContext || object ! = _webView )
return ;
if ( [ keyPath isEqualToString : @ "title" ] )
[ self updateTitle : _webView . title ] ;
else if ( [ keyPath isEqualToString : @ "URL" ] )
[ self updateTextFieldFromURL : _webView . URL ] ;
2021-03-26 19:44:31 +03:00
else if ( [ keyPath isEqualToString : @ "hasOnlySecureContent" ] )
[ self updateLockButtonIcon : _webView . hasOnlySecureContent ] ;
2020-04-22 20:52:17 +03:00
}
- ( nullable WKWebView * ) webView : ( WKWebView * ) webView createWebViewWithConfiguration : ( WKWebViewConfiguration * ) configuration forNavigationAction : ( WKNavigationAction * ) navigationAction windowFeatures : ( WKWindowFeatures * ) windowFeatures
{
// WebView lifecycle will control the BrowserWindowController life times .
BrowserWindowController * controller = [ [ BrowserWindowController alloc ] initWithConfiguration : configuration ] ;
return controller -> _webView ;
}
- ( void ) webView : ( WKWebView * ) webView runJavaScriptAlertPanelWithMessage : ( NSString * ) message initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( 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 = alert ;
[ alert beginSheetModalForWindow : self . window completionHandler : ^ void ( NSModalResponse response ) {
completionHandler ( ) ;
[ alert release ] ;
_alert = nil ;
} ] ;
}
- ( 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 = alert ;
[ alert beginSheetModalForWindow : self . window completionHandler : ^ void ( NSModalResponse response ) {
completionHandler ( response = = NSAlertFirstButtonReturn ) ;
[ alert release ] ;
_alert = nil ;
} ] ;
}
- ( 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 = alert ;
[ alert beginSheetModalForWindow : self . window completionHandler : ^ void ( NSModalResponse response ) {
[ input validateEditing ] ;
completionHandler ( response = = NSAlertFirstButtonReturn ? [ input stringValue ] : nil ) ;
[ alert release ] ;
_alert = nil ;
} ] ;
}
- ( void ) webView : ( WKWebView * ) webView handleJavaScriptDialog : ( BOOL ) accept value : ( NSString * ) value
{
if ( ! _alert )
return ;
NSTextField * input = ( NSTextField * ) _alert . accessoryView ;
if ( accept && input && value )
[ input setStringValue : value ] ;
[ self . window endSheet : _alert . window returnCode : accept ? NSAlertFirstButtonReturn : NSModalResponseCancel ] ;
}
# if __has _feature ( objc_generics )
- ( void ) webView : ( WKWebView * ) webView runOpenPanelWithParameters : ( WKOpenPanelParameters * ) parameters initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( NSArray < NSURL * > * URLs ) ) completionHandler
# else
- ( void ) webView : ( WKWebView * ) webView runOpenPanelWithParameters : ( WKOpenPanelParameters * ) parameters initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( NSArray * URLs ) ) completionHandler
# endif
{
NSOpenPanel * openPanel = [ NSOpenPanel openPanel ] ;
openPanel . allowsMultipleSelection = parameters . allowsMultipleSelection ;
[ openPanel beginSheetModalForWindow : webView . window completionHandler : ^ ( NSInteger result ) {
if ( result = = NSModalResponseOK )
completionHandler ( openPanel . URLs ) ;
else
completionHandler ( nil ) ;
} ] ;
}
- ( void ) _webView : ( WebView * ) sender runBeforeUnloadConfirmPanelWithMessage : ( NSString * ) message initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( BOOL result ) ) completionHandler
{
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
alert . messageText = [ NSString stringWithFormat : @ "JavaScript before unload dialog from %@." , [ frame . request . URL absoluteString ] ] ;
alert . informativeText = message ;
[ alert addButtonWithTitle : @ "Leave Page" ] ;
[ alert addButtonWithTitle : @ "Stay On Page" ] ;
_alert = alert ;
[ alert beginSheetModalForWindow : self . window completionHandler : ^ void ( NSModalResponse response ) {
completionHandler ( response = = NSAlertFirstButtonReturn ) ;
[ alert release ] ;
_alert = nil ;
} ] ;
}
- ( WKDragDestinationAction ) _webView : ( WKWebView * ) webView dragDestinationActionMaskForDraggingInfo : ( id ) draggingInfo
{
return WKDragDestinationActionAny ;
}
- ( void ) updateTextFieldFromURL : ( NSURL * ) URL
{
if ( ! URL )
return ;
if ( ! URL . absoluteString . length )
return ;
urlText . stringValue = [ URL _web _userVisibleString ] ;
}
2021-03-26 19:44:31 +03:00
- ( void ) updateLockButtonIcon : ( BOOL ) hasOnlySecureContent
{
if ( hasOnlySecureContent )
[ lockButton setImage : [ NSImage imageNamed : NSImageNameLockLockedTemplate ] ] ;
else
[ lockButton setImage : [ NSImage imageNamed : NSImageNameLockUnlockedTemplate ] ] ;
}
2020-04-22 20:52:17 +03:00
- ( void ) loadURLString : ( NSString * ) urlString
{
// FIXME : We shouldn ' t have to set the url text here .
[ urlText setStringValue : urlString ] ;
[ self fetch : nil ] ;
}
- ( void ) loadHTMLString : ( NSString * ) HTMLString
{
[ _webView loadHTMLString : HTMLString baseURL : nil ] ;
}
static NSSet * dataTypes ( )
{
return [ WKWebsiteDataStore allWebsiteDataTypes ] ;
}
- ( IBAction ) fetchWebsiteData : ( id ) sender
{
[ _configuration . websiteDataStore _fetchDataRecordsOfTypes : dataTypes ( ) withOptions : _WKWebsiteDataStoreFetchOptionComputeSizes completionHandler : ^ ( NSArray * websiteDataRecords ) {
NSLog ( @ "did fetch website data %@." , websiteDataRecords ) ;
} ] ;
}
- ( IBAction ) fetchAndClearWebsiteData : ( id ) sender
{
[ _configuration . websiteDataStore fetchDataRecordsOfTypes : dataTypes ( ) completionHandler : ^ ( NSArray * websiteDataRecords ) {
[ _configuration . websiteDataStore removeDataOfTypes : dataTypes ( ) forDataRecords : websiteDataRecords completionHandler : ^ {
[ _configuration . websiteDataStore fetchDataRecordsOfTypes : dataTypes ( ) completionHandler : ^ ( NSArray * websiteDataRecords ) {
NSLog ( @ "did clear website data, after clearing data is %@." , websiteDataRecords ) ;
} ] ;
} ] ;
} ] ;
}
- ( IBAction ) clearWebsiteData : ( id ) sender
{
[ _configuration . websiteDataStore removeDataOfTypes : dataTypes ( ) modifiedSince : [ NSDate distantPast ] completionHandler : ^ {
NSLog ( @ "Did clear website data." ) ;
} ] ;
}
- ( IBAction ) printWebView : ( id ) sender
{
[ [ _webView printOperationWithPrintInfo : [ NSPrintInfo sharedPrintInfo ] ] runOperationModalForWindow : self . window delegate : nil didRunSelector : nil contextInfo : nil ] ;
}
# pragma mark WKNavigationDelegate
- ( void ) webView : ( WKWebView * ) webView decidePolicyForNavigationAction : ( WKNavigationAction * ) navigationAction decisionHandler : ( void ( ^ ) ( WKNavigationActionPolicy ) ) decisionHandler
{
LOG ( @ "decidePolicyForNavigationAction" ) ;
2021-02-16 21:22:19 +03:00
if ( navigationAction . shouldPerformDownload ) {
decisionHandler ( WKNavigationActionPolicyDownload ) ;
return ;
}
2020-04-22 20:52:17 +03:00
if ( navigationAction . _canHandleRequest ) {
decisionHandler ( WKNavigationActionPolicyAllow ) ;
return ;
}
decisionHandler ( WKNavigationActionPolicyCancel ) ;
}
- ( void ) webView : ( WKWebView * ) webView decidePolicyForNavigationResponse : ( WKNavigationResponse * ) navigationResponse decisionHandler : ( void ( ^ ) ( WKNavigationResponsePolicy ) ) decisionHandler
{
if ( ! [ navigationResponse . response isKindOfClass : [ NSHTTPURLResponse class ] ] ) {
decisionHandler ( WKNavigationResponsePolicyAllow ) ;
return ;
}
NSHTTPURLResponse * httpResponse = ( NSHTTPURLResponse * ) navigationResponse . response ;
NSString * disposition = [ [ httpResponse allHeaderFields ] objectForKey : @ "Content-Disposition" ] ;
if ( disposition && [ disposition hasPrefix : @ "attachment" ] ) {
2020-12-16 10:07:34 +03:00
decisionHandler ( WKNavigationResponsePolicyDownload ) ;
2020-04-22 20:52:17 +03:00
return ;
}
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 ) ;
[ self updateTitle : nil ] ;
}
- ( void ) webView : ( WKWebView * ) webView didFinishNavigation : ( WKNavigation * ) navigation
{
LOG ( @ "didFinishNavigation: %@" , navigation ) ;
}
- ( void ) webView : ( WKWebView * ) webView didReceiveAuthenticationChallenge : ( NSURLAuthenticationChallenge * ) challenge completionHandler : ( void ( ^ ) ( NSURLSessionAuthChallengeDisposition disposition , NSURLCredential * __nullable credential ) ) completionHandler
{
LOG ( @ "didReceiveAuthenticationChallenge: %@" , challenge ) ;
if ( [ challenge . protectionSpace . authenticationMethod isEqualToString : NSURLAuthenticationMethodHTTPBasic ] ) {
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
NSView * container = [ [ [ NSView alloc ] initWithFrame : NSMakeRect ( 0 , 0 , 200 , 48 ) ] autorelease ] ;
NSTextField * userInput = [ [ [ NSTextField alloc ] initWithFrame : NSMakeRect ( 0 , 24 , 200 , 24 ) ] autorelease ] ;
NSTextField * passwordInput = [ [ [ NSSecureTextField alloc ] initWithFrame : NSMakeRect ( 0 , 0 , 200 , 24 ) ] autorelease ] ;
[ alert setMessageText : [ NSString stringWithFormat : @ "Log in to %@:%lu." , challenge . protectionSpace . host , challenge . protectionSpace . port ] ] ;
[ alert addButtonWithTitle : @ "Log in" ] ;
[ alert addButtonWithTitle : @ "Cancel" ] ;
[ container addSubview : userInput ] ;
[ container addSubview : passwordInput ] ;
[ alert setAccessoryView : container ] ;
[ userInput setNextKeyView : passwordInput ] ;
[ alert . window setInitialFirstResponder : userInput ] ;
[ alert beginSheetModalForWindow : self . window completionHandler : ^ ( NSModalResponse response ) {
[ userInput validateEditing ] ;
if ( response = = NSAlertFirstButtonReturn )
completionHandler ( NSURLSessionAuthChallengeUseCredential , [ [ [ NSURLCredential alloc ] initWithUser : [ userInput stringValue ] password : [ passwordInput stringValue ] persistence : NSURLCredentialPersistenceForSession ] autorelease ] ) ;
else
completionHandler ( NSURLSessionAuthChallengeRejectProtectionSpace , nil ) ;
[ alert release ] ;
} ] ;
return ;
}
completionHandler ( NSURLSessionAuthChallengeRejectProtectionSpace , nil ) ;
}
- ( void ) webView : ( WKWebView * ) webView didFailNavigation : ( WKNavigation * ) navigation withError : ( NSError * ) error
{
LOG ( @ "didFailNavigation: %@, error %@" , navigation , error ) ;
}
- ( void ) webViewWebContentProcessDidTerminate : ( WKWebView * ) webView
{
NSLog ( @ "WebContent process crashed; reloading" ) ;
[ self reload : nil ] ;
}
- ( void ) _webView : ( WKWebView * ) webView renderingProgressDidChange : ( _WKRenderingProgressEvents ) progressEvents
{
if ( progressEvents & _WKRenderingProgressEventFirstLayout )
LOG ( @ "renderingProgressDidChange: %@" , @ "first layout" ) ;
if ( progressEvents & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout )
LOG ( @ "renderingProgressDidChange: %@" , @ "first visually non-empty layout" ) ;
if ( progressEvents & _WKRenderingProgressEventFirstPaintWithSignificantArea )
LOG ( @ "renderingProgressDidChange: %@" , @ "first paint with significant area" ) ;
if ( progressEvents & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering )
LOG ( @ "renderingProgressDidChange: %@" , @ "first layout after suppressed incremental rendering" ) ;
if ( progressEvents & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering )
LOG ( @ "renderingProgressDidChange: %@" , @ "first paint after suppressed incremental rendering" ) ;
}
- ( void ) webView : ( WKWebView * ) webView shouldLoadIconWithParameters : ( _WKLinkIconParameters * ) parameters completionHandler : ( void ( ^ ) ( void ( ^ ) ( NSData * ) ) ) completionHandler
{
completionHandler ( ^ void ( NSData * data ) {
LOG ( @ "Icon URL %@ received icon data of length %u" , parameters . url , ( unsigned ) data . length ) ;
} ) ;
}
# pragma mark Find in Page
- ( IBAction ) performTextFinderAction : ( id ) sender
{
[ _textFinder performAction : [ sender tag ] ] ;
}
- ( NSView * ) findBarView
{
return _textFindBarView ;
}
- ( void ) setFindBarView : ( NSView * ) findBarView
{
_textFindBarView = findBarView ;
_findBarVisible = YES ;
[ _textFindBarView setFrame : NSMakeRect ( 0 , 0 , containerView . bounds . size . width , _textFindBarView . frame . size . height ) ] ;
}
- ( BOOL ) isFindBarVisible
{
return _findBarVisible ;
}
- ( void ) setFindBarVisible : ( BOOL ) findBarVisible
{
_findBarVisible = findBarVisible ;
if ( findBarVisible )
[ containerView addSubview : _textFindBarView ] ;
else
[ _textFindBarView removeFromSuperview ] ;
}
- ( NSView * ) contentView
{
return _webView ;
}
- ( void ) findBarViewDidChangeHeight
{
}
- ( void ) _webView : ( WKWebView * ) webView requestMediaCaptureAuthorization : ( _WKCaptureDevices ) devices decisionHandler : ( void ( ^ ) ( BOOL authorized ) ) decisionHandler
{
decisionHandler ( true ) ;
}
- ( void ) _webView : ( WKWebView * ) webView includeSensitiveMediaDeviceDetails : ( void ( ^ ) ( BOOL includeSensitiveDetails ) ) decisionHandler
{
decisionHandler ( false ) ;
}
- ( IBAction ) saveAsPDF : ( id ) sender
{
NSSavePanel * panel = [ NSSavePanel savePanel ] ;
panel . allowedFileTypes = @ [ @ "pdf" ] ;
[ panel beginSheetModalForWindow : self . window completionHandler : ^ ( NSInteger result ) {
if ( result = = NSModalResponseOK ) {
[ _webView createPDFWithConfiguration : nil completionHandler : ^ ( NSData * pdfSnapshotData , NSError * error ) {
[ pdfSnapshotData writeToURL : [ panel URL ] options : 0 error : nil ] ;
} ] ;
}
} ] ;
}
- ( IBAction ) saveAsWebArchive : ( id ) sender
{
NSSavePanel * panel = [ NSSavePanel savePanel ] ;
panel . allowedFileTypes = @ [ @ "webarchive" ] ;
[ panel beginSheetModalForWindow : self . window completionHandler : ^ ( NSInteger result ) {
if ( result = = NSModalResponseOK ) {
[ _webView createWebArchiveDataWithCompletionHandler : ^ ( NSData * archiveData , NSError * error ) {
[ archiveData writeToURL : [ panel URL ] options : 0 error : nil ] ;
} ] ;
}
} ] ;
}
- ( WKWebView * ) webView
{
return _webView ;
}
@ end