@javascriptcommon/react-native-webview 13.12.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -0
  3. package/android/build.gradle +110 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +15 -0
  6. package/android/src/main/AndroidManifestNew.xml +13 -0
  7. package/android/src/main/java/com/reactnativecommunity/webview/RNCBasicAuthCredential.java +11 -0
  8. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java +372 -0
  9. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java +450 -0
  10. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java +328 -0
  11. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewConfig.java +11 -0
  12. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewFileProvider.java +14 -0
  13. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt +720 -0
  14. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewMessagingModule.kt +19 -0
  15. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModuleImpl.java +554 -0
  16. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java +56 -0
  17. package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewWrapper.kt +39 -0
  18. package/android/src/main/java/com/reactnativecommunity/webview/URLUtil.java +179 -0
  19. package/android/src/main/java/com/reactnativecommunity/webview/events/TopCustomMenuSelectionEvent.kt +24 -0
  20. package/android/src/main/java/com/reactnativecommunity/webview/events/TopHttpErrorEvent.kt +25 -0
  21. package/android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingErrorEvent.kt +25 -0
  22. package/android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingFinishEvent.kt +24 -0
  23. package/android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingProgressEvent.kt +24 -0
  24. package/android/src/main/java/com/reactnativecommunity/webview/events/TopLoadingStartEvent.kt +25 -0
  25. package/android/src/main/java/com/reactnativecommunity/webview/events/TopMessageEvent.kt +24 -0
  26. package/android/src/main/java/com/reactnativecommunity/webview/events/TopNewWindowEvent.kt +25 -0
  27. package/android/src/main/java/com/reactnativecommunity/webview/events/TopRenderProcessGoneEvent.kt +26 -0
  28. package/android/src/main/java/com/reactnativecommunity/webview/events/TopShouldStartLoadWithRequestEvent.kt +29 -0
  29. package/android/src/main/res/xml/file_provider_paths.xml +6 -0
  30. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java +553 -0
  31. package/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewModule.java +63 -0
  32. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java +328 -0
  33. package/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewModule.java +59 -0
  34. package/apple/RCTConvert+WKDataDetectorTypes.h +11 -0
  35. package/apple/RCTConvert+WKDataDetectorTypes.m +27 -0
  36. package/apple/RNCWKProcessPoolManager.h +15 -0
  37. package/apple/RNCWKProcessPoolManager.m +36 -0
  38. package/apple/RNCWebView.h +35 -0
  39. package/apple/RNCWebView.mm +546 -0
  40. package/apple/RNCWebViewDecisionManager.h +20 -0
  41. package/apple/RNCWebViewDecisionManager.m +47 -0
  42. package/apple/RNCWebViewImpl.h +161 -0
  43. package/apple/RNCWebViewImpl.m +1979 -0
  44. package/apple/RNCWebViewManager.h +9 -0
  45. package/apple/RNCWebViewManager.mm +215 -0
  46. package/apple/RNCWebViewModule.h +23 -0
  47. package/apple/RNCWebViewModule.mm +34 -0
  48. package/index.d.ts +65 -0
  49. package/index.js +4 -0
  50. package/ios/RNCWebView.xcodeproj/project.pbxproj +273 -0
  51. package/lib/NativeRNCWebViewModule.d.ts +10 -0
  52. package/lib/NativeRNCWebViewModule.js +1 -0
  53. package/lib/RNCWebViewNativeComponent.d.ts +242 -0
  54. package/lib/RNCWebViewNativeComponent.js +1 -0
  55. package/lib/WebView.android.d.ts +6 -0
  56. package/lib/WebView.android.js +1 -0
  57. package/lib/WebView.d.ts +6 -0
  58. package/lib/WebView.ios.d.ts +6 -0
  59. package/lib/WebView.ios.js +1 -0
  60. package/lib/WebView.js +1 -0
  61. package/lib/WebView.macos.d.ts +6 -0
  62. package/lib/WebView.macos.js +1 -0
  63. package/lib/WebView.styles.d.ts +38 -0
  64. package/lib/WebView.styles.js +1 -0
  65. package/lib/WebView.windows.d.ts +17 -0
  66. package/lib/WebView.windows.js +1 -0
  67. package/lib/WebViewNativeComponent.macos.d.ts +3 -0
  68. package/lib/WebViewNativeComponent.macos.js +1 -0
  69. package/lib/WebViewNativeComponent.windows.d.ts +3 -0
  70. package/lib/WebViewNativeComponent.windows.js +1 -0
  71. package/lib/WebViewShared.d.ts +38 -0
  72. package/lib/WebViewShared.js +1 -0
  73. package/lib/WebViewTypes.d.ts +1093 -0
  74. package/lib/WebViewTypes.js +1 -0
  75. package/lib/index.d.ts +3 -0
  76. package/lib/index.js +1 -0
  77. package/macos/RNCWebView.xcodeproj/project.pbxproj +363 -0
  78. package/package.json +101 -0
  79. package/react-native-webview.podspec +46 -0
  80. package/react-native.config.js +44 -0
  81. package/src/NativeRNCWebViewModule.ts +15 -0
  82. package/src/RNCWebViewNativeComponent.ts +342 -0
  83. package/src/WebView.android.tsx +317 -0
  84. package/src/WebView.ios.tsx +299 -0
  85. package/src/WebView.macos.tsx +245 -0
  86. package/src/WebView.styles.ts +41 -0
  87. package/src/WebView.tsx +25 -0
  88. package/src/WebView.windows.tsx +210 -0
  89. package/src/WebViewNativeComponent.macos.ts +7 -0
  90. package/src/WebViewNativeComponent.windows.ts +8 -0
  91. package/src/WebViewShared.tsx +283 -0
  92. package/src/WebViewTypes.ts +1332 -0
  93. package/src/__tests__/WebViewShared-test.js +323 -0
  94. package/src/__tests__/__snapshots__/WebViewShared-test.js.snap +8 -0
  95. package/src/index.ts +4 -0
  96. package/windows/ExperimentalFeatures.props +11 -0
  97. package/windows/ReactNativeWebView/PropertySheet.props +16 -0
  98. package/windows/ReactNativeWebView/ReactNativeWebView.def +3 -0
  99. package/windows/ReactNativeWebView/ReactNativeWebView.filters +33 -0
  100. package/windows/ReactNativeWebView/ReactNativeWebView.vcxproj +189 -0
  101. package/windows/ReactNativeWebView/ReactPackageProvider.cpp +26 -0
  102. package/windows/ReactNativeWebView/ReactPackageProvider.h +20 -0
  103. package/windows/ReactNativeWebView/ReactPackageProvider.idl +7 -0
  104. package/windows/ReactNativeWebView/ReactWebView.cpp +242 -0
  105. package/windows/ReactNativeWebView/ReactWebView.h +56 -0
  106. package/windows/ReactNativeWebView/ReactWebView.idl +31 -0
  107. package/windows/ReactNativeWebView/ReactWebView2.cpp +473 -0
  108. package/windows/ReactNativeWebView/ReactWebView2.h +91 -0
  109. package/windows/ReactNativeWebView/ReactWebView2Manager.cpp +205 -0
  110. package/windows/ReactNativeWebView/ReactWebView2Manager.h +57 -0
  111. package/windows/ReactNativeWebView/ReactWebViewHelpers.cpp +70 -0
  112. package/windows/ReactNativeWebView/ReactWebViewHelpers.h +16 -0
  113. package/windows/ReactNativeWebView/ReactWebViewManager.cpp +224 -0
  114. package/windows/ReactNativeWebView/ReactWebViewManager.h +58 -0
  115. package/windows/ReactNativeWebView/WebBridge.cpp +19 -0
  116. package/windows/ReactNativeWebView/WebBridge.h +24 -0
  117. package/windows/ReactNativeWebView/WebBridge.idl +11 -0
  118. package/windows/ReactNativeWebView/pch.cpp +1 -0
  119. package/windows/ReactNativeWebView/pch.h +37 -0
  120. package/windows/ReactNativeWebView.sln +185 -0
@@ -0,0 +1,1979 @@
1
+ /**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #import "RNCWebViewImpl.h"
9
+ #import <React/RCTConvert.h>
10
+ #import <React/RCTAutoInsetsProtocol.h>
11
+ #import "RNCWKProcessPoolManager.h"
12
+ #if !TARGET_OS_OSX
13
+ #import <UIKit/UIKit.h>
14
+ #else
15
+ #import <React/RCTUIKit.h>
16
+ #endif // !TARGET_OS_OSX
17
+
18
+ #import "objc/runtime.h"
19
+
20
+ static NSTimer *keyboardTimer;
21
+ static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
22
+ static NSString *const MessageHandlerName = @"ReactNativeWebView";
23
+ static NSURLCredential* clientAuthenticationCredential;
24
+ static NSDictionary* customCertificatesForHost;
25
+
26
+ NSString *const CUSTOM_SELECTOR = @"_CUSTOM_SELECTOR_";
27
+
28
+ #if TARGET_OS_IOS
29
+ // runtime trick to remove WKWebView keyboard default toolbar
30
+ // see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279
31
+ @interface _SwizzleHelperWK : UIView
32
+ @property (nonatomic, copy) WKWebView *webView;
33
+ @end
34
+ @implementation _SwizzleHelperWK
35
+ -(id)inputAccessoryView
36
+ {
37
+ if (_webView == nil) {
38
+ return nil;
39
+ }
40
+
41
+ if ([_webView respondsToSelector:@selector(inputAssistantItem)]) {
42
+ UITextInputAssistantItem *inputAssistantItem = [_webView inputAssistantItem];
43
+ inputAssistantItem.leadingBarButtonGroups = @[];
44
+ inputAssistantItem.trailingBarButtonGroups = @[];
45
+ }
46
+ return nil;
47
+ }
48
+ @end
49
+ #endif // TARGET_OS_IOS
50
+
51
+ @interface RNCWKWebView : WKWebView
52
+ #if !TARGET_OS_OSX
53
+ @property (nonatomic, copy) NSArray<NSDictionary *> * _Nullable menuItems;
54
+ @property (nonatomic, copy) NSArray<NSString *> * _Nullable suppressMenuItems;
55
+ #endif // !TARGET_OS_OSX
56
+ @end
57
+ @implementation RNCWKWebView
58
+ #if !TARGET_OS_OSX
59
+ - (NSString *)stringFromAction:(SEL) action {
60
+ NSString *sel = NSStringFromSelector(action);
61
+
62
+ NSDictionary *map = @{
63
+ @"cut:": @"cut",
64
+ @"copy:": @"copy",
65
+ @"paste:": @"paste",
66
+ @"delete:": @"delete",
67
+ @"select:": @"select",
68
+ @"selectAll:": @"selectAll",
69
+ @"_promptForReplace:": @"replace",
70
+ @"_define:": @"lookup",
71
+ @"_translate:": @"translate",
72
+ @"toggleBoldface:": @"bold",
73
+ @"toggleItalics:": @"italic",
74
+ @"toggleUnderline:": @"underline",
75
+ @"_share:": @"share",
76
+ };
77
+
78
+ return map[sel] ?: sel;
79
+ }
80
+
81
+ - (BOOL)canPerformAction:(SEL)action
82
+ withSender:(id)sender{
83
+ if(self.suppressMenuItems) {
84
+ NSString * sel = [self stringFromAction:action];
85
+ if ([self.suppressMenuItems containsObject: sel]) {
86
+ return NO;
87
+ }
88
+ }
89
+
90
+ if (!self.menuItems) {
91
+ return [super canPerformAction:action withSender:sender];
92
+ }
93
+
94
+ return NO;
95
+ }
96
+ - (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder API_AVAILABLE(ios(13.0)) {
97
+ if (@available(iOS 16.0, *)) {
98
+ if(self.menuItems){
99
+ [builder removeMenuForIdentifier:UIMenuLookup];
100
+ }
101
+ }
102
+ [super buildMenuWithBuilder:builder];
103
+ }
104
+ #else // TARGET_OS_OSX
105
+ - (void)scrollWheel:(NSEvent *)theEvent {
106
+ RNCWebViewImpl *rncWebView = (RNCWebViewImpl *)[self superview];
107
+ RCTAssert([rncWebView isKindOfClass:[rncWebView class]], @"superview must be an RNCWebViewImpl");
108
+ if (![rncWebView scrollEnabled]) {
109
+ [[self nextResponder] scrollWheel:theEvent];
110
+ return;
111
+ }
112
+ [super scrollWheel:theEvent];
113
+ }
114
+ #endif // TARGET_OS_OSX
115
+ @end
116
+
117
+ @interface RNCWebViewImpl () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, WKHTTPCookieStoreObserver,
118
+ #if !TARGET_OS_OSX
119
+ UIScrollViewDelegate,
120
+ UIGestureRecognizerDelegate,
121
+ #endif // !TARGET_OS_OSX
122
+ RCTAutoInsetsProtocol>
123
+
124
+ @property (nonatomic, copy) RNCWKWebView *webView;
125
+ @property (nonatomic, strong) WKUserScript *postMessageScript;
126
+ @property (nonatomic, strong) WKUserScript *injectedObjectJsonScript;
127
+ @property (nonatomic, strong) WKUserScript *atStartScript;
128
+ @property (nonatomic, strong) WKUserScript *atEndScript;
129
+ @property (nonatomic, strong) NSTimer *keepWebViewActiveTimer;
130
+ @end
131
+
132
+ @implementation RNCWebViewImpl
133
+ {
134
+ #if !TARGET_OS_OSX
135
+ UIColor * _savedBackgroundColor;
136
+ #else
137
+ RCTUIColor * _savedBackgroundColor;
138
+ #endif // !TARGET_OS_OSX
139
+ BOOL _savedHideKeyboardAccessoryView;
140
+ BOOL _savedKeyboardDisplayRequiresUserAction;
141
+
142
+ // Workaround for StatusBar appearance bug for iOS 12
143
+ // https://github.com/react-native-webview/react-native-webview/issues/62
144
+ BOOL _isFullScreenVideoOpen;
145
+ #if !TARGET_OS_OSX
146
+ UIStatusBarStyle _savedStatusBarStyle;
147
+ #endif // !TARGET_OS_OSX
148
+ BOOL _savedStatusBarHidden;
149
+
150
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
151
+ UIScrollViewContentInsetAdjustmentBehavior _savedContentInsetAdjustmentBehavior;
152
+ #endif
153
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
154
+ BOOL _savedAutomaticallyAdjustsScrollIndicatorInsets;
155
+ #endif
156
+ }
157
+
158
+ - (instancetype)initWithFrame:(CGRect)frame
159
+ {
160
+ if ((self = [super initWithFrame:frame])) {
161
+ #if !TARGET_OS_OSX
162
+ super.backgroundColor = [UIColor clearColor];
163
+ #else
164
+ super.backgroundColor = [RCTUIColor clearColor];
165
+ #endif // !TARGET_OS_OSX
166
+ _bounces = YES;
167
+ _scrollEnabled = YES;
168
+ _showsHorizontalScrollIndicator = YES;
169
+ _showsVerticalScrollIndicator = YES;
170
+ _directionalLockEnabled = YES;
171
+ _automaticallyAdjustContentInsets = YES;
172
+ _autoManageStatusBarEnabled = YES;
173
+ _contentInset = UIEdgeInsetsZero;
174
+ _savedKeyboardDisplayRequiresUserAction = YES;
175
+ #if TARGET_OS_IOS
176
+ _savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
177
+ _savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
178
+ #endif // TARGET_OS_IOS
179
+ _injectedJavaScript = nil;
180
+ _injectedJavaScriptForMainFrameOnly = YES;
181
+ _injectedJavaScriptBeforeContentLoaded = nil;
182
+ _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
183
+
184
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
185
+ _savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
186
+ #endif
187
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
188
+ _savedAutomaticallyAdjustsScrollIndicatorInsets = NO;
189
+ #endif
190
+ _enableApplePay = NO;
191
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 /* iOS 15 */
192
+ _mediaCapturePermissionGrantType = RNCWebViewPermissionGrantType_Prompt;
193
+ #endif
194
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000 /* iOS 15 */
195
+ if (@available(iOS 16.0, *)) {
196
+ _editMenuInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self];
197
+ [self addInteraction:_editMenuInteraction];
198
+ }
199
+ #endif
200
+ }
201
+
202
+ #if TARGET_OS_IOS
203
+ [[NSNotificationCenter defaultCenter]addObserver:self
204
+ selector:@selector(appDidBecomeActive)
205
+ name:UIApplicationDidBecomeActiveNotification
206
+ object:nil];
207
+
208
+ [[NSNotificationCenter defaultCenter]addObserver:self
209
+ selector:@selector(appWillResignActive)
210
+ name:UIApplicationWillResignActiveNotification
211
+ object:nil];
212
+ if (@available(iOS 12.0, *)) {
213
+ // Workaround for a keyboard dismissal bug present in iOS 12
214
+ // https://openradar.appspot.com/radar?id=5018321736957952
215
+ [[NSNotificationCenter defaultCenter]
216
+ addObserver:self
217
+ selector:@selector(keyboardWillHide)
218
+ name:UIKeyboardWillHideNotification object:nil];
219
+ [[NSNotificationCenter defaultCenter]
220
+ addObserver:self
221
+ selector:@selector(keyboardWillShow)
222
+ name:UIKeyboardWillShowNotification object:nil];
223
+
224
+ // Workaround for StatusBar appearance bug for iOS 12
225
+ // https://github.com/react-native-webview/react-native-webview/issues/62
226
+ [[NSNotificationCenter defaultCenter] addObserver:self
227
+ selector:@selector(showFullScreenVideoStatusBars)
228
+ name:UIWindowDidBecomeVisibleNotification
229
+ object:nil];
230
+
231
+ [[NSNotificationCenter defaultCenter] addObserver:self
232
+ selector:@selector(hideFullScreenVideoStatusBars)
233
+ name:UIWindowDidBecomeHiddenNotification
234
+ object:nil];
235
+
236
+ }
237
+ #endif // TARGET_OS_IOS
238
+ return self;
239
+ }
240
+
241
+ #if !TARGET_OS_OSX
242
+ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
243
+ // Only allow long press gesture
244
+ if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
245
+ return YES;
246
+ }else{
247
+ return NO;
248
+ }
249
+ }
250
+
251
+ // Listener for long presses
252
+ - (void)startLongPress:(UILongPressGestureRecognizer *)pressSender
253
+ {
254
+ if (pressSender.state != UIGestureRecognizerStateEnded || !self.menuItems) {
255
+ return;
256
+ }
257
+ if (@available(iOS 16.0, *)) {
258
+ CGPoint location = [pressSender locationInView:self];
259
+ UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location];
260
+ [_editMenuInteraction presentEditMenuWithConfiguration:config];
261
+ } else {
262
+ // When a long press ends, bring up our custom UIMenu if defined
263
+ if (self.menuItems.count == 0) {
264
+ UIMenuController *menuController = [UIMenuController sharedMenuController];
265
+ menuController.menuItems = nil;
266
+ [menuController showMenuFromView:self rect:self.bounds];
267
+ return;
268
+ }
269
+
270
+ UIMenuController *menuController = [UIMenuController sharedMenuController];
271
+ NSMutableArray *menuControllerItems = [NSMutableArray arrayWithCapacity:self.menuItems.count];
272
+
273
+ for(NSDictionary *menuItem in self.menuItems) {
274
+ NSString *menuItemLabel = [RCTConvert NSString:menuItem[@"label"]];
275
+ NSString *menuItemKey = [RCTConvert NSString:menuItem[@"key"]];
276
+ NSString *sel = [NSString stringWithFormat:@"%@%@", CUSTOM_SELECTOR, menuItemKey];
277
+ UIMenuItem *item = [[UIMenuItem alloc] initWithTitle: menuItemLabel
278
+ action: NSSelectorFromString(sel)];
279
+ [menuControllerItems addObject: item];
280
+ }
281
+ menuController.menuItems = menuControllerItems;
282
+ [menuController showMenuFromView:self rect:self.bounds];
283
+ }
284
+ }
285
+
286
+ - (UIMenu *)editMenuInteraction:(UIEditMenuInteraction *)interaction menuForConfiguration:(UIEditMenuConfiguration *)configuration suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
287
+ {
288
+ NSMutableArray<UICommand *> *menuItems = [NSMutableArray new];
289
+ for(NSDictionary *menuItem in self.menuItems) {
290
+ NSString *menuItemLabel = [RCTConvert NSString:menuItem[@"label"]];
291
+ NSString *menuItemKey = [RCTConvert NSString:menuItem[@"key"]];
292
+ NSString *sel = [NSString stringWithFormat:@"%@%@", CUSTOM_SELECTOR, menuItemKey];
293
+ UICommand *command = [UICommand commandWithTitle:menuItemLabel
294
+ image:nil
295
+ action:NSSelectorFromString(sel)
296
+ propertyList:nil];
297
+ [menuItems addObject:command];
298
+ }
299
+ UIMenu *menu = [UIMenu menuWithChildren:menuItems];
300
+ return menu;
301
+ }
302
+ #endif // !TARGET_OS_OSX
303
+
304
+ - (void)dealloc
305
+ {
306
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
307
+ if (@available(iOS 11.0, *)) {
308
+ [self.webView.configuration.websiteDataStore.httpCookieStore removeObserver:self];
309
+ }
310
+ }
311
+
312
+ - (void)tappedMenuItem:(NSString *)eventType
313
+ {
314
+ // Get the selected text
315
+ // NOTE: selecting text in an iframe or shadow DOM will not work
316
+ [self.webView evaluateJavaScript: @"window.getSelection().toString()" completionHandler: ^(id result, NSError *error) {
317
+ if (error != nil) {
318
+ 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]);
319
+ } else {
320
+ if (self.onCustomMenuSelection) {
321
+ NSPredicate *filter = [NSPredicate predicateWithFormat:@"key contains[c] %@ ",eventType];
322
+ NSArray *filteredMenuItems = [self.menuItems filteredArrayUsingPredicate:filter];
323
+ NSDictionary *selectedMenuItem = filteredMenuItems[0];
324
+ NSString *label = [RCTConvert NSString:selectedMenuItem[@"label"]];
325
+ self.onCustomMenuSelection(@{
326
+ @"key": eventType,
327
+ @"label": label,
328
+ @"selectedText": result
329
+ });
330
+ } else {
331
+ RCTLogWarn(@"Error evaluating onCustomMenuSelection: You must implement an `onCustomMenuSelection` callback when using custom menu items");
332
+ }
333
+ }
334
+ }];
335
+ }
336
+
337
+ // Overwrite method that interprets which action to call upon UIMenu Selection
338
+ // https://developer.apple.com/documentation/objectivec/nsobject/1571960-methodsignatureforselector
339
+ - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
340
+ {
341
+ NSMethodSignature *existingSelector = [super methodSignatureForSelector:sel];
342
+ if (existingSelector) {
343
+ return existingSelector;
344
+ }
345
+ return [super methodSignatureForSelector:@selector(tappedMenuItem:)];
346
+ }
347
+
348
+ // Needed to forward messages to other objects
349
+ // https://developer.apple.com/documentation/objectivec/nsobject/1571955-forwardinvocation
350
+ - (void)forwardInvocation:(NSInvocation *)invocation
351
+ {
352
+ NSString *sel = NSStringFromSelector([invocation selector]);
353
+ NSRange match = [sel rangeOfString:CUSTOM_SELECTOR];
354
+ if (match.location == 0) {
355
+ [self tappedMenuItem:[sel substringFromIndex:17]];
356
+ } else {
357
+ [super forwardInvocation:invocation];
358
+ }
359
+ }
360
+
361
+ // Allows the instance to respond to UIMenuController Actions
362
+ - (BOOL)canBecomeFirstResponder
363
+ {
364
+ return YES;
365
+ }
366
+
367
+ // Control which items show up on the UIMenuController
368
+ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
369
+ {
370
+ NSString *sel = NSStringFromSelector(action);
371
+ // Do any of them have our custom keys?
372
+ NSRange match = [sel rangeOfString:CUSTOM_SELECTOR];
373
+
374
+ if (match.location == 0) {
375
+ return YES;
376
+ }
377
+ return NO;
378
+ }
379
+
380
+ /**
381
+ * See https://stackoverflow.com/questions/25713069/why-is-wkwebview-not-opening-links-with-target-blank/25853806#25853806 for details.
382
+ */
383
+ - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
384
+ {
385
+ if (!navigationAction.targetFrame.isMainFrame) {
386
+ NSURL *url = navigationAction.request.URL;
387
+
388
+ if (_onOpenWindow) {
389
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
390
+ [event addEntriesFromDictionary: @{@"targetUrl": url.absoluteString}];
391
+ _onOpenWindow(event);
392
+ } else {
393
+ [webView loadRequest:navigationAction.request];
394
+ }
395
+ }
396
+ return nil;
397
+ }
398
+
399
+ /**
400
+ * Enables file input on macos, see https://developer.apple.com/documentation/webkit/wkuidelegate/1641952-webview
401
+ */
402
+ #if TARGET_OS_OSX
403
+ - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler
404
+ {
405
+ NSOpenPanel *openPanel = [NSOpenPanel openPanel];
406
+ openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
407
+ [openPanel beginSheetModalForWindow:webView.window completionHandler:^(NSInteger result) {
408
+ if (result == NSModalResponseOK)
409
+ completionHandler(openPanel.URLs);
410
+ else
411
+ completionHandler(nil);
412
+ }];
413
+ }
414
+ #endif //Target_OS_OSX
415
+
416
+ - (WKWebViewConfiguration *)setUpWkWebViewConfig
417
+ {
418
+ WKWebViewConfiguration *wkWebViewConfig = [WKWebViewConfiguration new];
419
+ WKPreferences *prefs = [[WKPreferences alloc]init];
420
+ BOOL _prefsUsed = NO;
421
+ if (!_javaScriptEnabled) {
422
+ prefs.javaScriptEnabled = NO;
423
+ _prefsUsed = YES;
424
+ }
425
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
426
+ if (@available(iOS 13.0, *)) {
427
+ if (!_fraudulentWebsiteWarningEnabled) {
428
+ prefs.fraudulentWebsiteWarningEnabled = NO;
429
+ _prefsUsed = YES;
430
+ }
431
+ }
432
+ #endif
433
+ if (_allowUniversalAccessFromFileURLs) {
434
+ [wkWebViewConfig setValue:@TRUE forKey:@"allowUniversalAccessFromFileURLs"];
435
+ }
436
+ if (_allowFileAccessFromFileURLs) {
437
+ [prefs setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"];
438
+ _prefsUsed = YES;
439
+ }
440
+ if (_javaScriptCanOpenWindowsAutomatically) {
441
+ [prefs setValue:@TRUE forKey:@"javaScriptCanOpenWindowsAutomatically"];
442
+ _prefsUsed = YES;
443
+ }
444
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140500 /* iOS 14.5 */
445
+ if (@available(iOS 14.5, *)) {
446
+ if (!_textInteractionEnabled) {
447
+ [prefs setValue:@FALSE forKey:@"textInteractionEnabled"];
448
+ _prefsUsed = YES;
449
+ }
450
+ }
451
+ #endif
452
+ if (_prefsUsed) {
453
+ wkWebViewConfig.preferences = prefs;
454
+ }
455
+ if (_incognito) {
456
+ wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
457
+ } else if (_cacheEnabled) {
458
+ wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
459
+ }
460
+ if(self.useSharedProcessPool) {
461
+ wkWebViewConfig.processPool = [[RNCWKProcessPoolManager sharedManager] sharedProcessPool];
462
+ }
463
+ wkWebViewConfig.userContentController = [WKUserContentController new];
464
+
465
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
466
+ if (@available(iOS 13.0, *)) {
467
+ WKWebpagePreferences *pagePrefs = [[WKWebpagePreferences alloc]init];
468
+ pagePrefs.preferredContentMode = _contentMode;
469
+ wkWebViewConfig.defaultWebpagePreferences = pagePrefs;
470
+ }
471
+ #endif
472
+
473
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 /* iOS 14 */
474
+ if (@available(iOS 14.0, *)) {
475
+ if ([wkWebViewConfig respondsToSelector:@selector(limitsNavigationsToAppBoundDomains)]) {
476
+ if (_limitsNavigationsToAppBoundDomains) {
477
+ wkWebViewConfig.limitsNavigationsToAppBoundDomains = YES;
478
+ }
479
+ }
480
+ }
481
+ #endif
482
+
483
+ // Shim the HTML5 history API:
484
+ [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
485
+ name:HistoryShimName];
486
+ [self resetupScripts:wkWebViewConfig];
487
+
488
+ if(@available(macos 10.11, ios 9.0, *)) {
489
+ wkWebViewConfig.allowsAirPlayForMediaPlayback = _allowsAirPlayForMediaPlayback;
490
+ }
491
+
492
+ #if !TARGET_OS_OSX
493
+ wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
494
+ wkWebViewConfig.allowsPictureInPictureMediaPlayback = _allowsPictureInPictureMediaPlayback;
495
+ wkWebViewConfig.mediaTypesRequiringUserActionForPlayback = _mediaPlaybackRequiresUserAction
496
+ ? WKAudiovisualMediaTypeAll
497
+ : WKAudiovisualMediaTypeNone;
498
+ wkWebViewConfig.dataDetectorTypes = _dataDetectorTypes;
499
+ #endif // !TARGET_OS_OSX
500
+
501
+ if (_applicationNameForUserAgent) {
502
+ wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
503
+ }
504
+
505
+ NSURL *jsonFileURL = [[NSBundle mainBundle] URLForResource:@"rulelist" withExtension:@"json"];
506
+
507
+ if (jsonFileURL) {
508
+ NSError *error = nil;
509
+ NSString *jsonRules = [NSString stringWithContentsOfURL:jsonFileURL
510
+ encoding:NSUTF8StringEncoding
511
+ error:&error];
512
+
513
+ if (jsonRules && jsonRules.length > 0) {
514
+ WKContentRuleListStore *store = [WKContentRuleListStore defaultStore];
515
+
516
+ if (store) {
517
+ [store compileContentRuleListForIdentifier:@"rules-identifier"
518
+ encodedContentRuleList:jsonRules
519
+ completionHandler:^(WKContentRuleList * _Nullable contentRuleList, NSError * _Nullable error) {
520
+ if (contentRuleList && wkWebViewConfig.userContentController) {
521
+ [wkWebViewConfig.userContentController addContentRuleList:contentRuleList];
522
+ }
523
+ }];
524
+ }
525
+ }
526
+ }
527
+
528
+ return wkWebViewConfig;
529
+ }
530
+
531
+ - (void)_keepWKWebViewActive:(NSTimer*) timer {
532
+ if (_webView) {
533
+ [_webView evaluateJavaScript:@"1+1" completionHandler:nil];
534
+ }
535
+ }
536
+
537
+ - (void)didMoveToWindow
538
+ {
539
+ if (self.window != nil && _webView == nil) {
540
+ WKWebViewConfiguration *wkWebViewConfig = [self setUpWkWebViewConfig];
541
+ _webView = [[RNCWKWebView alloc] initWithFrame:self.bounds configuration: wkWebViewConfig];
542
+ [self setBackgroundColor: _savedBackgroundColor];
543
+ #if !TARGET_OS_OSX
544
+ _webView.menuItems = _menuItems;
545
+ _webView.suppressMenuItems = _suppressMenuItems;
546
+ _webView.scrollView.delegate = self;
547
+ #endif // !TARGET_OS_OSX
548
+ _webView.UIDelegate = self;
549
+ _webView.navigationDelegate = self;
550
+ #if !TARGET_OS_OSX
551
+ if (_pullToRefreshEnabled) {
552
+ [self addPullToRefreshControl];
553
+ }
554
+ _webView.scrollView.scrollEnabled = _scrollEnabled;
555
+ _webView.scrollView.pagingEnabled = _pagingEnabled;
556
+ //For UIRefreshControl to work correctly, the bounces should always be true
557
+ _webView.scrollView.bounces = _pullToRefreshEnabled || _bounces;
558
+ _webView.scrollView.showsHorizontalScrollIndicator = _showsHorizontalScrollIndicator;
559
+ _webView.scrollView.showsVerticalScrollIndicator = _showsVerticalScrollIndicator;
560
+ _webView.scrollView.directionalLockEnabled = _directionalLockEnabled;
561
+ #endif // !TARGET_OS_OSX
562
+ _webView.allowsLinkPreview = _allowsLinkPreview;
563
+ [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
564
+ _webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures;
565
+
566
+ _webView.customUserAgent = _userAgent;
567
+
568
+ #if !TARGET_OS_OSX
569
+ if ([_webView.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
570
+ _webView.scrollView.contentInsetAdjustmentBehavior = _savedContentInsetAdjustmentBehavior;
571
+ }
572
+ #endif // !TARGET_OS_OSX
573
+
574
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
575
+ if (@available(iOS 13.0, *)) {
576
+ _webView.scrollView.automaticallyAdjustsScrollIndicatorInsets = _savedAutomaticallyAdjustsScrollIndicatorInsets;
577
+ }
578
+ #endif
579
+
580
+ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130300 || \
581
+ __IPHONE_OS_VERSION_MAX_ALLOWED >= 160400 || \
582
+ __TV_OS_VERSION_MAX_ALLOWED >= 160400
583
+ if (@available(macOS 13.3, iOS 16.4, tvOS 16.4, *))
584
+ _webView.inspectable = _webviewDebuggingEnabled;
585
+ #endif
586
+
587
+ [self addSubview:_webView];
588
+ [self setHideKeyboardAccessoryView: _savedHideKeyboardAccessoryView];
589
+ [self setKeyboardDisplayRequiresUserAction: _savedKeyboardDisplayRequiresUserAction];
590
+ [self visitSource];
591
+
592
+ self.keepWebViewActiveTimer = [NSTimer scheduledTimerWithTimeInterval:0.2
593
+ target:self
594
+ selector:@selector(_keepWKWebViewActive:)
595
+ userInfo:nil
596
+ repeats:YES];
597
+ }
598
+
599
+ #if !TARGET_OS_OSX
600
+ // Allow this object to recognize gestures
601
+ if (self.menuItems != nil) {
602
+ UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(startLongPress:)];
603
+ longPress.delegate = self;
604
+
605
+ longPress.minimumPressDuration = 0.4f;
606
+ longPress.numberOfTouchesRequired = 1;
607
+ longPress.cancelsTouchesInView = YES;
608
+ [self addGestureRecognizer:longPress];
609
+ }
610
+ #endif // !TARGET_OS_OSX
611
+ }
612
+
613
+ // Update webview property when the component prop changes.
614
+ - (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures {
615
+ _allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
616
+ _webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures;
617
+ }
618
+
619
+ #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 130300 || \
620
+ __IPHONE_OS_VERSION_MAX_ALLOWED >= 160400 || \
621
+ __TV_OS_VERSION_MAX_ALLOWED >= 160400
622
+ - (void)setWebviewDebuggingEnabled:(BOOL)webviewDebuggingEnabled {
623
+ _webviewDebuggingEnabled = webviewDebuggingEnabled;
624
+ if (@available(macOS 13.3, iOS 16.4, tvOS 16.4, *))
625
+ _webView.inspectable = _webviewDebuggingEnabled;
626
+ }
627
+ #endif
628
+
629
+ #ifdef RCT_NEW_ARCH_ENABLED
630
+ - (void)destroyWebView
631
+ #else
632
+ - (void)removeFromSuperview
633
+ #endif
634
+ {
635
+ if (_webView) {
636
+ [_webView.configuration.userContentController removeScriptMessageHandlerForName:HistoryShimName];
637
+ [_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
638
+ [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
639
+ [_webView removeFromSuperview];
640
+ if (@available(iOS 15.0, macOS 12.0, *)) {
641
+ [_webView pauseAllMediaPlaybackWithCompletionHandler:nil];
642
+ } else if (@available(iOS 14.5, macOS 11.3, *)) {
643
+ [_webView suspendAllMediaPlayback:nil];
644
+ }
645
+ #if !TARGET_OS_OSX
646
+ _webView.scrollView.delegate = nil;
647
+ if (_menuItems) {
648
+ UIMenuController *menuController = [UIMenuController sharedMenuController];
649
+ menuController.menuItems = nil;
650
+ }
651
+ #endif // !TARGET_OS_OSX
652
+ _webView = nil;
653
+ if (_onContentProcessDidTerminate) {
654
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
655
+ _onContentProcessDidTerminate(event);
656
+ }
657
+ }
658
+
659
+ #ifndef RCT_NEW_ARCH_ENABLED
660
+ [super removeFromSuperview];
661
+ #endif
662
+ }
663
+
664
+ #if TARGET_OS_IOS
665
+ -(void)showFullScreenVideoStatusBars
666
+ {
667
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
668
+ if (!_autoManageStatusBarEnabled) {
669
+ return;
670
+ }
671
+
672
+ _isFullScreenVideoOpen = YES;
673
+ RCTUnsafeExecuteOnMainQueueSync(^{
674
+ [RCTSharedApplication() setStatusBarStyle:self->_savedStatusBarStyle animated:YES];
675
+ });
676
+ #pragma clang diagnostic pop
677
+ }
678
+
679
+ -(void)hideFullScreenVideoStatusBars
680
+ {
681
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
682
+ if (!_autoManageStatusBarEnabled) {
683
+ return;
684
+ }
685
+
686
+ _isFullScreenVideoOpen = NO;
687
+ RCTUnsafeExecuteOnMainQueueSync(^{
688
+ [RCTSharedApplication() setStatusBarHidden:self->_savedStatusBarHidden animated:YES];
689
+ [RCTSharedApplication() setStatusBarStyle:self->_savedStatusBarStyle animated:YES];
690
+ });
691
+ #pragma clang diagnostic pop
692
+ }
693
+
694
+ -(void)keyboardWillHide
695
+ {
696
+ keyboardTimer = [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(keyboardDisplacementFix) userInfo:nil repeats:false];
697
+ [[NSRunLoop mainRunLoop] addTimer:keyboardTimer forMode:NSRunLoopCommonModes];
698
+ }
699
+ -(void)keyboardWillShow
700
+ {
701
+ if (keyboardTimer != nil) {
702
+ [keyboardTimer invalidate];
703
+ }
704
+ }
705
+ -(void)keyboardDisplacementFix
706
+ {
707
+ // Additional viewport checks to prevent unintentional scrolls
708
+ UIScrollView *scrollView = self.webView.scrollView;
709
+ double maxContentOffset = scrollView.contentSize.height - scrollView.frame.size.height;
710
+ if (maxContentOffset < 0) {
711
+ maxContentOffset = 0;
712
+ }
713
+ if (scrollView.contentOffset.y > maxContentOffset) {
714
+ // https://stackoverflow.com/a/9637807/824966
715
+ [UIView animateWithDuration:.25 animations:^{
716
+ scrollView.contentOffset = CGPointMake(0, maxContentOffset);
717
+ }];
718
+ }
719
+ }
720
+ #endif // TARGET_OS_IOS
721
+
722
+ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
723
+ if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
724
+ if(_onLoadingProgress){
725
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
726
+ [event addEntriesFromDictionary:@{@"progress":[NSNumber numberWithDouble:self.webView.estimatedProgress]}];
727
+ _onLoadingProgress(event);
728
+ }
729
+ }else{
730
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
731
+ }
732
+ }
733
+
734
+ #if !TARGET_OS_OSX
735
+ - (void)setBackgroundColor:(UIColor *)backgroundColor
736
+ #else
737
+ - (void)setBackgroundColor:(RCTUIColor *)backgroundColor
738
+ #endif // !TARGET_OS_OSX
739
+ {
740
+ _savedBackgroundColor = backgroundColor;
741
+ if (_webView == nil) {
742
+ return;
743
+ }
744
+
745
+ CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
746
+ BOOL opaque = (alpha == 1.0);
747
+ #if !TARGET_OS_OSX
748
+ self.opaque = _webView.opaque = opaque;
749
+ _webView.scrollView.backgroundColor = backgroundColor;
750
+ _webView.backgroundColor = backgroundColor;
751
+ #else
752
+ // https://stackoverflow.com/questions/40007753/macos-wkwebview-background-transparency
753
+ NSOperatingSystemVersion version = { 10, 12, 0 };
754
+ if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) {
755
+ [_webView setValue:@(opaque) forKey: @"drawsBackground"];
756
+ } else {
757
+ [_webView setValue:@(!opaque) forKey: @"drawsTransparentBackground"];
758
+ }
759
+ #endif // !TARGET_OS_OSX
760
+ }
761
+
762
+ #if !TARGET_OS_OSX
763
+ - (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBehavior)behavior
764
+ {
765
+ _savedContentInsetAdjustmentBehavior = behavior;
766
+ if (_webView == nil) {
767
+ return;
768
+ }
769
+
770
+ if ([_webView.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
771
+ CGPoint contentOffset = _webView.scrollView.contentOffset;
772
+ _webView.scrollView.contentInsetAdjustmentBehavior = behavior;
773
+ _webView.scrollView.contentOffset = contentOffset;
774
+ }
775
+ }
776
+ #endif // !TARGET_OS_OSX
777
+
778
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
779
+ - (void)setAutomaticallyAdjustsScrollIndicatorInsets:(BOOL)automaticallyAdjustsScrollIndicatorInsets{
780
+ _savedAutomaticallyAdjustsScrollIndicatorInsets = automaticallyAdjustsScrollIndicatorInsets;
781
+ if (_webView == nil) {
782
+ return;
783
+ }
784
+ if ([_webView.scrollView respondsToSelector:@selector(setAutomaticallyAdjustsScrollIndicatorInsets:)]) {
785
+ _webView.scrollView.automaticallyAdjustsScrollIndicatorInsets = automaticallyAdjustsScrollIndicatorInsets;
786
+ }
787
+ }
788
+ #endif
789
+
790
+ /**
791
+ * This method is called whenever JavaScript running within the web view calls:
792
+ * - window.webkit.messageHandlers[MessageHandlerName].postMessage
793
+ */
794
+ - (void)userContentController:(WKUserContentController *)userContentController
795
+ didReceiveScriptMessage:(WKScriptMessage *)message
796
+ {
797
+ if ([message.name isEqualToString:HistoryShimName]) {
798
+ if (_onLoadingFinish) {
799
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
800
+ [event addEntriesFromDictionary: @{@"navigationType": message.body}];
801
+ _onLoadingFinish(event);
802
+ }
803
+ } else if ([message.name isEqualToString:MessageHandlerName]) {
804
+ if (_onMessage) {
805
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
806
+ [event addEntriesFromDictionary: @{@"data": message.body}];
807
+ _onMessage(event);
808
+ }
809
+ }
810
+ }
811
+
812
+ - (void)setSource:(NSDictionary *)source
813
+ {
814
+ if (![_source isEqualToDictionary:source]) {
815
+ _source = [source copy];
816
+
817
+ if (_webView != nil) {
818
+ [self visitSource];
819
+ }
820
+ }
821
+ }
822
+
823
+ - (void)setAllowingReadAccessToURL:(NSString *)allowingReadAccessToURL
824
+ {
825
+ if (![_allowingReadAccessToURL isEqualToString:allowingReadAccessToURL]) {
826
+ _allowingReadAccessToURL = [allowingReadAccessToURL copy];
827
+
828
+ if (_webView != nil) {
829
+ [self visitSource];
830
+ }
831
+ }
832
+ }
833
+
834
+ #if !TARGET_OS_OSX
835
+ - (void)setContentInset:(UIEdgeInsets)contentInset
836
+ {
837
+ _contentInset = contentInset;
838
+ [RCTView autoAdjustInsetsForView:self
839
+ withScrollView:_webView.scrollView
840
+ updateOffset:NO];
841
+ }
842
+
843
+ - (void)refreshContentInset
844
+ {
845
+ [RCTView autoAdjustInsetsForView:self
846
+ withScrollView:_webView.scrollView
847
+ updateOffset:YES];
848
+ }
849
+ #endif // !TARGET_OS_OSX
850
+
851
+ - (void)visitSource
852
+ {
853
+ // Check for a static html source first
854
+ NSString *html = [RCTConvert NSString:_source[@"html"]];
855
+ if (html) {
856
+ NSURL *baseURL = [RCTConvert NSURL:_source[@"baseUrl"]];
857
+ if (!baseURL) {
858
+ baseURL = [NSURL URLWithString:@"about:blank"];
859
+ }
860
+ [_webView loadHTMLString:html baseURL:baseURL];
861
+ return;
862
+ }
863
+ // Add cookie for subsequent resource requests sent by page itself, if cookie was set in headers on WebView
864
+ NSString *headerCookie = [RCTConvert NSString:_source[@"headers"][@"cookie"]];
865
+ if(headerCookie) {
866
+ NSDictionary *headers = [NSDictionary dictionaryWithObjectsAndKeys:headerCookie,@"Set-Cookie",nil];
867
+ NSURL *urlString = [NSURL URLWithString:_source[@"uri"]];
868
+ NSArray *httpCookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:urlString];
869
+ [self writeCookiesToWebView:httpCookies completion:nil];
870
+ }
871
+
872
+ NSURLRequest *request = [self requestForSource:_source];
873
+ __weak WKWebView *webView = _webView;
874
+ NSString *allowingReadAccessToURL = _allowingReadAccessToURL;
875
+
876
+ [self syncCookiesToWebView:^{
877
+ // Add observer to sync cookies from webview to sharedHTTPCookieStorage
878
+ [webView.configuration.websiteDataStore.httpCookieStore addObserver:self];
879
+
880
+ // Because of the way React works, as pages redirect, we actually end up
881
+ // passing the redirect urls back here, so we ignore them if trying to load
882
+ // the same url. We'll expose a call to 'reload' to allow a user to load
883
+ // the existing page.
884
+ if ([request.URL isEqual:webView.URL]) {
885
+ return;
886
+ }
887
+ if (!request.URL) {
888
+ // Clear the webview
889
+ [webView loadHTMLString:@"" baseURL:nil];
890
+ return;
891
+ }
892
+ if (request.URL.host) {
893
+ [webView loadRequest:request];
894
+ }
895
+ else {
896
+ NSURL* readAccessUrl = allowingReadAccessToURL ? [RCTConvert NSURL:allowingReadAccessToURL] : request.URL;
897
+ [webView loadFileURL:request.URL allowingReadAccessToURL:readAccessUrl];
898
+ }
899
+ }];
900
+ }
901
+
902
+ #if !TARGET_OS_OSX
903
+ -(void)setMenuItems:(NSArray<NSDictionary *> *)menuItems {
904
+ _menuItems = menuItems;
905
+ _webView.menuItems = menuItems;
906
+ }
907
+
908
+ -(void)setSuppressMenuItems:(NSArray<NSString *> *)suppressMenuItems {
909
+ _suppressMenuItems = suppressMenuItems;
910
+ _webView.suppressMenuItems = suppressMenuItems;
911
+ }
912
+
913
+ #if TARGET_OS_IOS
914
+ -(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAction
915
+ {
916
+ if (_webView == nil) {
917
+ _savedKeyboardDisplayRequiresUserAction = keyboardDisplayRequiresUserAction;
918
+ return;
919
+ }
920
+
921
+ if (_savedKeyboardDisplayRequiresUserAction == true) {
922
+ return;
923
+ }
924
+
925
+ UIView* subview;
926
+
927
+ for (UIView* view in _webView.scrollView.subviews) {
928
+ if([[view.class description] hasPrefix:@"WK"])
929
+ subview = view;
930
+ }
931
+
932
+ if(subview == nil) return;
933
+
934
+ Class class = subview.class;
935
+
936
+ NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0};
937
+ NSOperatingSystemVersion iOS_12_2_0 = (NSOperatingSystemVersion){12, 2, 0};
938
+ NSOperatingSystemVersion iOS_13_0_0 = (NSOperatingSystemVersion){13, 0, 0};
939
+
940
+ Method method;
941
+ IMP override;
942
+
943
+ if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_13_0_0]) {
944
+ // iOS 13.0.0 - Future
945
+ SEL selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:");
946
+ method = class_getInstanceMethod(class, selector);
947
+ IMP original = method_getImplementation(method);
948
+ override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
949
+ ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
950
+ });
951
+ }
952
+ else if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_12_2_0]) {
953
+ // iOS 12.2.0 - iOS 13.0.0
954
+ SEL selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
955
+ method = class_getInstanceMethod(class, selector);
956
+ IMP original = method_getImplementation(method);
957
+ override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
958
+ ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
959
+ });
960
+ }
961
+ else if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) {
962
+ // iOS 11.3.0 - 12.2.0
963
+ SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
964
+ method = class_getInstanceMethod(class, selector);
965
+ IMP original = method_getImplementation(method);
966
+ override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
967
+ ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
968
+ });
969
+ } else {
970
+ // iOS 9.0 - 11.3.0
971
+ SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");
972
+ method = class_getInstanceMethod(class, selector);
973
+ IMP original = method_getImplementation(method);
974
+ override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) {
975
+ ((void (*)(id, SEL, void*, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3);
976
+ });
977
+ }
978
+
979
+ method_setImplementation(method, override);
980
+ }
981
+
982
+ -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
983
+ {
984
+ _savedHideKeyboardAccessoryView = hideKeyboardAccessoryView;
985
+
986
+ if (_webView == nil) {
987
+ return;
988
+ }
989
+
990
+ if (_savedHideKeyboardAccessoryView == false) {
991
+ [self __addInputAccessoryView];
992
+ return;
993
+ }
994
+
995
+ UIView* subview;
996
+
997
+ for (UIView* view in _webView.scrollView.subviews) {
998
+ if([[view.class description] hasPrefix:@"WK"])
999
+ subview = view;
1000
+ }
1001
+
1002
+ if(subview == nil) return;
1003
+
1004
+ NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelperWK", subview.class.superclass];
1005
+ Class newClass = NSClassFromString(name);
1006
+
1007
+ if(newClass == nil)
1008
+ {
1009
+ newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0);
1010
+ if(!newClass) return;
1011
+
1012
+ Method method = class_getInstanceMethod([_SwizzleHelperWK class], @selector(inputAccessoryView));
1013
+ class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method));
1014
+
1015
+ objc_registerClassPair(newClass);
1016
+ }
1017
+
1018
+ object_setClass(subview, newClass);
1019
+ }
1020
+ #endif // TARGET_OS_IOS
1021
+
1022
+ - (void)__addInputAccessoryView {
1023
+ UIView* subview;
1024
+
1025
+ for (UIView* view in _webView.scrollView.subviews) {
1026
+ if([[view.class description] hasSuffix:@"_SwizzleHelperWK"])
1027
+ subview = view;
1028
+ }
1029
+
1030
+ if(subview == nil) return;
1031
+
1032
+ Class newClass = subview.superclass;
1033
+
1034
+ object_setClass(subview, newClass);
1035
+ }
1036
+
1037
+ // UIScrollViewDelegate method
1038
+ - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
1039
+ {
1040
+ scrollView.decelerationRate = _decelerationRate;
1041
+ }
1042
+ #endif // !TARGET_OS_OSX
1043
+
1044
+ - (void)setUserAgent:(NSString*)userAgent
1045
+ {
1046
+ _userAgent = userAgent;
1047
+ _webView.customUserAgent = userAgent;
1048
+ }
1049
+
1050
+ - (void)setScrollEnabled:(BOOL)scrollEnabled
1051
+ {
1052
+ _scrollEnabled = scrollEnabled;
1053
+ #if !TARGET_OS_OSX
1054
+ _webView.scrollView.scrollEnabled = scrollEnabled;
1055
+ #endif // !TARGET_OS_OSX
1056
+ }
1057
+
1058
+ #if !TARGET_OS_OSX
1059
+ // UIScrollViewDelegate method
1060
+ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
1061
+ {
1062
+ // Don't allow scrolling the scrollView.
1063
+ if (!_scrollEnabled) {
1064
+ scrollView.bounds = _webView.bounds;
1065
+ }
1066
+ else if (_onScroll != nil) {
1067
+ NSDictionary *event = @{
1068
+ @"contentOffset": @{
1069
+ @"x": @(scrollView.contentOffset.x),
1070
+ @"y": @(scrollView.contentOffset.y)
1071
+ },
1072
+ @"contentInset": @{
1073
+ @"top": @(scrollView.contentInset.top),
1074
+ @"left": @(scrollView.contentInset.left),
1075
+ @"bottom": @(scrollView.contentInset.bottom),
1076
+ @"right": @(scrollView.contentInset.right)
1077
+ },
1078
+ @"contentSize": @{
1079
+ @"width": @(scrollView.contentSize.width),
1080
+ @"height": @(scrollView.contentSize.height)
1081
+ },
1082
+ @"layoutMeasurement": @{
1083
+ @"width": @(scrollView.frame.size.width),
1084
+ @"height": @(scrollView.frame.size.height)
1085
+ },
1086
+ @"zoomScale": @(scrollView.zoomScale ?: 1),
1087
+ };
1088
+ _onScroll(event);
1089
+ }
1090
+ }
1091
+
1092
+ - (void)setDirectionalLockEnabled:(BOOL)directionalLockEnabled
1093
+ {
1094
+ _directionalLockEnabled = directionalLockEnabled;
1095
+ _webView.scrollView.directionalLockEnabled = directionalLockEnabled;
1096
+ }
1097
+
1098
+ - (void)setShowsHorizontalScrollIndicator:(BOOL)showsHorizontalScrollIndicator
1099
+ {
1100
+ _showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
1101
+ _webView.scrollView.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
1102
+ }
1103
+
1104
+ - (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator
1105
+ {
1106
+ _showsVerticalScrollIndicator = showsVerticalScrollIndicator;
1107
+ _webView.scrollView.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
1108
+ }
1109
+ #endif // !TARGET_OS_OSX
1110
+
1111
+ - (void)postMessage:(NSString *)message
1112
+ {
1113
+ NSDictionary *eventInitDict = @{@"data": message};
1114
+ NSString *source = [NSString
1115
+ stringWithFormat:@"window.dispatchEvent(new MessageEvent('message', %@));",
1116
+ RCTJSONStringify(eventInitDict, NULL)
1117
+ ];
1118
+ [self injectJavaScript: source];
1119
+ }
1120
+
1121
+ - (void)layoutSubviews
1122
+ {
1123
+ [super layoutSubviews];
1124
+
1125
+ // Ensure webview takes the position and dimensions of RNCWebViewImpl
1126
+ _webView.frame = self.bounds;
1127
+ #if !TARGET_OS_OSX
1128
+ _webView.scrollView.contentInset = _contentInset;
1129
+ #endif // !TARGET_OS_OSX
1130
+ }
1131
+
1132
+ - (NSMutableDictionary<NSString *, id> *)baseEvent
1133
+ {
1134
+ NSDictionary *event = @{
1135
+ @"url": _webView.URL.absoluteString ?: @"",
1136
+ @"title": _webView.title ?: @"",
1137
+ @"loading" : @(_webView.loading),
1138
+ @"canGoBack": @(_webView.canGoBack),
1139
+ @"canGoForward" : @(_webView.canGoForward)
1140
+ };
1141
+ return [[NSMutableDictionary alloc] initWithDictionary: event];
1142
+ }
1143
+
1144
+ + (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential {
1145
+ clientAuthenticationCredential = credential;
1146
+ }
1147
+
1148
+ + (void)setCustomCertificatesForHost:(nullable NSDictionary*)certificates {
1149
+ customCertificatesForHost = certificates;
1150
+ }
1151
+
1152
+ - (void) webView:(WKWebView *)webView
1153
+ didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
1154
+ completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable))completionHandler
1155
+ {
1156
+ NSString* host = nil;
1157
+ if (webView.URL != nil) {
1158
+ host = webView.URL.host;
1159
+ }
1160
+ if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
1161
+ completionHandler(NSURLSessionAuthChallengeUseCredential, clientAuthenticationCredential);
1162
+ return;
1163
+ }
1164
+ if ([[challenge protectionSpace] serverTrust] != nil && customCertificatesForHost != nil && host != nil) {
1165
+ SecCertificateRef localCertificate = (__bridge SecCertificateRef)([customCertificatesForHost objectForKey:host]);
1166
+ if (localCertificate != nil) {
1167
+ NSData *localCertificateData = (NSData*) CFBridgingRelease(SecCertificateCopyData(localCertificate));
1168
+ SecTrustRef trust = [[challenge protectionSpace] serverTrust];
1169
+ long count = SecTrustGetCertificateCount(trust);
1170
+ for (long i = 0; i < count; i++) {
1171
+ SecCertificateRef serverCertificate = SecTrustGetCertificateAtIndex(trust, i);
1172
+ if (serverCertificate == nil) { continue; }
1173
+ NSData *serverCertificateData = (NSData *) CFBridgingRelease(SecCertificateCopyData(serverCertificate));
1174
+ if ([serverCertificateData isEqualToData:localCertificateData]) {
1175
+ NSURLCredential *useCredential = [NSURLCredential credentialForTrust:trust];
1176
+ if (challenge.sender != nil) {
1177
+ [challenge.sender useCredential:useCredential forAuthenticationChallenge:challenge];
1178
+ }
1179
+ completionHandler(NSURLSessionAuthChallengeUseCredential, useCredential);
1180
+ return;
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodHTTPBasic) {
1186
+ NSString *username = [_basicAuthCredential valueForKey:@"username"];
1187
+ NSString *password = [_basicAuthCredential valueForKey:@"password"];
1188
+ if (username && password) {
1189
+ NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceNone];
1190
+ completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
1191
+ return;
1192
+ }
1193
+ }
1194
+ completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
1195
+ }
1196
+
1197
+ #pragma mark - WKNavigationDelegate methods
1198
+
1199
+ /**
1200
+ * alert
1201
+ */
1202
+ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
1203
+ {
1204
+ #if !TARGET_OS_OSX
1205
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
1206
+ [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
1207
+ completionHandler();
1208
+ }]];
1209
+ [[self topViewController] presentViewController:alert animated:YES completion:NULL];
1210
+ #else
1211
+ NSAlert *alert = [[NSAlert alloc] init];
1212
+ [alert setMessageText:message];
1213
+ [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(__unused NSModalResponse response){
1214
+ completionHandler();
1215
+ }];
1216
+ #endif // !TARGET_OS_OSX
1217
+ }
1218
+
1219
+ /**
1220
+ * confirm
1221
+ */
1222
+ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
1223
+ #if !TARGET_OS_OSX
1224
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
1225
+ [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
1226
+ completionHandler(YES);
1227
+ }]];
1228
+ [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
1229
+ completionHandler(NO);
1230
+ }]];
1231
+ [[self topViewController] presentViewController:alert animated:YES completion:NULL];
1232
+ #else
1233
+ NSAlert *alert = [[NSAlert alloc] init];
1234
+ [alert setMessageText:message];
1235
+ [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
1236
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
1237
+ void (^callbacksHandlers)(NSModalResponse response) = ^void(NSModalResponse response) {
1238
+ completionHandler(response == NSAlertFirstButtonReturn);
1239
+ };
1240
+ [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:callbacksHandlers];
1241
+ #endif // !TARGET_OS_OSX
1242
+ }
1243
+
1244
+ /**
1245
+ * prompt
1246
+ */
1247
+ - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
1248
+ #if !TARGET_OS_OSX
1249
+ UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
1250
+ [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
1251
+ textField.text = defaultText;
1252
+ }];
1253
+ UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
1254
+ completionHandler([[alert.textFields lastObject] text]);
1255
+ }];
1256
+ [alert addAction:okAction];
1257
+ UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
1258
+ completionHandler(nil);
1259
+ }];
1260
+ [alert addAction:cancelAction];
1261
+ alert.preferredAction = okAction;
1262
+ [[self topViewController] presentViewController:alert animated:YES completion:NULL];
1263
+ #else
1264
+ NSAlert *alert = [[NSAlert alloc] init];
1265
+ [alert setMessageText:prompt];
1266
+
1267
+ const NSRect RCTSingleTextFieldFrame = NSMakeRect(0.0, 0.0, 275.0, 22.0);
1268
+ NSTextField *textField = [[NSTextField alloc] initWithFrame:RCTSingleTextFieldFrame];
1269
+ textField.cell.scrollable = YES;
1270
+ if (@available(macOS 10.11, *)) {
1271
+ textField.maximumNumberOfLines = 1;
1272
+ }
1273
+ textField.stringValue = defaultText;
1274
+ [alert setAccessoryView:textField];
1275
+
1276
+ [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
1277
+ [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
1278
+ [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(NSModalResponse response) {
1279
+ if (response == NSAlertFirstButtonReturn) {
1280
+ completionHandler([textField stringValue]);
1281
+ } else {
1282
+ completionHandler(nil);
1283
+ }
1284
+ }];
1285
+ #endif // !TARGET_OS_OSX
1286
+ }
1287
+
1288
+ #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 /* iOS 15 */
1289
+ /**
1290
+ * Media capture permissions (prevent multiple prompts)
1291
+ */
1292
+ - (void) webView:(WKWebView *)webView
1293
+ requestMediaCapturePermissionForOrigin:(WKSecurityOrigin *)origin
1294
+ initiatedByFrame:(WKFrameInfo *)frame
1295
+ type:(WKMediaCaptureType)type
1296
+ decisionHandler:(void (^)(WKPermissionDecision decision))decisionHandler {
1297
+ if (_mediaCapturePermissionGrantType == RNCWebViewPermissionGrantType_GrantIfSameHost_ElsePrompt || _mediaCapturePermissionGrantType == RNCWebViewPermissionGrantType_GrantIfSameHost_ElseDeny) {
1298
+ if ([origin.host isEqualToString:webView.URL.host]) {
1299
+ decisionHandler(WKPermissionDecisionGrant);
1300
+ } else {
1301
+ WKPermissionDecision decision = _mediaCapturePermissionGrantType == RNCWebViewPermissionGrantType_GrantIfSameHost_ElsePrompt ? WKPermissionDecisionPrompt : WKPermissionDecisionDeny;
1302
+ decisionHandler(decision);
1303
+ }
1304
+ } else if (_mediaCapturePermissionGrantType == RNCWebViewPermissionGrantType_Deny) {
1305
+ decisionHandler(WKPermissionDecisionDeny);
1306
+ } else if (_mediaCapturePermissionGrantType == RNCWebViewPermissionGrantType_Grant) {
1307
+ decisionHandler(WKPermissionDecisionGrant);
1308
+ } else {
1309
+ decisionHandler(WKPermissionDecisionPrompt);
1310
+ }
1311
+ }
1312
+ #endif
1313
+
1314
+ #if !TARGET_OS_OSX
1315
+ /**
1316
+ * topViewController
1317
+ */
1318
+ -(UIViewController *)topViewController{
1319
+ return RCTPresentedViewController();
1320
+ }
1321
+
1322
+ #endif // !TARGET_OS_OSX
1323
+
1324
+ /**
1325
+ * Decides whether to allow or cancel a navigation.
1326
+ * @see https://fburl.com/42r9fxob
1327
+ */
1328
+ - (void) webView:(WKWebView *)webView
1329
+ decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
1330
+ decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
1331
+ {
1332
+ static NSDictionary<NSNumber *, NSString *> *navigationTypes;
1333
+ static dispatch_once_t onceToken;
1334
+
1335
+ dispatch_once(&onceToken, ^{
1336
+ navigationTypes = @{
1337
+ @(WKNavigationTypeLinkActivated): @"click",
1338
+ @(WKNavigationTypeFormSubmitted): @"formsubmit",
1339
+ @(WKNavigationTypeBackForward): @"backforward",
1340
+ @(WKNavigationTypeReload): @"reload",
1341
+ @(WKNavigationTypeFormResubmitted): @"formresubmit",
1342
+ @(WKNavigationTypeOther): @"other",
1343
+ };
1344
+ });
1345
+
1346
+ WKNavigationType navigationType = navigationAction.navigationType;
1347
+ NSURLRequest *request = navigationAction.request;
1348
+ BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
1349
+ BOOL hasTargetFrame = navigationAction.targetFrame != nil;
1350
+
1351
+ if (_onOpenWindow && !hasTargetFrame) {
1352
+ // When OnOpenWindow should be called, we want to prevent the navigation
1353
+ // If not prevented, the `decisionHandler` is called first and after that `createWebViewWithConfiguration` is called
1354
+ // In that order the WebView's ref would be updated with the target URL even if `createWebViewWithConfiguration` does not call `loadRequest`
1355
+ // So the WebView's document stays on the current URL, but the WebView's ref is replaced by the target URL
1356
+ // By preventing the navigation here, we also prevent the WebView's ref mutation
1357
+ // The counterpart is that we have to manually call `_onOpenWindow` here, because no navigation means no call to `createWebViewWithConfiguration`
1358
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
1359
+ [event addEntriesFromDictionary: @{@"targetUrl": request.URL.absoluteString}];
1360
+ decisionHandler(WKNavigationActionPolicyCancel);
1361
+ _onOpenWindow(event);
1362
+ return;
1363
+ }
1364
+
1365
+ if (_onShouldStartLoadWithRequest) {
1366
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
1367
+ int lockIdentifier = [[RNCWebViewDecisionManager getInstance] setDecisionHandler: ^(BOOL shouldStart){
1368
+ dispatch_async(dispatch_get_main_queue(), ^{
1369
+ if (!shouldStart) {
1370
+ decisionHandler(WKNavigationActionPolicyCancel);
1371
+ return;
1372
+ }
1373
+ if (self->_onLoadingStart) {
1374
+ // We have this check to filter out iframe requests and whatnot
1375
+ if (isTopFrame) {
1376
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
1377
+ [event addEntriesFromDictionary: @{
1378
+ @"url": (request.URL).absoluteString,
1379
+ @"navigationType": navigationTypes[@(navigationType)]
1380
+ }];
1381
+ self->_onLoadingStart(event);
1382
+ }
1383
+ }
1384
+
1385
+ // Allow all navigation by default
1386
+ decisionHandler(WKNavigationActionPolicyAllow);
1387
+ });
1388
+
1389
+ }];
1390
+ if (request.mainDocumentURL) {
1391
+ [event addEntriesFromDictionary: @{
1392
+ @"mainDocumentURL": (request.mainDocumentURL).absoluteString,
1393
+ }];
1394
+ }
1395
+ [event addEntriesFromDictionary: @{
1396
+ @"url": (request.URL).absoluteString,
1397
+ @"navigationType": navigationTypes[@(navigationType)],
1398
+ @"isTopFrame": @(isTopFrame),
1399
+ @"hasTargetFrame": @(hasTargetFrame),
1400
+ @"lockIdentifier": @(lockIdentifier)
1401
+ }];
1402
+ _onShouldStartLoadWithRequest(event);
1403
+ // decisionHandler(WKNavigationActionPolicyAllow);
1404
+ return;
1405
+ }
1406
+
1407
+ if (_onLoadingStart) {
1408
+ // We have this check to filter out iframe requests and whatnot
1409
+ if (isTopFrame) {
1410
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
1411
+ [event addEntriesFromDictionary: @{
1412
+ @"url": (request.URL).absoluteString,
1413
+ @"navigationType": navigationTypes[@(navigationType)]
1414
+ }];
1415
+ _onLoadingStart(event);
1416
+ }
1417
+ }
1418
+
1419
+ // Allow all navigation by default
1420
+ decisionHandler(WKNavigationActionPolicyAllow);
1421
+ }
1422
+
1423
+ /**
1424
+ * Called when the web view’s content process is terminated.
1425
+ * @see https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi?language=objc
1426
+ */
1427
+ - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
1428
+ {
1429
+ RCTLogWarn(@"Webview Process Terminated");
1430
+ if (_onContentProcessDidTerminate) {
1431
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
1432
+ _onContentProcessDidTerminate(event);
1433
+ }
1434
+ }
1435
+
1436
+ /**
1437
+ * Decides whether to allow or cancel a navigation after its response is known.
1438
+ * @see https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview?language=objc
1439
+ */
1440
+ - (void) webView:(WKWebView *)webView
1441
+ decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
1442
+ decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
1443
+ {
1444
+ WKNavigationResponsePolicy policy = WKNavigationResponsePolicyAllow;
1445
+ if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) {
1446
+ NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
1447
+ NSInteger statusCode = response.statusCode;
1448
+ if (_onHttpError && navigationResponse.forMainFrame) {
1449
+ if (statusCode >= 400) {
1450
+ NSMutableDictionary<NSString *, id> *httpErrorEvent = [self baseEvent];
1451
+ [httpErrorEvent addEntriesFromDictionary: @{
1452
+ @"url": response.URL.absoluteString,
1453
+ @"statusCode": @(statusCode)
1454
+ }];
1455
+
1456
+ _onHttpError(httpErrorEvent);
1457
+ }
1458
+ }
1459
+ NSString *disposition = nil;
1460
+ if (@available(iOS 13, macOS 10.15, *)) {
1461
+ disposition = [response valueForHTTPHeaderField:@"Content-Disposition"];
1462
+ }
1463
+ BOOL isAttachment = disposition != nil && [disposition hasPrefix:@"attachment"];
1464
+ if (isAttachment || !navigationResponse.canShowMIMEType) {
1465
+ if (_onFileDownload) {
1466
+ policy = WKNavigationResponsePolicyCancel;
1467
+
1468
+ NSMutableDictionary<NSString *, id> *downloadEvent = [self baseEvent];
1469
+ [downloadEvent addEntriesFromDictionary: @{
1470
+ @"downloadUrl": (response.URL).absoluteString,
1471
+ }];
1472
+ _onFileDownload(downloadEvent);
1473
+ }
1474
+ }
1475
+ }
1476
+
1477
+ decisionHandler(policy);
1478
+ }
1479
+
1480
+ /**
1481
+ * Called when an error occurs while the web view is loading content.
1482
+ * @see https://fburl.com/km6vqenw
1483
+ */
1484
+ - (void) webView:(WKWebView *)webView
1485
+ didFailProvisionalNavigation:(WKNavigation *)navigation
1486
+ withError:(NSError *)error
1487
+ {
1488
+ if (_onLoadingError) {
1489
+ if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
1490
+ // NSURLErrorCancelled is reported when a page has a redirect OR if you load
1491
+ // a new URL in the WebView before the previous one came back. We can just
1492
+ // ignore these since they aren't real errors.
1493
+ // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
1494
+ return;
1495
+ }
1496
+
1497
+ if ([error.domain isEqualToString:@"WebKitErrorDomain"] &&
1498
+ (error.code == 102 || error.code == 101)) {
1499
+ // Error code 102 "Frame load interrupted" is raised by the WKWebView
1500
+ // when the URL is from an http redirect. This is a common pattern when
1501
+ // implementing OAuth with a WebView.
1502
+ return;
1503
+ }
1504
+
1505
+ NSMutableDictionary<NSString *, id> *event = [self baseEvent];
1506
+ [event addEntriesFromDictionary:@{
1507
+ @"didFailProvisionalNavigation": @YES,
1508
+ @"domain": error.domain,
1509
+ @"code": @(error.code),
1510
+ @"description": error.localizedDescription,
1511
+ }];
1512
+ _onLoadingError(event);
1513
+ }
1514
+ }
1515
+
1516
+ - (void)evaluateJS:(NSString *)js
1517
+ thenCall: (void (^)(NSString*)) callback
1518
+ {
1519
+ if (self.enableApplePay) {
1520
+ RCTLogWarn(@"Cannot run javascript when apple pay is enabled");
1521
+ return;
1522
+ }
1523
+ [self.webView evaluateJavaScript: js completionHandler: ^(id result, NSError *error) {
1524
+ if (callback != nil) {
1525
+ callback([NSString stringWithFormat:@"%@", result]);
1526
+ }
1527
+ if (error != nil) {
1528
+ 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]);
1529
+ }
1530
+ }];
1531
+ }
1532
+
1533
+ -(void)forceIgnoreSilentHardwareSwitch:(BOOL)initialSetup
1534
+ {
1535
+ 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==";
1536
+ NSString *scr;
1537
+ if (initialSetup) {
1538
+ scr = [NSString stringWithFormat:@"var s=new Audio('%@');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s);true", mp3Str];
1539
+ } else {
1540
+ 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];
1541
+ }
1542
+ [self evaluateJS: scr thenCall: nil];
1543
+ }
1544
+
1545
+ -(void)disableIgnoreSilentSwitch
1546
+ {
1547
+ [self evaluateJS: @"document.getElementById('wkwebviewAudio').src=null;true" thenCall: nil];
1548
+ }
1549
+
1550
+ -(void)appDidBecomeActive
1551
+ {
1552
+ if (_ignoreSilentHardwareSwitch) {
1553
+ [self forceIgnoreSilentHardwareSwitch:false];
1554
+ }
1555
+ }
1556
+
1557
+ -(void)appWillResignActive
1558
+ {
1559
+ if (_ignoreSilentHardwareSwitch) {
1560
+ [self disableIgnoreSilentSwitch];
1561
+ }
1562
+ }
1563
+
1564
+ /**
1565
+ * Called when the navigation is complete.
1566
+ * @see https://fburl.com/rtys6jlb
1567
+ */
1568
+ - (void)webView:(WKWebView *)webView
1569
+ didFinishNavigation:(WKNavigation *)navigation
1570
+ {
1571
+ if (_ignoreSilentHardwareSwitch) {
1572
+ [self forceIgnoreSilentHardwareSwitch:true];
1573
+ }
1574
+
1575
+ if (_onLoadingFinish) {
1576
+ _onLoadingFinish([self baseEvent]);
1577
+ }
1578
+ }
1579
+
1580
+ - (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore
1581
+ {
1582
+ if (@available(iOS 11.0, *)) {
1583
+ if(_sharedCookiesEnabled) {
1584
+ // Write all cookies from WKWebView back to sharedHTTPCookieStorage
1585
+ [cookieStore getAllCookies:^(NSArray* cookies) {
1586
+ for (NSHTTPCookie *cookie in cookies) {
1587
+ [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
1588
+ }
1589
+ }];
1590
+ }
1591
+ }
1592
+ }
1593
+
1594
+ - (void)injectJavaScript:(NSString *)script
1595
+ {
1596
+ [self evaluateJS: script thenCall: nil];
1597
+ }
1598
+
1599
+ - (void)goForward
1600
+ {
1601
+ [_webView goForward];
1602
+ }
1603
+
1604
+ - (void)goBack
1605
+ {
1606
+ [_webView goBack];
1607
+ }
1608
+
1609
+ - (void)reload
1610
+ {
1611
+ /**
1612
+ * When the initial load fails due to network connectivity issues,
1613
+ * [_webView reload] doesn't reload the webpage. Therefore, we must
1614
+ * manually call [_webView loadRequest:request].
1615
+ */
1616
+ NSURLRequest *request = [self requestForSource:self.source];
1617
+
1618
+ if (request.URL && !_webView.URL.absoluteString.length) {
1619
+ [_webView loadRequest:request];
1620
+ } else {
1621
+ [_webView reload];
1622
+ }
1623
+ }
1624
+ #if !TARGET_OS_OSX
1625
+ - (void)addPullToRefreshControl
1626
+ {
1627
+ UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
1628
+ _refreshControl = refreshControl;
1629
+ if(_refreshControlLightMode) {
1630
+ [refreshControl setTintColor:[UIColor whiteColor]];
1631
+ }
1632
+ [_webView.scrollView addSubview: refreshControl];
1633
+ [refreshControl addTarget:self action:@selector(pullToRefresh:) forControlEvents: UIControlEventValueChanged];
1634
+ }
1635
+
1636
+ - (void)pullToRefresh:(UIRefreshControl *)refreshControl
1637
+ {
1638
+ [self reload];
1639
+ [refreshControl endRefreshing];
1640
+ }
1641
+
1642
+
1643
+ - (void)setPullToRefreshEnabled:(BOOL)pullToRefreshEnabled
1644
+ {
1645
+ _pullToRefreshEnabled = pullToRefreshEnabled;
1646
+
1647
+ if (pullToRefreshEnabled) {
1648
+ [self addPullToRefreshControl];
1649
+ } else {
1650
+ [_refreshControl removeFromSuperview];
1651
+ }
1652
+
1653
+ [self setBounces:_bounces];
1654
+ }
1655
+ #endif // !TARGET_OS_OSX
1656
+
1657
+ - (void)stopLoading
1658
+ {
1659
+ [_webView stopLoading];
1660
+ }
1661
+
1662
+ - (void)requestFocus
1663
+ {
1664
+ #if !TARGET_OS_OSX
1665
+ [_webView becomeFirstResponder];
1666
+ #endif // !TARGET_OS_OSX
1667
+ }
1668
+
1669
+ - (void)clearCache:(BOOL)includeDiskFiles
1670
+ {
1671
+ NSMutableSet *dataTypes = [NSMutableSet setWithArray:@[
1672
+ WKWebsiteDataTypeMemoryCache,
1673
+ WKWebsiteDataTypeOfflineWebApplicationCache,
1674
+ ]];
1675
+ if (@available(iOS 11.3, *)) {
1676
+ [dataTypes addObject:WKWebsiteDataTypeFetchCache];
1677
+ }
1678
+ if (includeDiskFiles) {
1679
+ [dataTypes addObjectsFromArray:@[
1680
+ WKWebsiteDataTypeDiskCache,
1681
+ WKWebsiteDataTypeSessionStorage,
1682
+ WKWebsiteDataTypeLocalStorage,
1683
+ WKWebsiteDataTypeWebSQLDatabases,
1684
+ WKWebsiteDataTypeIndexedDBDatabases
1685
+ ]];
1686
+ }
1687
+ [self removeData:dataTypes];
1688
+ }
1689
+
1690
+ - (void)removeData:(NSSet *)dataTypes
1691
+ {
1692
+ if (_webView == nil) {
1693
+ return;
1694
+ }
1695
+ NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
1696
+
1697
+ [_webView.configuration.websiteDataStore removeDataOfTypes:dataTypes modifiedSince:dateFrom completionHandler:^{}];
1698
+ }
1699
+
1700
+ #if !TARGET_OS_OSX
1701
+ - (void)setBounces:(BOOL)bounces
1702
+ {
1703
+ _bounces = bounces;
1704
+ //For UIRefreshControl to work correctly, the bounces should always be true
1705
+ _webView.scrollView.bounces = _pullToRefreshEnabled || bounces;
1706
+ }
1707
+ #endif // !TARGET_OS_OSX
1708
+
1709
+ - (void)setInjectedJavaScript:(NSString *)source {
1710
+ _injectedJavaScript = source;
1711
+
1712
+ self.atEndScript = source == nil ? nil : [[WKUserScript alloc] initWithSource:source
1713
+ injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
1714
+ forMainFrameOnly:_injectedJavaScriptForMainFrameOnly];
1715
+
1716
+ if(_webView != nil){
1717
+ [self resetupScripts:_webView.configuration];
1718
+ }
1719
+ }
1720
+
1721
+ - (void)setInjectedJavaScriptObject:(NSString *)source
1722
+ {
1723
+ self.injectedObjectJsonScript = [
1724
+ [WKUserScript alloc]
1725
+ initWithSource: [
1726
+ NSString
1727
+ stringWithFormat:
1728
+ @"window.%@ = window.%@ || {};"
1729
+ "window.%@.injectedObjectJson = function () {"
1730
+ " return `%@`;"
1731
+ "};", MessageHandlerName, MessageHandlerName, MessageHandlerName, source
1732
+ ]
1733
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1734
+ /* 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.
1735
+ * I am keeping it as YES for consistency with previous behaviour. */
1736
+ // forMainFrameOnly:_messagingEnabledForMainFrameOnly
1737
+ forMainFrameOnly:YES
1738
+ ];
1739
+ }
1740
+
1741
+ - (void)setEnableApplePay:(BOOL)enableApplePay {
1742
+ _enableApplePay = enableApplePay;
1743
+ if(_webView != nil){
1744
+ [self resetupScripts:_webView.configuration];
1745
+ }
1746
+ }
1747
+
1748
+ - (void)setInjectedJavaScriptBeforeContentLoaded:(NSString *)source {
1749
+ _injectedJavaScriptBeforeContentLoaded = source;
1750
+
1751
+ self.atStartScript = source == nil ? nil : [[WKUserScript alloc] initWithSource:source
1752
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1753
+ forMainFrameOnly:_injectedJavaScriptBeforeContentLoadedForMainFrameOnly];
1754
+
1755
+ if(_webView != nil){
1756
+ [self resetupScripts:_webView.configuration];
1757
+ }
1758
+ }
1759
+
1760
+ - (void)setInjectedJavaScriptForMainFrameOnly:(BOOL)mainFrameOnly {
1761
+ _injectedJavaScriptForMainFrameOnly = mainFrameOnly;
1762
+ [self setInjectedJavaScript:_injectedJavaScript];
1763
+ }
1764
+
1765
+ - (void)setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly:(BOOL)mainFrameOnly {
1766
+ _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = mainFrameOnly;
1767
+ [self setInjectedJavaScriptBeforeContentLoaded:_injectedJavaScriptBeforeContentLoaded];
1768
+ }
1769
+
1770
+ - (void)setMessagingEnabled:(BOOL)messagingEnabled {
1771
+ _messagingEnabled = messagingEnabled;
1772
+
1773
+ self.postMessageScript = _messagingEnabled ?
1774
+ [
1775
+ [WKUserScript alloc]
1776
+ initWithSource: [
1777
+ NSString
1778
+ stringWithFormat:
1779
+ @"window.%@ = window.%@ || {};"
1780
+ "window.%@.postMessage = function (data) {"
1781
+ " window.webkit.messageHandlers.%@.postMessage(String(data));"
1782
+ "};", MessageHandlerName, MessageHandlerName, MessageHandlerName, MessageHandlerName
1783
+ ]
1784
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1785
+ /* 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.
1786
+ * I am keeping it as YES for consistency with previous behaviour. */
1787
+ // forMainFrameOnly:_messagingEnabledForMainFrameOnly
1788
+ forMainFrameOnly:YES
1789
+ ] :
1790
+ nil;
1791
+
1792
+ if(_webView != nil){
1793
+ [self resetupScripts:_webView.configuration];
1794
+ }
1795
+ }
1796
+
1797
+ - (void)writeCookiesToWebView:(NSArray<NSHTTPCookie *>*)cookies completion:(void (^)(void))completion {
1798
+ // The required cookie APIs only became available on iOS 11
1799
+ if (@available(iOS 11.0, *)) {
1800
+ if (_sharedCookiesEnabled) {
1801
+ __weak WKWebView *webView = _webView;
1802
+ dispatch_async(dispatch_get_main_queue(), ^{
1803
+ dispatch_group_t group = dispatch_group_create();
1804
+ for (NSHTTPCookie *cookie in cookies) {
1805
+ dispatch_group_enter(group);
1806
+ [webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:^{
1807
+ dispatch_group_leave(group);
1808
+ }];
1809
+ }
1810
+ dispatch_group_notify(group, dispatch_get_main_queue(), ^{
1811
+ if (completion) {
1812
+ completion();
1813
+ }
1814
+ });
1815
+ });
1816
+ return;
1817
+ }
1818
+ }
1819
+
1820
+ if (completion) {
1821
+ completion();
1822
+ }
1823
+ }
1824
+
1825
+ - (void)syncCookiesToWebView:(void (^)(void))completion {
1826
+ NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
1827
+ [self writeCookiesToWebView:cookies completion:completion];
1828
+ }
1829
+
1830
+ - (void)resetupScripts:(WKWebViewConfiguration *)wkWebViewConfig {
1831
+ [wkWebViewConfig.userContentController removeAllUserScripts];
1832
+ [wkWebViewConfig.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
1833
+ if(self.enableApplePay){
1834
+ if (self.postMessageScript){
1835
+ [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
1836
+ name:MessageHandlerName];
1837
+ }
1838
+ return;
1839
+ }
1840
+
1841
+ NSString *html5HistoryAPIShimSource = [NSString stringWithFormat:
1842
+ @"(function(history) {\n"
1843
+ " function notify(type) {\n"
1844
+ " setTimeout(function() {\n"
1845
+ " window.webkit.messageHandlers.%@.postMessage(type)\n"
1846
+ " }, 0)\n"
1847
+ " }\n"
1848
+ " function shim(f) {\n"
1849
+ " return function pushState() {\n"
1850
+ " notify('other')\n"
1851
+ " return f.apply(history, arguments)\n"
1852
+ " }\n"
1853
+ " }\n"
1854
+ " history.pushState = shim(history.pushState)\n"
1855
+ " history.replaceState = shim(history.replaceState)\n"
1856
+ " window.addEventListener('popstate', function() {\n"
1857
+ " notify('backforward')\n"
1858
+ " })\n"
1859
+ "})(window.history)\n", HistoryShimName
1860
+ ];
1861
+ WKUserScript *script = [[WKUserScript alloc] initWithSource:html5HistoryAPIShimSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
1862
+ [wkWebViewConfig.userContentController addUserScript:script];
1863
+
1864
+ if(_sharedCookiesEnabled) {
1865
+ // More info to sending cookies with WKWebView
1866
+ // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
1867
+ if (@available(iOS 11.0, *)) {
1868
+ // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
1869
+ // See also https://forums.developer.apple.com/thread/97194
1870
+ // check if websiteDataStore has not been initialized before
1871
+ if(!_incognito && !_cacheEnabled) {
1872
+ wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1873
+ }
1874
+ [self syncCookiesToWebView:^{}];
1875
+ } else {
1876
+ NSMutableString *script = [NSMutableString string];
1877
+
1878
+ // Clear all existing cookies in a direct called function. This ensures that no
1879
+ // javascript error will break the web content javascript.
1880
+ // We keep this code here, if someone requires that Cookies are also removed within the
1881
+ // the WebView and want to extends the current sharedCookiesEnabled option with an
1882
+ // additional property.
1883
+ // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
1884
+ // for each cookie which is already available in the WebView context.
1885
+ /*
1886
+ [script appendString:@"(function () {\n"];
1887
+ [script appendString:@" var cookies = document.cookie.split('; ');\n"];
1888
+ [script appendString:@" for (var i = 0; i < cookies.length; i++) {\n"];
1889
+ [script appendString:@" if (cookies[i].indexOf('=') !== -1) {\n"];
1890
+ [script appendString:@" document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
1891
+ [script appendString:@" }\n"];
1892
+ [script appendString:@" }\n"];
1893
+ [script appendString:@"})();\n\n"];
1894
+ */
1895
+
1896
+ // Set cookies in a direct called function. This ensures that no
1897
+ // javascript error will break the web content javascript.
1898
+ // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
1899
+ // for each cookie which is available in the application context.
1900
+ [script appendString:@"(function () {\n"];
1901
+ for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
1902
+ [script appendFormat:@"document.cookie = %@ + '=' + %@",
1903
+ RCTJSONStringify(cookie.name, NULL),
1904
+ RCTJSONStringify(cookie.value, NULL)];
1905
+ if (cookie.path) {
1906
+ [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
1907
+ }
1908
+ if (cookie.expiresDate) {
1909
+ [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
1910
+ cookie.expiresDate.timeIntervalSince1970 * 1000
1911
+ ];
1912
+ }
1913
+ [script appendString:@";\n"];
1914
+ }
1915
+ [script appendString:@"})();\n"];
1916
+
1917
+ WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
1918
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1919
+ forMainFrameOnly:YES];
1920
+ [wkWebViewConfig.userContentController addUserScript:cookieInScript];
1921
+ }
1922
+ }
1923
+
1924
+ if(_messagingEnabled){
1925
+ if (self.postMessageScript){
1926
+ [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
1927
+ name:MessageHandlerName];
1928
+ [wkWebViewConfig.userContentController addUserScript:self.postMessageScript];
1929
+ }
1930
+ if (self.atEndScript) {
1931
+ [wkWebViewConfig.userContentController addUserScript:self.atEndScript];
1932
+ }
1933
+ }
1934
+ // Whether or not messaging is enabled, add the startup script if it exists.
1935
+ if (self.atStartScript) {
1936
+ [wkWebViewConfig.userContentController addUserScript:self.atStartScript];
1937
+ }
1938
+ if (self.injectedObjectJsonScript) {
1939
+ [wkWebViewConfig.userContentController addUserScript:self.injectedObjectJsonScript];
1940
+ }
1941
+ }
1942
+
1943
+ - (NSURLRequest *)requestForSource:(id)json {
1944
+ NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
1945
+
1946
+ // If sharedCookiesEnabled we automatically add all application cookies to the
1947
+ // http request. This is automatically done on iOS 11+ in the WebView constructor.
1948
+ // Se we need to manually add these shared cookies here only for iOS versions < 11.
1949
+ if (_sharedCookiesEnabled) {
1950
+ if (@available(iOS 11.0, *)) {
1951
+ // see WKWebView initialization for added cookies
1952
+ } else if (request != nil) {
1953
+ NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:request.URL];
1954
+ NSDictionary<NSString *, NSString *> *cookieHeader = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
1955
+ NSMutableURLRequest *mutableRequest = [request mutableCopy];
1956
+ [mutableRequest setAllHTTPHeaderFields:cookieHeader];
1957
+ return mutableRequest;
1958
+ }
1959
+ }
1960
+ return request;
1961
+ }
1962
+
1963
+ @end
1964
+
1965
+ @implementation RNCWeakScriptMessageDelegate
1966
+
1967
+ - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
1968
+ self = [super init];
1969
+ if (self) {
1970
+ _scriptDelegate = scriptDelegate;
1971
+ }
1972
+ return self;
1973
+ }
1974
+
1975
+ - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
1976
+ [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
1977
+ }
1978
+
1979
+ @end