2018-09-08 04:11:49 +00:00
/ * *
* Copyright ( c ) 2015 - present , Facebook , Inc .
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
* /
2019-08-30 10:04:32 +00:00
# import "RNCWebView.h"
2018-09-08 04:11:49 +00:00
# import < React / RCTConvert . h >
# import < React / RCTAutoInsetsProtocol . h >
2019-01-07 14:19:32 +00:00
# import "RNCWKProcessPoolManager.h"
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-12-14 10:18:48 +00:00
# import < UIKit / UIKit . h >
2020-02-12 23:39:11 +00:00
# else
# import < React / RCTUIKit . h >
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
2018-10-17 14:59:19 +00:00
# import "objc/runtime.h"
2019-02-06 14:29:59 +00:00
static NSTimer * keyboardTimer ;
2019-12-16 16:52:54 +00:00
static NSString * const HistoryShimName = @ "ReactNativeHistoryShim" ;
2019-02-01 21:04:11 +00:00
static NSString * const MessageHandlerName = @ "ReactNativeWebView" ;
2019-02-12 09:35:14 +00:00
static NSURLCredential * clientAuthenticationCredential ;
2019-09-21 00:44:53 +00:00
static NSDictionary * customCertificatesForHost ;
2018-09-08 04:11:49 +00:00
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-10-17 14:59:19 +00:00
// runtime trick to remove WKWebView keyboard default toolbar
// see : http : // stackoverflow . com / questions / 19033292 / ios -7 - uiwebview - keyboard - issue / 19042279 #19042279
2019-09-26 23:27:42 +00:00
@ interface _SwizzleHelperWK : UIView
@ property ( nonatomic , copy ) WKWebView * webView ;
@ end
2018-10-17 14:59:19 +00:00
@ implementation _SwizzleHelperWK
- ( id ) inputAccessoryView
{
2019-09-26 23:27:42 +00:00
if ( _webView = = nil ) {
return nil ;
}
if ( [ _webView respondsToSelector : @ selector ( inputAssistantItem ) ] ) {
UITextInputAssistantItem * inputAssistantItem = [ _webView inputAssistantItem ] ;
inputAssistantItem . leadingBarButtonGroups = @ [ ] ;
inputAssistantItem . trailingBarButtonGroups = @ [ ] ;
}
return nil ;
2018-10-17 14:59:19 +00:00
}
@ end
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-10-17 14:59:19 +00:00
2020-02-12 23:39:11 +00:00
# if TARGET_OS _OSX
@ interface RNCWKWebView : WKWebView
@ end
@ implementation RNCWKWebView
- ( void ) scrollWheel : ( NSEvent * ) theEvent {
RNCWebView * rncWebView = ( RNCWebView * ) [ self superview ] ;
RCTAssert ( [ rncWebView isKindOfClass : [ rncWebView class ] ] , @ "superview must be an RNCWebView" ) ;
if ( ! [ rncWebView scrollEnabled ] ) {
[ [ self nextResponder ] scrollWheel : theEvent ] ;
return ;
}
[ super scrollWheel : theEvent ] ;
}
@ end
# endif // TARGET_OS _OSX
@ interface RNCWebView ( ) < WKUIDelegate , WKNavigationDelegate , WKScriptMessageHandler ,
# if ! TARGET_OS _OSX
UIScrollViewDelegate ,
# endif // ! TARGET_OS _OSX
RCTAutoInsetsProtocol >
2020-04-29 16:09:22 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onFileDownload ;
2018-09-08 04:11:49 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onLoadingStart ;
@ property ( nonatomic , copy ) RCTDirectEventBlock onLoadingFinish ;
@ property ( nonatomic , copy ) RCTDirectEventBlock onLoadingError ;
2018-10-17 13:08:52 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onLoadingProgress ;
2018-09-08 04:11:49 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onShouldStartLoadWithRequest ;
2019-09-21 00:43:28 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onHttpError ;
2018-09-08 04:11:49 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onMessage ;
2019-05-16 22:27:16 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onScroll ;
2019-09-23 08:20:12 +00:00
@ property ( nonatomic , copy ) RCTDirectEventBlock onContentProcessDidTerminate ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
@ property ( nonatomic , copy ) WKWebView * webView ;
2020-02-12 23:39:11 +00:00
# else
@ property ( nonatomic , copy ) RNCWKWebView * webView ;
# endif // ! TARGET_OS _OSX
2020-03-17 21:01:20 +00:00
@ property ( nonatomic , strong ) WKUserScript * postMessageScript ;
@ property ( nonatomic , strong ) WKUserScript * atStartScript ;
@ property ( nonatomic , strong ) WKUserScript * atEndScript ;
2018-09-08 04:11:49 +00:00
@ end
2019-08-30 10:04:32 +00:00
@ implementation RNCWebView
2018-09-08 04:11:49 +00:00
{
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
UIColor * _savedBackgroundColor ;
2020-02-12 23:39:11 +00:00
# else
RCTUIColor * _savedBackgroundColor ;
# endif // ! TARGET_OS _OSX
2018-10-17 14:59:19 +00:00
BOOL _savedHideKeyboardAccessoryView ;
2019-04-29 15:46:07 +00:00
BOOL _savedKeyboardDisplayRequiresUserAction ;
2019-08-30 10:04:32 +00:00
2019-04-30 08:08:41 +00:00
// Workaround for StatusBar appearance bug for iOS 12
// https : // github . com / react - native - community / react - native - webview / issues / 62
BOOL _isFullScreenVideoOpen ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2019-04-30 08:08:41 +00:00
UIStatusBarStyle _savedStatusBarStyle ;
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2019-04-30 08:08:41 +00:00
BOOL _savedStatusBarHidden ;
2019-08-09 08:55:35 +00:00
# if defined ( __IPHONE _OS _VERSION _MAX _ALLOWED ) && __IPHONE _OS _VERSION _MAX _ALLOWED >= 110000 / * __IPHONE _11 _0 * /
UIScrollViewContentInsetAdjustmentBehavior _savedContentInsetAdjustmentBehavior ;
# endif
2018-09-08 04:11:49 +00:00
}
- ( instancetype ) initWithFrame : ( CGRect ) frame
{
if ( ( self = [ super initWithFrame : frame ] ) ) {
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
super . backgroundColor = [ UIColor clearColor ] ;
2020-02-12 23:39:11 +00:00
# else
super . backgroundColor = [ RCTUIColor clearColor ] ;
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
_bounces = YES ;
_scrollEnabled = YES ;
2019-02-12 13:47:24 +00:00
_showsHorizontalScrollIndicator = YES ;
_showsVerticalScrollIndicator = YES ;
2019-03-07 09:27:29 +00:00
_directionalLockEnabled = YES ;
2018-09-08 04:11:49 +00:00
_automaticallyAdjustContentInsets = YES ;
2020-09-15 00:42:09 +00:00
_autoManageStatusBarEnabled = YES ;
2018-09-08 04:11:49 +00:00
_contentInset = UIEdgeInsetsZero ;
2019-04-29 15:46:07 +00:00
_savedKeyboardDisplayRequiresUserAction = YES ;
2020-03-17 21:01:20 +00:00
# if ! TARGET_OS _OSX
2019-04-30 08:08:41 +00:00
_savedStatusBarStyle = RCTSharedApplication ( ) . statusBarStyle ;
_savedStatusBarHidden = RCTSharedApplication ( ) . statusBarHidden ;
2020-03-17 21:01:20 +00:00
# endif // ! TARGET_OS _OSX
_injectedJavaScript = nil ;
_injectedJavaScriptForMainFrameOnly = YES ;
_injectedJavaScriptBeforeContentLoaded = nil ;
_injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES ;
2019-08-09 08:55:35 +00:00
# if defined ( __IPHONE _OS _VERSION _MAX _ALLOWED ) && __IPHONE _OS _VERSION _MAX _ALLOWED >= 110000 / * __IPHONE _11 _0 * /
_savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever ;
# endif
2018-09-08 04:11:49 +00:00
}
2019-02-06 14:29:59 +00:00
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2020-04-13 15:54:47 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( appDidBecomeActive )
name : UIApplicationDidBecomeActiveNotification
object : nil ] ;
2020-05-26 21:53:08 +00:00
2020-04-13 15:54:47 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( appWillResignActive )
name : UIApplicationWillResignActiveNotification
object : nil ] ;
2019-02-06 14:29:59 +00:00
if ( @ available ( iOS 12.0 , * ) ) {
2019-04-30 08:08:41 +00:00
// Workaround for a keyboard dismissal bug present in iOS 12
// https : // openradar . appspot . com / radar ? id = 5018321736957952
2019-02-06 14:29:59 +00:00
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @ selector ( keyboardWillHide )
name : UIKeyboardWillHideNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ]
addObserver : self
selector : @ selector ( keyboardWillShow )
name : UIKeyboardWillShowNotification object : nil ] ;
2019-08-30 10:04:32 +00:00
2019-04-30 08:08:41 +00:00
// Workaround for StatusBar appearance bug for iOS 12
// https : // github . com / react - native - community / react - native - webview / issues / 62
2019-09-26 23:22:07 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( showFullScreenVideoStatusBars )
name : UIWindowDidBecomeVisibleNotification
object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( hideFullScreenVideoStatusBars )
name : UIWindowDidBecomeHiddenNotification
object : nil ] ;
2020-05-26 21:53:08 +00:00
2019-02-06 14:29:59 +00:00
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
return self ;
}
2019-02-14 14:18:15 +00:00
- ( void ) dealloc
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
}
2019-02-01 01:18:02 +00:00
/ * *
* See https : // stackoverflow . com / questions / 25713069 / why - is - wkwebview - not - opening - links - with - target - blank / 25853806 #25853806 for details .
* /
- ( WKWebView * ) webView : ( WKWebView * ) webView createWebViewWithConfiguration : ( WKWebViewConfiguration * ) configuration forNavigationAction : ( WKNavigationAction * ) navigationAction windowFeatures : ( WKWindowFeatures * ) windowFeatures
{
if ( ! navigationAction . targetFrame . isMainFrame ) {
[ webView loadRequest : navigationAction . request ] ;
}
return nil ;
}
2020-01-08 06:27:53 +00:00
- ( WKWebViewConfiguration * ) setUpWkWebViewConfig
2018-09-08 04:11:49 +00:00
{
2020-01-08 06:27:53 +00:00
WKWebViewConfiguration * wkWebViewConfig = [ WKWebViewConfiguration new ] ;
WKPreferences * prefs = [ [ WKPreferences alloc ] init ] ;
BOOL _prefsUsed = NO ;
if ( ! _javaScriptEnabled ) {
prefs . javaScriptEnabled = NO ;
_prefsUsed = YES ;
}
if ( _allowFileAccessFromFileURLs ) {
[ prefs setValue : @ TRUE forKey : @ "allowFileAccessFromFileURLs" ] ;
_prefsUsed = YES ;
}
2020-05-28 16:54:20 +00:00
if ( _javaScriptCanOpenWindowsAutomatically ) {
[ prefs setValue : @ TRUE forKey : @ "javaScriptCanOpenWindowsAutomatically" ] ;
_prefsUsed = YES ;
}
2020-01-08 06:27:53 +00:00
if ( _prefsUsed ) {
wkWebViewConfig . preferences = prefs ;
}
if ( _incognito ) {
wkWebViewConfig . websiteDataStore = [ WKWebsiteDataStore nonPersistentDataStore ] ;
} else if ( _cacheEnabled ) {
wkWebViewConfig . websiteDataStore = [ WKWebsiteDataStore defaultDataStore ] ;
}
if ( self . useSharedProcessPool ) {
wkWebViewConfig . processPool = [ [ RNCWKProcessPoolManager sharedManager ] sharedProcessPool ] ;
}
wkWebViewConfig . userContentController = [ WKUserContentController new ] ;
2020-08-15 03:16:54 +00:00
# if defined ( __IPHONE _OS _VERSION _MAX _ALLOWED ) && __IPHONE _OS _VERSION _MAX _ALLOWED >= 130000 / * iOS 13 * /
if ( @ available ( iOS 13.0 , * ) ) {
WKWebpagePreferences * pagePrefs = [ [ WKWebpagePreferences alloc ] init ] ;
pagePrefs . preferredContentMode = _contentMode ;
wkWebViewConfig . defaultWebpagePreferences = pagePrefs ;
}
# endif
2020-01-08 06:27:53 +00:00
// Shim the HTML5 history API :
[ wkWebViewConfig . userContentController addScriptMessageHandler : [ [ RNCWeakScriptMessageDelegate alloc ] initWithDelegate : self ]
name : HistoryShimName ] ;
2020-03-17 21:01:20 +00:00
[ self resetupScripts : wkWebViewConfig ] ;
2019-02-01 17:37:28 +00:00
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2020-01-08 06:27:53 +00:00
wkWebViewConfig . allowsInlineMediaPlayback = _allowsInlineMediaPlayback ;
2018-09-08 04:11:49 +00:00
# if WEBKIT_IOS _10 _APIS _AVAILABLE
2020-01-08 06:27:53 +00:00
wkWebViewConfig . mediaTypesRequiringUserActionForPlayback = _mediaPlaybackRequiresUserAction
? WKAudiovisualMediaTypeAll
: WKAudiovisualMediaTypeNone ;
wkWebViewConfig . dataDetectorTypes = _dataDetectorTypes ;
2018-11-19 10:17:57 +00:00
# else
2020-01-08 06:27:53 +00:00
wkWebViewConfig . mediaPlaybackRequiresUserAction = _mediaPlaybackRequiresUserAction ;
2018-09-08 04:11:49 +00:00
# endif
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
2020-01-08 06:27:53 +00:00
if ( _applicationNameForUserAgent ) {
wkWebViewConfig . applicationNameForUserAgent = [ NSString stringWithFormat : @ "%@ %@" , wkWebViewConfig . applicationNameForUserAgent , _applicationNameForUserAgent ] ;
}
2020-05-26 21:53:08 +00:00
2020-01-08 06:27:53 +00:00
return wkWebViewConfig ;
}
2019-04-15 08:19:10 +00:00
2020-01-08 06:27:53 +00:00
- ( void ) didMoveToWindow
{
if ( self . window ! = nil && _webView = = nil ) {
WKWebViewConfiguration * wkWebViewConfig = [ self setUpWkWebViewConfig ] ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
_webView = [ [ WKWebView alloc ] initWithFrame : self . bounds configuration : wkWebViewConfig ] ;
2020-02-12 23:39:11 +00:00
# else
_webView = [ [ RNCWKWebView alloc ] initWithFrame : self . bounds configuration : wkWebViewConfig ] ;
# endif // ! TARGET_OS _OSX
2019-09-21 00:39:06 +00:00
[ self setBackgroundColor : _savedBackgroundColor ] ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
_webView . scrollView . delegate = self ;
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
_webView . UIDelegate = self ;
_webView . navigationDelegate = self ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2020-08-17 10:07:36 +00:00
if ( _pullToRefreshEnabled ) {
[ self addPullToRefreshControl ] ;
}
2018-09-08 04:11:49 +00:00
_webView . scrollView . scrollEnabled = _scrollEnabled ;
2018-11-22 12:58:07 +00:00
_webView . scrollView . pagingEnabled = _pagingEnabled ;
2020-08-17 10:07:36 +00:00
// For UIRefreshControl to work correctly , the bounces should always be true
_webView . scrollView . bounces = _pullToRefreshEnabled || _bounces ;
2019-02-12 13:47:24 +00:00
_webView . scrollView . showsHorizontalScrollIndicator = _showsHorizontalScrollIndicator ;
_webView . scrollView . showsVerticalScrollIndicator = _showsVerticalScrollIndicator ;
2019-03-07 09:27:29 +00:00
_webView . scrollView . directionalLockEnabled = _directionalLockEnabled ;
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-11-25 10:17:28 +00:00
_webView . allowsLinkPreview = _allowsLinkPreview ;
2018-10-17 13:08:52 +00:00
[ _webView addObserver : self forKeyPath : @ "estimatedProgress" options : NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context : nil ] ;
2018-10-21 21:12:59 +00:00
_webView . allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures ;
2018-11-25 10:17:28 +00:00
2018-11-19 10:13:31 +00:00
if ( _userAgent ) {
_webView . customUserAgent = _userAgent ;
}
2018-09-08 04:11:49 +00:00
# if defined ( __IPHONE _OS _VERSION _MAX _ALLOWED ) && __IPHONE _OS _VERSION _MAX _ALLOWED >= 110000 / * __IPHONE _11 _0 * /
if ( [ _webView . scrollView respondsToSelector : @ selector ( setContentInsetAdjustmentBehavior : ) ] ) {
2019-08-09 08:55:35 +00:00
_webView . scrollView . contentInsetAdjustmentBehavior = _savedContentInsetAdjustmentBehavior ;
2018-09-08 04:11:49 +00:00
}
# endif
[ self addSubview : _webView ] ;
2018-10-17 14:59:19 +00:00
[ self setHideKeyboardAccessoryView : _savedHideKeyboardAccessoryView ] ;
2019-04-29 15:46:07 +00:00
[ self setKeyboardDisplayRequiresUserAction : _savedKeyboardDisplayRequiresUserAction ] ;
2018-09-08 04:11:49 +00:00
[ self visitSource ] ;
}
}
2019-01-11 18:53:55 +00:00
// Update webview property when the component prop changes .
- ( void ) setAllowsBackForwardNavigationGestures : ( BOOL ) allowsBackForwardNavigationGestures {
_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures ;
_webView . allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures ;
}
2018-11-20 00:48:26 +00:00
- ( void ) removeFromSuperview
{
if ( _webView ) {
2019-02-01 17:37:28 +00:00
[ _webView . configuration . userContentController removeScriptMessageHandlerForName : MessageHandlerName ] ;
2018-11-20 00:48:26 +00:00
[ _webView removeObserver : self forKeyPath : @ "estimatedProgress" ] ;
[ _webView removeFromSuperview ] ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2019-02-14 14:18:15 +00:00
_webView . scrollView . delegate = nil ;
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-11-20 00:48:26 +00:00
_webView = nil ;
2020-05-26 21:53:08 +00:00
if ( _onContentProcessDidTerminate ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
_onContentProcessDidTerminate ( event ) ;
}
2018-11-20 00:48:26 +00:00
}
2018-11-22 12:58:07 +00:00
2018-11-20 00:48:26 +00:00
[ super removeFromSuperview ] ;
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2019-09-26 23:22:07 +00:00
- ( void ) showFullScreenVideoStatusBars
2019-04-30 08:08:41 +00:00
{
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
2020-09-15 00:42:09 +00:00
if ( ! _autoManageStatusBarEnabled ) {
return ;
}
2019-04-30 08:08:41 +00:00
_isFullScreenVideoOpen = YES ;
RCTUnsafeExecuteOnMainQueueSync ( ^ {
2020-09-15 00:42:09 +00:00
[ RCTSharedApplication ( ) setStatusBarStyle : self -> _savedStatusBarStyle animated : YES ] ;
2019-04-30 08:08:41 +00:00
} ) ;
2019-09-26 23:22:07 +00:00
# pragma clang diagnostic pop
}
- ( void ) hideFullScreenVideoStatusBars
{
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
2020-09-15 00:42:09 +00:00
if ( ! _autoManageStatusBarEnabled ) {
return ;
}
2019-04-30 08:08:41 +00:00
_isFullScreenVideoOpen = NO ;
RCTUnsafeExecuteOnMainQueueSync ( ^ {
2019-09-23 08:49:06 +00:00
[ RCTSharedApplication ( ) setStatusBarHidden : self -> _savedStatusBarHidden animated : YES ] ;
[ RCTSharedApplication ( ) setStatusBarStyle : self -> _savedStatusBarStyle animated : YES ] ;
2019-04-30 08:08:41 +00:00
} ) ;
# pragma clang diagnostic pop
}
2019-02-06 14:29:59 +00:00
- ( void ) keyboardWillHide
{
keyboardTimer = [ NSTimer scheduledTimerWithTimeInterval : 0 target : self selector : @ selector ( keyboardDisplacementFix ) userInfo : nil repeats : false ] ;
[ [ NSRunLoop mainRunLoop ] addTimer : keyboardTimer forMode : NSRunLoopCommonModes ] ;
}
- ( void ) keyboardWillShow
{
if ( keyboardTimer ! = nil ) {
[ keyboardTimer invalidate ] ;
}
}
- ( void ) keyboardDisplacementFix
{
// Additional viewport checks to prevent unintentional scrolls
UIScrollView * scrollView = self . webView . scrollView ;
double maxContentOffset = scrollView . contentSize . height - scrollView . frame . size . height ;
if ( maxContentOffset < 0 ) {
maxContentOffset = 0 ;
}
if ( scrollView . contentOffset . y > maxContentOffset ) {
// https : // stackoverflow . com / a / 9637807 / 824966
[ UIView animateWithDuration : .25 animations : ^ {
scrollView . contentOffset = CGPointMake ( 0 , maxContentOffset ) ;
} ] ;
}
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2019-02-06 14:29:59 +00:00
2018-10-17 13:08:52 +00:00
- ( void ) observeValueForKeyPath : ( NSString * ) keyPath ofObject : ( id ) object change : ( NSDictionary < NSKeyValueChangeKey , id > * ) change context : ( void * ) context {
if ( [ keyPath isEqual : @ "estimatedProgress" ] && object = = self . webView ) {
if ( _onLoadingProgress ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
[ event addEntriesFromDictionary : @ { @ "progress" : [ NSNumber numberWithDouble : self . webView . estimatedProgress ] } ] ;
_onLoadingProgress ( event ) ;
}
} else {
[ super observeValueForKeyPath : keyPath ofObject : object change : change context : context ] ;
}
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
- ( void ) setBackgroundColor : ( UIColor * ) backgroundColor
2020-02-12 23:39:11 +00:00
# else
- ( void ) setBackgroundColor : ( RCTUIColor * ) backgroundColor
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
{
_savedBackgroundColor = backgroundColor ;
if ( _webView = = nil ) {
return ;
}
CGFloat alpha = CGColorGetAlpha ( backgroundColor . CGColor ) ;
2020-02-12 23:39:11 +00:00
BOOL opaque = ( alpha = = 1.0 ) ;
# if ! TARGET_OS _OSX
self . opaque = _webView . opaque = opaque ;
2018-09-08 04:11:49 +00:00
_webView . scrollView . backgroundColor = backgroundColor ;
_webView . backgroundColor = backgroundColor ;
2020-02-12 23:39:11 +00:00
# else
// https : // stackoverflow . com / questions / 40007753 / macos - wkwebview - background - transparency
NSOperatingSystemVersion version = { 10 , 12 , 0 } ;
if ( [ [ NSProcessInfo processInfo ] isOperatingSystemAtLeastVersion : version ] ) {
[ _webView setValue : @ ( opaque ) forKey : @ "drawsBackground" ] ;
} else {
[ _webView setValue : @ ( ! opaque ) forKey : @ "drawsTransparentBackground" ] ;
}
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
}
2019-08-09 08:55:35 +00:00
# if defined ( __IPHONE _OS _VERSION _MAX _ALLOWED ) && __IPHONE _OS _VERSION _MAX _ALLOWED >= 110000 / * __IPHONE _11 _0 * /
- ( void ) setContentInsetAdjustmentBehavior : ( UIScrollViewContentInsetAdjustmentBehavior ) behavior
{
_savedContentInsetAdjustmentBehavior = behavior ;
if ( _webView = = nil ) {
return ;
}
if ( [ _webView . scrollView respondsToSelector : @ selector ( setContentInsetAdjustmentBehavior : ) ] ) {
CGPoint contentOffset = _webView . scrollView . contentOffset ;
_webView . scrollView . contentInsetAdjustmentBehavior = behavior ;
_webView . scrollView . contentOffset = contentOffset ;
}
}
# endif
2018-09-08 04:11:49 +00:00
/ * *
* This method is called whenever JavaScript running within the web view calls :
2019-02-01 17:37:28 +00:00
* - window . webkit . messageHandlers [ MessageHandlerName ] . postMessage
2018-09-08 04:11:49 +00:00
* /
- ( void ) userContentController : ( WKUserContentController * ) userContentController
didReceiveScriptMessage : ( WKScriptMessage * ) message
{
2019-12-16 16:52:54 +00:00
if ( [ message . name isEqualToString : HistoryShimName ] ) {
if ( _onLoadingFinish ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
[ event addEntriesFromDictionary : @ { @ "navigationType" : message . body } ] ;
_onLoadingFinish ( event ) ;
}
} else if ( [ message . name isEqualToString : MessageHandlerName ] ) {
if ( _onMessage ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
[ event addEntriesFromDictionary : @ { @ "data" : message . body } ] ;
_onMessage ( event ) ;
}
2018-09-08 04:11:49 +00:00
}
}
- ( void ) setSource : ( NSDictionary * ) source
{
if ( ! [ _source isEqualToDictionary : source ] ) {
_source = [ source copy ] ;
if ( _webView ! = nil ) {
[ self visitSource ] ;
}
}
}
2019-08-29 14:53:09 +00:00
- ( void ) setAllowingReadAccessToURL : ( NSString * ) allowingReadAccessToURL
{
if ( ! [ _allowingReadAccessToURL isEqualToString : allowingReadAccessToURL ] ) {
_allowingReadAccessToURL = [ allowingReadAccessToURL copy ] ;
if ( _webView ! = nil ) {
[ self visitSource ] ;
}
}
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
- ( void ) setContentInset : ( UIEdgeInsets ) contentInset
{
_contentInset = contentInset ;
[ RCTView autoAdjustInsetsForView : self
withScrollView : _webView . scrollView
updateOffset : NO ] ;
}
- ( void ) refreshContentInset
{
[ RCTView autoAdjustInsetsForView : self
withScrollView : _webView . scrollView
updateOffset : YES ] ;
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
- ( void ) visitSource
{
2019-04-15 08:19:10 +00:00
// Check for a static html source first
NSString * html = [ RCTConvert NSString : _source [ @ "html" ] ] ;
if ( html ) {
NSURL * baseURL = [ RCTConvert NSURL : _source [ @ "baseUrl" ] ] ;
if ( ! baseURL ) {
baseURL = [ NSURL URLWithString : @ "about:blank" ] ;
}
[ _webView loadHTMLString : html baseURL : baseURL ] ;
return ;
2018-09-08 04:11:49 +00:00
}
2019-04-02 13:46:00 +00:00
2019-04-15 08:19:10 +00:00
NSURLRequest * request = [ self requestForSource : _source ] ;
// Because of the way React works , as pages redirect , we actually end up
// passing the redirect urls back here , so we ignore them if trying to load
// the same url . We ' ll expose a call to ' reload ' to allow a user to load
// the existing page .
if ( [ request . URL isEqual : _webView . URL ] ) {
return ;
}
if ( ! request . URL ) {
// Clear the webview
[ _webView loadHTMLString : @ "" baseURL : nil ] ;
return ;
}
if ( request . URL . host ) {
[ _webView loadRequest : request ] ;
}
else {
2019-09-05 13:13:33 +00:00
NSURL * readAccessUrl = _allowingReadAccessToURL ? [ RCTConvert NSURL : _allowingReadAccessToURL ] : request . URL ;
2019-08-29 14:53:09 +00:00
[ _webView loadFileURL : request . URL allowingReadAccessToURL : readAccessUrl ] ;
2019-04-15 08:19:10 +00:00
}
2018-09-08 04:11:49 +00:00
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2019-04-29 15:46:07 +00:00
- ( void ) setKeyboardDisplayRequiresUserAction : ( BOOL ) keyboardDisplayRequiresUserAction
{
if ( _webView = = nil ) {
_savedKeyboardDisplayRequiresUserAction = keyboardDisplayRequiresUserAction ;
return ;
}
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
if ( _savedKeyboardDisplayRequiresUserAction = = true ) {
return ;
}
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
UIView * subview ;
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
for ( UIView * view in _webView . scrollView . subviews ) {
if ( [ [ view . class description ] hasPrefix : @ "WK" ] )
subview = view ;
}
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
if ( subview = = nil ) return ;
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
Class class = subview . class ;
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
NSOperatingSystemVersion iOS_11 _3 _0 = ( NSOperatingSystemVersion ) { 11 , 3 , 0 } ;
NSOperatingSystemVersion iOS_12 _2 _0 = ( NSOperatingSystemVersion ) { 12 , 2 , 0 } ;
2019-08-08 08:32:41 +00:00
NSOperatingSystemVersion iOS_13 _0 _0 = ( NSOperatingSystemVersion ) { 13 , 0 , 0 } ;
2019-04-29 15:46:07 +00:00
Method method ;
IMP override ;
2019-08-08 08:32:41 +00:00
if ( [ [ NSProcessInfo processInfo ] isOperatingSystemAtLeastVersion : iOS_13 _0 _0 ] ) {
// iOS 13.0 .0 - Future
SEL selector = sel_getUid ( "_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:" ) ;
method = class_getInstanceMethod ( class , selector ) ;
IMP original = method_getImplementation ( method ) ;
override = imp_implementationWithBlock ( ^ void ( id me , void * arg0 , BOOL arg1 , BOOL arg2 , BOOL arg3 , id arg4 ) {
( ( void ( * ) ( id , SEL , void * , BOOL , BOOL , BOOL , id ) ) original ) ( me , selector , arg0 , TRUE , arg2 , arg3 , arg4 ) ;
} ) ;
}
else if ( [ [ NSProcessInfo processInfo ] isOperatingSystemAtLeastVersion : iOS_12 _2 _0 ] ) {
// iOS 12.2 .0 - iOS 13.0 .0
2019-04-29 15:46:07 +00:00
SEL selector = sel_getUid ( "_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:" ) ;
method = class_getInstanceMethod ( class , selector ) ;
IMP original = method_getImplementation ( method ) ;
override = imp_implementationWithBlock ( ^ void ( id me , void * arg0 , BOOL arg1 , BOOL arg2 , BOOL arg3 , id arg4 ) {
( ( void ( * ) ( id , SEL , void * , BOOL , BOOL , BOOL , id ) ) original ) ( me , selector , arg0 , TRUE , arg2 , arg3 , arg4 ) ;
} ) ;
}
else if ( [ [ NSProcessInfo processInfo ] isOperatingSystemAtLeastVersion : iOS_11 _3 _0 ] ) {
// iOS 11.3 .0 - 12.2 .0
SEL selector = sel_getUid ( "_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:" ) ;
method = class_getInstanceMethod ( class , selector ) ;
IMP original = method_getImplementation ( method ) ;
override = imp_implementationWithBlock ( ^ void ( id me , void * arg0 , BOOL arg1 , BOOL arg2 , BOOL arg3 , id arg4 ) {
( ( void ( * ) ( id , SEL , void * , BOOL , BOOL , BOOL , id ) ) original ) ( me , selector , arg0 , TRUE , arg2 , arg3 , arg4 ) ;
} ) ;
} else {
// iOS 9.0 - 11.3 .0
SEL selector = sel_getUid ( "_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:" ) ;
method = class_getInstanceMethod ( class , selector ) ;
IMP original = method_getImplementation ( method ) ;
override = imp_implementationWithBlock ( ^ void ( id me , void * arg0 , BOOL arg1 , BOOL arg2 , id arg3 ) {
( ( void ( * ) ( id , SEL , void * , BOOL , BOOL , id ) ) original ) ( me , selector , arg0 , TRUE , arg2 , arg3 ) ;
} ) ;
}
2019-08-30 10:04:32 +00:00
2019-04-29 15:46:07 +00:00
method_setImplementation ( method , override ) ;
}
2018-10-17 14:59:19 +00:00
- ( void ) setHideKeyboardAccessoryView : ( BOOL ) hideKeyboardAccessoryView
{
if ( _webView = = nil ) {
_savedHideKeyboardAccessoryView = hideKeyboardAccessoryView ;
return ;
}
if ( _savedHideKeyboardAccessoryView = = false ) {
return ;
}
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
UIView * subview ;
2019-02-01 17:37:28 +00:00
2018-10-17 14:59:19 +00:00
for ( UIView * view in _webView . scrollView . subviews ) {
if ( [ [ view . class description ] hasPrefix : @ "WK" ] )
subview = view ;
}
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
if ( subview = = nil ) return ;
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
NSString * name = [ NSString stringWithFormat : @ "%@_SwizzleHelperWK" , subview . class . superclass ] ;
Class newClass = NSClassFromString ( name ) ;
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
if ( newClass = = nil )
{
newClass = objc_allocateClassPair ( subview . class , [ name cStringUsingEncoding : NSASCIIStringEncoding ] , 0 ) ;
if ( ! newClass ) return ;
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
Method method = class_getInstanceMethod ( [ _SwizzleHelperWK class ] , @ selector ( inputAccessoryView ) ) ;
class_addMethod ( newClass , @ selector ( inputAccessoryView ) , method_getImplementation ( method ) , method_getTypeEncoding ( method ) ) ;
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
objc_registerClassPair ( newClass ) ;
}
2018-11-19 10:17:57 +00:00
2018-10-17 14:59:19 +00:00
object_setClass ( subview , newClass ) ;
}
2018-09-08 04:11:49 +00:00
2020-02-12 23:39:11 +00:00
// UIScrollViewDelegate method
2018-09-08 04:11:49 +00:00
- ( void ) scrollViewWillBeginDragging : ( UIScrollView * ) scrollView
{
scrollView . decelerationRate = _decelerationRate ;
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
- ( void ) setScrollEnabled : ( BOOL ) scrollEnabled
{
_scrollEnabled = scrollEnabled ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
_webView . scrollView . scrollEnabled = scrollEnabled ;
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2019-02-28 21:01:42 +00:00
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
// UIScrollViewDelegate method
2019-02-28 21:01:42 +00:00
- ( void ) scrollViewDidScroll : ( UIScrollView * ) scrollView
{
// Don ' t allow scrolling the scrollView .
2019-03-01 09:46:53 +00:00
if ( ! _scrollEnabled ) {
scrollView . bounds = _webView . bounds ;
}
2019-05-16 22:27:16 +00:00
else if ( _onScroll ! = nil ) {
NSDictionary * event = @ {
@ "contentOffset" : @ {
@ "x" : @ ( scrollView . contentOffset . x ) ,
@ "y" : @ ( scrollView . contentOffset . y )
} ,
@ "contentInset" : @ {
@ "top" : @ ( scrollView . contentInset . top ) ,
@ "left" : @ ( scrollView . contentInset . left ) ,
@ "bottom" : @ ( scrollView . contentInset . bottom ) ,
@ "right" : @ ( scrollView . contentInset . right )
} ,
@ "contentSize" : @ {
@ "width" : @ ( scrollView . contentSize . width ) ,
@ "height" : @ ( scrollView . contentSize . height )
} ,
@ "layoutMeasurement" : @ {
@ "width" : @ ( scrollView . frame . size . width ) ,
@ "height" : @ ( scrollView . frame . size . height )
} ,
@ "zoomScale" : @ ( scrollView . zoomScale ? : 1 ) ,
} ;
_onScroll ( event ) ;
}
2018-09-08 04:11:49 +00:00
}
2019-03-07 09:27:29 +00:00
- ( void ) setDirectionalLockEnabled : ( BOOL ) directionalLockEnabled
{
_directionalLockEnabled = directionalLockEnabled ;
_webView . scrollView . directionalLockEnabled = directionalLockEnabled ;
}
2019-02-12 13:47:24 +00:00
- ( void ) setShowsHorizontalScrollIndicator : ( BOOL ) showsHorizontalScrollIndicator
{
_showsHorizontalScrollIndicator = showsHorizontalScrollIndicator ;
_webView . scrollView . showsHorizontalScrollIndicator = showsHorizontalScrollIndicator ;
}
- ( void ) setShowsVerticalScrollIndicator : ( BOOL ) showsVerticalScrollIndicator
{
_showsVerticalScrollIndicator = showsVerticalScrollIndicator ;
_webView . scrollView . showsVerticalScrollIndicator = showsVerticalScrollIndicator ;
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2019-02-12 13:47:24 +00:00
2018-09-08 04:11:49 +00:00
- ( void ) postMessage : ( NSString * ) message
{
NSDictionary * eventInitDict = @ { @ "data" : message } ;
NSString * source = [ NSString
2019-02-01 17:37:28 +00:00
stringWithFormat : @ "window.dispatchEvent(new MessageEvent('message', %@));" ,
2018-09-08 04:11:49 +00:00
RCTJSONStringify ( eventInitDict , NULL )
] ;
2019-02-01 17:37:28 +00:00
[ self injectJavaScript : source ] ;
2018-09-08 04:11:49 +00:00
}
- ( void ) layoutSubviews
{
[ super layoutSubviews ] ;
2019-08-30 10:04:32 +00:00
// Ensure webview takes the position and dimensions of RNCWebView
2018-09-08 04:11:49 +00:00
_webView . frame = self . bounds ;
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2019-06-02 18:54:17 +00:00
_webView . scrollView . contentInset = _contentInset ;
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
}
- ( NSMutableDictionary < NSString * , id > * ) baseEvent
{
NSDictionary * event = @ {
@ "url" : _webView . URL . absoluteString ? : @ "" ,
2019-04-23 09:16:04 +00:00
@ "title" : _webView . title ? : @ "" ,
2018-09-08 04:11:49 +00:00
@ "loading" : @ ( _webView . loading ) ,
@ "canGoBack" : @ ( _webView . canGoBack ) ,
@ "canGoForward" : @ ( _webView . canGoForward )
} ;
return [ [ NSMutableDictionary alloc ] initWithDictionary : event ] ;
}
2019-02-12 09:35:14 +00:00
+ ( void ) setClientAuthenticationCredential : ( nullable NSURLCredential * ) credential {
clientAuthenticationCredential = credential ;
}
2019-09-21 00:44:53 +00:00
+ ( void ) setCustomCertificatesForHost : ( nullable NSDictionary * ) certificates {
customCertificatesForHost = certificates ;
}
2019-02-12 09:35:14 +00:00
- ( void ) webView : ( WKWebView * ) webView
didReceiveAuthenticationChallenge : ( NSURLAuthenticationChallenge * ) challenge
completionHandler : ( void ( ^ ) ( NSURLSessionAuthChallengeDisposition disposition , NSURLCredential * _Nullable ) ) completionHandler
{
2019-09-21 00:44:53 +00:00
NSString * host = nil ;
if ( webView . URL ! = nil ) {
host = webView . URL . host ;
}
if ( [ [ challenge protectionSpace ] authenticationMethod ] = = NSURLAuthenticationMethodClientCertificate ) {
completionHandler ( NSURLSessionAuthChallengeUseCredential , clientAuthenticationCredential ) ;
return ;
}
if ( [ [ challenge protectionSpace ] serverTrust ] ! = nil && customCertificatesForHost ! = nil && host ! = nil ) {
SecCertificateRef localCertificate = ( __bridge SecCertificateRef ) ( [ customCertificatesForHost objectForKey : host ] ) ;
if ( localCertificate ! = nil ) {
NSData * localCertificateData = ( NSData * ) CFBridgingRelease ( SecCertificateCopyData ( localCertificate ) ) ;
SecTrustRef trust = [ [ challenge protectionSpace ] serverTrust ] ;
long count = SecTrustGetCertificateCount ( trust ) ;
for ( long i = 0 ; i < count ; i + + ) {
SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex ( trust , i ) ;
if ( serverCertificate = = nil ) { continue ; }
NSData * serverCertificateData = ( NSData * ) CFBridgingRelease ( SecCertificateCopyData ( serverCertificate ) ) ;
if ( [ serverCertificateData isEqualToData : localCertificateData ] ) {
NSURLCredential * useCredential = [ NSURLCredential credentialForTrust : trust ] ;
if ( challenge . sender ! = nil ) {
[ challenge . sender useCredential : useCredential forAuthenticationChallenge : challenge ] ;
}
completionHandler ( NSURLSessionAuthChallengeUseCredential , useCredential ) ;
return ;
}
}
}
}
2019-02-12 09:35:14 +00:00
completionHandler ( NSURLSessionAuthChallengePerformDefaultHandling , nil ) ;
}
2018-09-08 04:11:49 +00:00
# pragma mark - WKNavigationDelegate methods
2018-12-14 10:18:48 +00:00
/ * *
2020-02-12 23:39:11 +00:00
* alert
* /
2019-01-07 14:19:32 +00:00
- ( void ) webView : ( WKWebView * ) webView runJavaScriptAlertPanelWithMessage : ( NSString * ) message initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( void ) ) completionHandler
{
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "" message : message preferredStyle : UIAlertControllerStyleAlert ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : @ "Ok" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
completionHandler ( ) ;
} ] ] ;
[ [ self topViewController ] presentViewController : alert animated : YES completion : NULL ] ;
# else
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
[ alert setMessageText : message ] ;
[ alert beginSheetModalForWindow : [ NSApp keyWindow ] completionHandler : ^ ( __unused NSModalResponse response ) {
completionHandler ( ) ;
} ] ;
# endif // ! TARGET_OS _OSX
2018-12-14 10:18:48 +00:00
}
/ * *
2020-02-12 23:39:11 +00:00
* confirm
* /
2018-12-14 10:18:48 +00:00
- ( void ) webView : ( WKWebView * ) webView runJavaScriptConfirmPanelWithMessage : ( NSString * ) message initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( BOOL ) ) completionHandler {
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "" message : message preferredStyle : UIAlertControllerStyleAlert ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : @ "Ok" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
completionHandler ( YES ) ;
} ] ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
completionHandler ( NO ) ;
} ] ] ;
[ [ self topViewController ] presentViewController : alert animated : YES completion : NULL ] ;
# else
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
[ alert setMessageText : message ] ;
[ alert addButtonWithTitle : NSLocalizedString ( @ "OK" , @ "OK button" ) ] ;
[ alert addButtonWithTitle : NSLocalizedString ( @ "Cancel" , @ "Cancel button" ) ] ;
void ( ^ callbacksHandlers ) ( NSModalResponse response ) = ^ void ( NSModalResponse response ) {
completionHandler ( response = = NSAlertFirstButtonReturn ) ;
} ;
[ alert beginSheetModalForWindow : [ NSApp keyWindow ] completionHandler : callbacksHandlers ] ;
# endif // ! TARGET_OS _OSX
2018-12-14 10:18:48 +00:00
}
/ * *
2020-02-12 23:39:11 +00:00
* prompt
* /
2018-12-14 10:18:48 +00:00
- ( void ) webView : ( WKWebView * ) webView runJavaScriptTextInputPanelWithPrompt : ( NSString * ) prompt defaultText : ( NSString * ) defaultText initiatedByFrame : ( WKFrameInfo * ) frame completionHandler : ( void ( ^ ) ( NSString * ) ) completionHandler {
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "" message : prompt preferredStyle : UIAlertControllerStyleAlert ] ;
[ alert addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
textField . text = defaultText ;
} ] ;
UIAlertAction * okAction = [ UIAlertAction actionWithTitle : @ "Ok" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
completionHandler ( [ [ alert . textFields lastObject ] text ] ) ;
} ] ;
[ alert addAction : okAction ] ;
UIAlertAction * cancelAction = [ UIAlertAction actionWithTitle : @ "Cancel" style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
completionHandler ( nil ) ;
} ] ;
[ alert addAction : cancelAction ] ;
alert . preferredAction = okAction ;
[ [ self topViewController ] presentViewController : alert animated : YES completion : NULL ] ;
# else
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
[ alert setMessageText : prompt ] ;
const NSRect RCTSingleTextFieldFrame = NSMakeRect ( 0.0 , 0.0 , 275.0 , 22.0 ) ;
NSTextField * textField = [ [ NSTextField alloc ] initWithFrame : RCTSingleTextFieldFrame ] ;
textField . cell . scrollable = YES ;
if ( @ available ( macOS 10.11 , * ) ) {
textField . maximumNumberOfLines = 1 ;
}
textField . stringValue = defaultText ;
[ alert setAccessoryView : textField ] ;
[ alert addButtonWithTitle : NSLocalizedString ( @ "OK" , @ "OK button" ) ] ;
[ alert addButtonWithTitle : NSLocalizedString ( @ "Cancel" , @ "Cancel button" ) ] ;
[ alert beginSheetModalForWindow : [ NSApp keyWindow ] completionHandler : ^ ( NSModalResponse response ) {
if ( response = = NSAlertFirstButtonReturn ) {
completionHandler ( [ textField stringValue ] ) ;
} else {
completionHandler ( nil ) ;
}
} ] ;
# endif // ! TARGET_OS _OSX
2018-12-14 10:18:48 +00:00
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-12-14 10:18:48 +00:00
/ * *
* topViewController
* /
- ( UIViewController * ) topViewController {
2020-08-25 23:13:00 +00:00
return RCTPresentedViewController ( ) ;
2018-12-14 10:18:48 +00:00
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2018-12-14 10:18:48 +00:00
2018-09-08 04:11:49 +00:00
/ * *
* Decides whether to allow or cancel a navigation .
* @ see https : // fburl . com / 42 r9fxob
* /
- ( void ) webView : ( WKWebView * ) webView
decidePolicyForNavigationAction : ( WKNavigationAction * ) navigationAction
decisionHandler : ( void ( ^ ) ( WKNavigationActionPolicy ) ) decisionHandler
{
static NSDictionary < NSNumber * , NSString * > * navigationTypes ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
navigationTypes = @ {
@ ( WKNavigationTypeLinkActivated ) : @ "click" ,
@ ( WKNavigationTypeFormSubmitted ) : @ "formsubmit" ,
@ ( WKNavigationTypeBackForward ) : @ "backforward" ,
@ ( WKNavigationTypeReload ) : @ "reload" ,
@ ( WKNavigationTypeFormResubmitted ) : @ "formresubmit" ,
@ ( WKNavigationTypeOther ) : @ "other" ,
} ;
} ) ;
WKNavigationType navigationType = navigationAction . navigationType ;
NSURLRequest * request = navigationAction . request ;
2020-08-15 09:21:38 +00:00
BOOL isTopFrame = [ request . URL isEqual : request . mainDocumentURL ] ;
2018-09-08 04:11:49 +00:00
if ( _onShouldStartLoadWithRequest ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
[ event addEntriesFromDictionary : @ {
@ "url" : ( request . URL ) . absoluteString ,
2019-06-13 06:59:52 +00:00
@ "mainDocumentURL" : ( request . mainDocumentURL ) . absoluteString ,
2020-08-15 09:21:38 +00:00
@ "navigationType" : navigationTypes [ @ ( navigationType ) ] ,
@ "isTopFrame" : @ ( isTopFrame )
2018-09-08 04:11:49 +00:00
} ] ;
if ( ! [ self . delegate webView : self
shouldStartLoadForRequest : event
withCallback : _onShouldStartLoadWithRequest ] ) {
2019-09-23 08:49:06 +00:00
decisionHandler ( WKNavigationActionPolicyCancel ) ;
2018-09-08 04:11:49 +00:00
return ;
}
}
if ( _onLoadingStart ) {
// We have this check to filter out iframe requests and whatnot
if ( isTopFrame ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
[ event addEntriesFromDictionary : @ {
@ "url" : ( request . URL ) . absoluteString ,
@ "navigationType" : navigationTypes [ @ ( navigationType ) ]
} ] ;
_onLoadingStart ( event ) ;
}
}
// Allow all navigation by default
2019-09-23 08:49:06 +00:00
decisionHandler ( WKNavigationActionPolicyAllow ) ;
2018-09-08 04:11:49 +00:00
}
2019-09-21 00:43:28 +00:00
2019-09-23 08:20:12 +00:00
/ * *
* Called when the web view ’ s content process is terminated .
* @ see https : // developer . apple . com / documentation / webkit / wknavigationdelegate / 1455639 - webviewwebcontentprocessdidtermi ? language = objc
* /
- ( void ) webViewWebContentProcessDidTerminate : ( WKWebView * ) webView
{
RCTLogWarn ( @ "Webview Process Terminated" ) ;
if ( _onContentProcessDidTerminate ) {
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
_onContentProcessDidTerminate ( event ) ;
}
}
2019-09-21 00:43:28 +00:00
/ * *
* Decides whether to allow or cancel a navigation after its response is known .
* @ see https : // developer . apple . com / documentation / webkit / wknavigationdelegate / 1455643 - webview ? language = objc
* /
- ( void ) webView : ( WKWebView * ) webView
decidePolicyForNavigationResponse : ( WKNavigationResponse * ) navigationResponse
decisionHandler : ( void ( ^ ) ( WKNavigationResponsePolicy ) ) decisionHandler
{
2020-04-29 16:09:22 +00:00
WKNavigationResponsePolicy policy = WKNavigationResponsePolicyAllow ;
2019-09-21 00:43:28 +00:00
if ( _onHttpError && navigationResponse . forMainFrame ) {
if ( [ navigationResponse . response isKindOfClass : [ NSHTTPURLResponse class ] ] ) {
NSHTTPURLResponse * response = ( NSHTTPURLResponse * ) navigationResponse . response ;
NSInteger statusCode = response . statusCode ;
if ( statusCode >= 400 ) {
2020-04-29 16:09:22 +00:00
NSMutableDictionary < NSString * , id > * httpErrorEvent = [ self baseEvent ] ;
[ httpErrorEvent addEntriesFromDictionary : @ {
2019-09-21 00:43:28 +00:00
@ "url" : response . URL . absoluteString ,
@ "statusCode" : @ ( statusCode )
} ] ;
2020-04-29 16:09:22 +00:00
_onHttpError ( httpErrorEvent ) ;
}
NSString * disposition = nil ;
if ( @ available ( iOS 13 , * ) ) {
disposition = [ response valueForHTTPHeaderField : @ "Content-Disposition" ] ;
}
BOOL isAttachment = disposition ! = nil && [ disposition hasPrefix : @ "attachment" ] ;
if ( isAttachment || ! navigationResponse . canShowMIMEType ) {
if ( _onFileDownload ) {
policy = WKNavigationResponsePolicyCancel ;
NSMutableDictionary < NSString * , id > * downloadEvent = [ self baseEvent ] ;
[ downloadEvent addEntriesFromDictionary : @ {
@ "downloadUrl" : ( response . URL ) . absoluteString ,
} ] ;
_onFileDownload ( downloadEvent ) ;
}
2019-09-21 00:43:28 +00:00
}
}
2020-04-29 16:09:22 +00:00
}
2019-09-21 00:43:28 +00:00
2020-04-29 16:09:22 +00:00
decisionHandler ( policy ) ;
2019-09-21 00:43:28 +00:00
}
2018-09-08 04:11:49 +00:00
/ * *
* Called when an error occurs while the web view is loading content .
* @ see https : // fburl . com / km6vqenw
* /
- ( void ) webView : ( WKWebView * ) webView
didFailProvisionalNavigation : ( WKNavigation * ) navigation
withError : ( NSError * ) error
{
if ( _onLoadingError ) {
if ( [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorCancelled ) {
// NSURLErrorCancelled is reported when a page has a redirect OR if you load
// a new URL in the WebView before the previous one came back . We can just
// ignore these since they aren ' t real errors .
// http : // stackoverflow . com / questions / 1024748 / how - do - i - fix - nsurlerrordomain - error -999 - in - iphone -3 -0 - os
return ;
}
2019-12-17 09:55:26 +00:00
if ( [ error . domain isEqualToString : @ "WebKitErrorDomain" ] && error . code = = 102 || [ error . domain isEqualToString : @ "WebKitErrorDomain" ] && error . code = = 101 ) {
2019-01-11 14:01:49 +00:00
// Error code 102 "Frame load interrupted" is raised by the WKWebView
// when the URL is from an http redirect . This is a common pattern when
// implementing OAuth with a WebView .
return ;
}
2018-09-08 04:11:49 +00:00
NSMutableDictionary < NSString * , id > * event = [ self baseEvent ] ;
[ event addEntriesFromDictionary : @ {
@ "didFailProvisionalNavigation" : @ YES ,
@ "domain" : error . domain ,
@ "code" : @ ( error . code ) ,
@ "description" : error . localizedDescription ,
} ] ;
_onLoadingError ( event ) ;
}
}
- ( void ) evaluateJS : ( NSString * ) js
thenCall : ( void ( ^ ) ( NSString * ) ) callback
{
[ self . webView evaluateJavaScript : js completionHandler : ^ ( id result , NSError * error ) {
2019-08-04 08:43:04 +00:00
if ( callback ! = nil ) {
callback ( [ NSString stringWithFormat : @ "%@" , result ] ) ;
}
if ( error ! = nil ) {
2019-09-23 08:49:06 +00:00
RCTLogWarn ( @ "%@" , [ NSString stringWithFormat : @ "Error evaluating injectedJavaScript: This is possibly due to an unsupported return type. Try adding true to the end of your injectedJavaScript string. %@" , error ] ) ;
2018-09-08 04:11:49 +00:00
}
} ] ;
}
2020-04-13 15:54:47 +00:00
- ( void ) forceIgnoreSilentHardwareSwitch : ( BOOL ) initialSetup
{
NSString * mp3Str = @ "data:audio/mp3;base64,//tAxAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAFAAAESAAzMzMzMzMzMzMzMzMzMzMzMzMzZmZmZmZmZmZmZmZmZmZmZmZmZmaZmZmZmZmZmZmZmZmZmZmZmZmZmczMzMzMzMzMzMzMzMzMzMzMzMzM//////////////////////////8AAAA5TEFNRTMuMTAwAZYAAAAAAAAAABQ4JAMGQgAAOAAABEhNIZS0AAAAAAD/+0DEAAPH3Yz0AAR8CPqyIEABp6AxjG/4x/XiInE4lfQDFwIIRE+uBgZoW4RL0OLMDFn6E5v+/u5ehf76bu7/6bu5+gAiIQGAABQIUJ0QolFghEn/9PhZQpcUTpXMjo0OGzRCZXyKxoIQzB2KhCtGobpT9TRVj/3Pmfp+f8X7Pu1B04sTnc3s0XhOlXoGVCMNo9X//9/r6a10TZEY5DsxqvO7mO5qFvpFCmKIjhpSItGsUYcRO//7QsQRgEiljQIAgLFJAbIhNBCa+JmorCbOi5q9nVd2dKnusTMQg4MFUlD6DQ4OFijwGAijRMfLbHG4nLVTjydyPlJTj8pfPflf9/5GD950A5e+jsrmNZSjSirjs1R7hnkia8vr//l/7Nb+crvr9Ok5ZJOylUKRxf/P9Zn0j2P4pJYXyKkeuy5wUYtdmOu6uobEtFqhIJViLEKIjGxchGev/L3Y0O3bwrIOszTBAZ7Ih28EUaSOZf/7QsQfg8fpjQIADN0JHbGgQBAZ8T//y//t/7d/2+f5m7MdCeo/9tdkMtGLbt1tqnabRroO1Qfvh20yEbei8nfDXP7btW7f9/uO9tbe5IvHQbLlxpf3DkAk0ojYcv///5/u3/7PTfGjPEPUvt5D6f+/3Lea4lz4tc4TnM/mFPrmalWbboeNiNyeyr+vufttZuvrVrt/WYv3T74JFo8qEDiJqJrmDTs///v99xDku2xG02jjunrICP/7QsQtA8kpkQAAgNMA/7FgQAGnobgfghgqA+uXwWQ3XFmGimSbe2X3ksY//KzK1a2k6cnNWOPJnPWUsYbKqkh8RJzrVf///P///////4vyhLKHLrCb5nIrYIUss4cthigL1lQ1wwNAc6C1pf1TIKRSkt+a//z+yLVcwlXKSqeSuCVQFLng2h4AFAFgTkH+Z/8jTX/zr//zsJV/5f//5UX/0ZNCNCCaf5lTCTRkaEdhNP//n/KUjf/7QsQ5AEhdiwAAjN7I6jGddBCO+WGTQ1mXrYatSAgaykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==" ;
NSString * scr ;
if ( initialSetup ) {
scr = [ NSString stringWithFormat : @ "var s=new Audio('%@');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s);true" , mp3Str ] ;
} else {
scr = [ NSString stringWithFormat : @ "var s=document.getElementById('wkwebviewAudio');s.src=null;s.parentNode.removeChild(s);s=null;s=new Audio('%@');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s);true" , mp3Str ] ;
}
[ self evaluateJS : scr thenCall : nil ] ;
}
- ( void ) disableIgnoreSilentSwitch
{
[ self evaluateJS : @ "document.getElementById('wkwebviewAudio').src=null;true" thenCall : nil ] ;
}
- ( void ) appDidBecomeActive
{
if ( _ignoreSilentHardwareSwitch ) {
[ self forceIgnoreSilentHardwareSwitch : false ] ;
}
}
- ( void ) appWillResignActive
{
if ( _ignoreSilentHardwareSwitch ) {
[ self disableIgnoreSilentSwitch ] ;
}
}
2018-09-08 04:11:49 +00:00
/ * *
* Called when the navigation is complete .
* @ see https : // fburl . com / rtys6jlb
* /
2019-12-06 10:15:02 +00:00
- ( void ) webView : ( WKWebView * ) webView
2018-09-08 04:11:49 +00:00
didFinishNavigation : ( WKNavigation * ) navigation
{
2020-04-13 15:54:47 +00:00
if ( _ignoreSilentHardwareSwitch ) {
[ self forceIgnoreSilentHardwareSwitch : true ] ;
}
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
if ( _onLoadingFinish ) {
2018-09-08 04:11:49 +00:00
_onLoadingFinish ( [ self baseEvent ] ) ;
}
}
- ( void ) injectJavaScript : ( NSString * ) script
{
[ self evaluateJS : script thenCall : nil ] ;
}
- ( void ) goForward
{
[ _webView goForward ] ;
}
- ( void ) goBack
{
[ _webView goBack ] ;
}
- ( void ) reload
{
/ * *
* When the initial load fails due to network connectivity issues ,
* [ _webView reload ] doesn ' t reload the webpage . Therefore , we must
* manually call [ _webView loadRequest : request ] .
* /
2019-04-15 08:19:10 +00:00
NSURLRequest * request = [ self requestForSource : self . source ] ;
2018-09-08 04:11:49 +00:00
if ( request . URL && ! _webView . URL . absoluteString . length ) {
[ _webView loadRequest : request ] ;
2019-04-15 08:19:10 +00:00
} else {
2018-09-08 04:11:49 +00:00
[ _webView reload ] ;
}
}
2020-10-05 15:14:42 +00:00
# if ! TARGET_OS _OSX
2020-08-17 10:07:36 +00:00
- ( void ) addPullToRefreshControl
{
UIRefreshControl * refreshControl = [ [ UIRefreshControl alloc ] init ] ;
_refreshControl = refreshControl ;
[ _webView . scrollView addSubview : refreshControl ] ;
[ refreshControl addTarget : self action : @ selector ( pullToRefresh : ) forControlEvents : UIControlEventValueChanged ] ;
}
- ( void ) pullToRefresh : ( UIRefreshControl * ) refreshControl
{
[ self reload ] ;
[ refreshControl endRefreshing ] ;
}
2020-10-05 15:14:42 +00:00
2020-08-17 10:07:36 +00:00
- ( void ) setPullToRefreshEnabled : ( BOOL ) pullToRefreshEnabled
{
_pullToRefreshEnabled = pullToRefreshEnabled ;
if ( pullToRefreshEnabled ) {
[ self addPullToRefreshControl ] ;
} else {
[ _refreshControl removeFromSuperview ] ;
}
[ self setBounces : _bounces ] ;
}
# endif // ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
- ( void ) stopLoading
{
[ _webView stopLoading ] ;
}
2020-02-12 23:39:11 +00:00
# if ! TARGET_OS _OSX
2018-09-08 04:11:49 +00:00
- ( void ) setBounces : ( BOOL ) bounces
{
_bounces = bounces ;
2020-08-17 10:07:36 +00:00
// For UIRefreshControl to work correctly , the bounces should always be true
_webView . scrollView . bounces = _pullToRefreshEnabled || bounces ;
2018-09-08 04:11:49 +00:00
}
2020-02-12 23:39:11 +00:00
# endif // ! TARGET_OS _OSX
2019-04-15 08:19:10 +00:00
2020-03-17 21:01:20 +00:00
- ( void ) setInjectedJavaScript : ( NSString * ) source {
_injectedJavaScript = source ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
self . atEndScript = source = = nil ? nil : [ [ WKUserScript alloc ] initWithSource : source
injectionTime : WKUserScriptInjectionTimeAtDocumentEnd
forMainFrameOnly : _injectedJavaScriptForMainFrameOnly ] ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
if ( _webView ! = nil ) {
[ self resetupScripts : _webView . configuration ] ;
}
}
- ( void ) setInjectedJavaScriptBeforeContentLoaded : ( NSString * ) source {
_injectedJavaScriptBeforeContentLoaded = source ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
self . atStartScript = source = = nil ? nil : [ [ WKUserScript alloc ] initWithSource : source
injectionTime : WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly : _injectedJavaScriptBeforeContentLoadedForMainFrameOnly ] ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
if ( _webView ! = nil ) {
[ self resetupScripts : _webView . configuration ] ;
}
}
- ( void ) setInjectedJavaScriptForMainFrameOnly : ( BOOL ) mainFrameOnly {
_injectedJavaScriptForMainFrameOnly = mainFrameOnly ;
[ self setInjectedJavaScript : _injectedJavaScript ] ;
}
- ( void ) setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly : ( BOOL ) mainFrameOnly {
_injectedJavaScriptBeforeContentLoadedForMainFrameOnly = mainFrameOnly ;
[ self setInjectedJavaScriptBeforeContentLoaded : _injectedJavaScriptBeforeContentLoaded ] ;
}
- ( void ) setMessagingEnabled : ( BOOL ) messagingEnabled {
_messagingEnabled = messagingEnabled ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
self . postMessageScript = _messagingEnabled ?
[
[ WKUserScript alloc ]
initWithSource : [
NSString
stringWithFormat :
@ "window.%@ = {"
" postMessage: function (data) {"
" window.webkit.messageHandlers.%@.postMessage(String(data));"
" }"
"};" , MessageHandlerName , MessageHandlerName
]
injectionTime : WKUserScriptInjectionTimeAtDocumentStart
/ * TODO : For a separate ( minor ) PR : use logic like this ( as react - native - wkwebview does ) so that messaging can be used in all frames if desired .
* I am keeping it as YES for consistency with previous behaviour . * /
// forMainFrameOnly : _messagingEnabledForMainFrameOnly
forMainFrameOnly : YES
] :
nil ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
if ( _webView ! = nil ) {
[ self resetupScripts : _webView . configuration ] ;
}
}
- ( void ) resetupScripts : ( WKWebViewConfiguration * ) wkWebViewConfig {
[ wkWebViewConfig . userContentController removeAllUserScripts ] ;
[ wkWebViewConfig . userContentController removeScriptMessageHandlerForName : MessageHandlerName ] ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
NSString * html5HistoryAPIShimSource = [ NSString stringWithFormat :
@ "(function(history) {\n"
" function notify(type) {\n"
" setTimeout(function() {\n"
" window.webkit.messageHandlers.%@.postMessage(type)\n"
" }, 0)\n"
" }\n"
" function shim(f) {\n"
" return function pushState() {\n"
" notify('other')\n"
" return f.apply(history, arguments)\n"
" }\n"
" }\n"
" history.pushState = shim(history.pushState)\n"
" history.replaceState = shim(history.replaceState)\n"
" window.addEventListener('popstate', function() {\n"
" notify('backforward')\n"
" })\n"
"})(window.history)\n" , HistoryShimName
] ;
WKUserScript * script = [ [ WKUserScript alloc ] initWithSource : html5HistoryAPIShimSource injectionTime : WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly : YES ] ;
[ wkWebViewConfig . userContentController addUserScript : script ] ;
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
if ( _sharedCookiesEnabled ) {
// More info to sending cookies with WKWebView
// https : // stackoverflow . com / questions / 26573137 / can - i - set - the - cookies - to - be - used - by - a - wkwebview / 26577303 #26577303
if ( @ available ( iOS 11.0 , * ) ) {
// Set Cookies in iOS 11 and above , initialize websiteDataStore before setting cookies
// See also https : // forums . developer . apple . com / thread / 97194
// check if websiteDataStore has not been initialized before
if ( ! _incognito && ! _cacheEnabled ) {
wkWebViewConfig . websiteDataStore = [ WKWebsiteDataStore nonPersistentDataStore ] ;
}
for ( NSHTTPCookie * cookie in [ [ NSHTTPCookieStorage sharedHTTPCookieStorage ] cookies ] ) {
[ wkWebViewConfig . websiteDataStore . httpCookieStore setCookie : cookie completionHandler : nil ] ;
}
} else {
NSMutableString * script = [ NSMutableString string ] ;
// Clear all existing cookies in a direct called function . This ensures that no
// javascript error will break the web content javascript .
// We keep this code here , if someone requires that Cookies are also removed within the
// the WebView and want to extends the current sharedCookiesEnabled option with an
// additional property .
// Generates JS : document . cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
// for each cookie which is already available in the WebView context .
/ *
[ script appendString : @ "(function () {\n" ] ;
[ script appendString : @ " var cookies = document.cookie.split('; ');\n" ] ;
[ script appendString : @ " for (var i = 0; i < cookies.length; i++) {\n" ] ;
[ script appendString : @ " if (cookies[i].indexOf('=') !== -1) {\n" ] ;
[ script appendString : @ " document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n" ] ;
[ script appendString : @ " }\n" ] ;
[ script appendString : @ " }\n" ] ;
[ script appendString : @ "})();\n\n" ] ;
* /
// Set cookies in a direct called function . This ensures that no
// javascript error will break the web content javascript .
// Generates JS : document . cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
// for each cookie which is available in the application context .
[ script appendString : @ "(function () {\n" ] ;
for ( NSHTTPCookie * cookie in [ [ NSHTTPCookieStorage sharedHTTPCookieStorage ] cookies ] ) {
[ script appendFormat : @ "document.cookie = %@ + '=' + %@" ,
RCTJSONStringify ( cookie . name , NULL ) ,
RCTJSONStringify ( cookie . value , NULL ) ] ;
if ( cookie . path ) {
[ script appendFormat : @ " + '; Path=' + %@" , RCTJSONStringify ( cookie . path , NULL ) ] ;
}
if ( cookie . expiresDate ) {
[ script appendFormat : @ " + '; Expires=' + new Date(%f).toUTCString()" ,
cookie . expiresDate . timeIntervalSince1970 * 1000
] ;
}
[ script appendString : @ ";\n" ] ;
}
[ script appendString : @ "})();\n" ] ;
WKUserScript * cookieInScript = [ [ WKUserScript alloc ] initWithSource : script
injectionTime : WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly : YES ] ;
[ wkWebViewConfig . userContentController addUserScript : cookieInScript ] ;
}
}
2020-05-26 21:53:08 +00:00
2020-03-17 21:01:20 +00:00
if ( _messagingEnabled ) {
if ( self . postMessageScript ) {
[ wkWebViewConfig . userContentController addScriptMessageHandler : [ [ RNCWeakScriptMessageDelegate alloc ] initWithDelegate : self ]
name : MessageHandlerName ] ;
[ wkWebViewConfig . userContentController addUserScript : self . postMessageScript ] ;
}
if ( self . atEndScript ) {
[ wkWebViewConfig . userContentController addUserScript : self . atEndScript ] ;
}
}
2020-04-07 16:59:52 +00:00
// Whether or not messaging is enabled , add the startup script if it exists .
if ( self . atStartScript ) {
[ wkWebViewConfig . userContentController addUserScript : self . atStartScript ] ;
}
2020-03-17 21:01:20 +00:00
}
2019-04-15 08:19:10 +00:00
- ( NSURLRequest * ) requestForSource : ( id ) json {
NSURLRequest * request = [ RCTConvert NSURLRequest : self . source ] ;
// If sharedCookiesEnabled we automatically add all application cookies to the
// http request . This is automatically done on iOS 11 + in the WebView constructor .
// Se we need to manually add these shared cookies here only for iOS versions < 11.
if ( _sharedCookiesEnabled ) {
if ( @ available ( iOS 11.0 , * ) ) {
// see WKWebView initialization for added cookies
} else {
NSArray * cookies = [ [ NSHTTPCookieStorage sharedHTTPCookieStorage ] cookiesForURL : request . URL ] ;
NSDictionary < NSString * , NSString * > * cookieHeader = [ NSHTTPCookie requestHeaderFieldsWithCookies : cookies ] ;
NSMutableURLRequest * mutableRequest = [ request mutableCopy ] ;
[ mutableRequest setAllHTTPHeaderFields : cookieHeader ] ;
return mutableRequest ;
}
}
return request ;
}
2018-09-08 04:11:49 +00:00
@ end
2019-12-27 10:39:53 +00:00
@ implementation RNCWeakScriptMessageDelegate
- ( instancetype ) initWithDelegate : ( id < WKScriptMessageHandler > ) scriptDelegate {
self = [ super init ] ;
if ( self ) {
_scriptDelegate = scriptDelegate ;
}
return self ;
}
- ( void ) userContentController : ( WKUserContentController * ) userContentController didReceiveScriptMessage : ( WKScriptMessage * ) message {
[ self . scriptDelegate userContentController : userContentController didReceiveScriptMessage : message ] ;
}
@ end