Files
react-native-webview/apple/RNCWebViewManager.m
T
Scott Batson e1fc730eed Allow custom menu items for selection menu (#2101)
remove unused vars

use gesture handler

change callback name, pass selected text back

do not use deprecate setTarget

do not call super on methodSignatureForSelector twice

add checks for custom menu items and callback

make custom params optional

add custom menu items outside of initWithFrame

require key and label for menuItems

fix typo
2021-10-08 08:38:43 +02:00

259 lines
9.6 KiB
Objective-C

/**
* 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.
*/
#import "RNCWebViewManager.h"
#import <React/RCTUIManager.h>
#import <React/RCTDefines.h>
#import "RNCWebView.h"
@interface RNCWebViewManager () <RNCWebViewDelegate>
@end
@implementation RCTConvert (WKWebView)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
RCT_ENUM_CONVERTER(WKContentMode, (@{
@"recommended": @(WKContentModeRecommended),
@"mobile": @(WKContentModeMobile),
@"desktop": @(WKContentModeDesktop),
}), WKContentModeRecommended, integerValue)
#endif
@end
@implementation RNCWebViewManager
{
NSConditionLock *_shouldStartLoadLock;
BOOL _shouldStartLoad;
}
RCT_EXPORT_MODULE()
#if !TARGET_OS_OSX
- (UIView *)view
#else
- (RCTUIView *)view
#endif // !TARGET_OS_OSX
{
RNCWebView *webView = [RNCWebView new];
webView.delegate = self;
return webView;
}
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(onFileDownload, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onHttpError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptForMainFrameOnly, BOOL)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoadedForMainFrameOnly, BOOL)
RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(javaScriptCanOpenWindowsAutomatically, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowUniversalAccessFromFileURLs, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
RCT_EXPORT_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, BOOL)
#if WEBKIT_IOS_10_APIS_AVAILABLE
RCT_EXPORT_VIEW_PROPERTY(dataDetectorTypes, WKDataDetectorTypes)
#endif
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
RCT_EXPORT_VIEW_PROPERTY(autoManageStatusBarEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowsBackForwardNavigationGestures, BOOL)
RCT_EXPORT_VIEW_PROPERTY(incognito, BOOL)
RCT_EXPORT_VIEW_PROPERTY(pagingEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(applicationNameForUserAgent, NSString)
RCT_EXPORT_VIEW_PROPERTY(cacheEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowsLinkPreview, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowingReadAccessToURL, NSString)
RCT_EXPORT_VIEW_PROPERTY(basicAuthCredential, NSDictionary)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
#endif
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* __IPHONE_13_0 */
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
#endif
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
RCT_EXPORT_VIEW_PROPERTY(contentMode, WKContentMode)
#endif
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 /* iOS 14 */
RCT_EXPORT_VIEW_PROPERTY(limitsNavigationsToAppBoundDomains, BOOL)
#endif
/**
* Expose methods to enable messaging the webview.
*/
RCT_EXPORT_VIEW_PROPERTY(messagingEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onMessage, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(enableApplePay, BOOL)
RCT_EXPORT_VIEW_PROPERTY(menuItems, NSArray);
RCT_EXPORT_VIEW_PROPERTY(onCustomMenuSelection, RCTDirectEventBlock)
RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)message)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view postMessage:message];
}
}];
}
RCT_CUSTOM_VIEW_PROPERTY(pullToRefreshEnabled, BOOL, RNCWebView) {
view.pullToRefreshEnabled = json == nil ? false : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWebView) {
view.bounces = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(useSharedProcessPool, BOOL, RNCWebView) {
view.useSharedProcessPool = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(userAgent, NSString, RNCWebView) {
view.userAgent = [RCTConvert NSString: json];
}
RCT_CUSTOM_VIEW_PROPERTY(scrollEnabled, BOOL, RNCWebView) {
view.scrollEnabled = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(sharedCookiesEnabled, BOOL, RNCWebView) {
view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json];
}
#if !TARGET_OS_OSX
RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWebView) {
view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json];
}
#endif // !TARGET_OS_OSX
RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWebView) {
view.directionalLockEnabled = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL, RNCWebView) {
view.showsHorizontalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL, RNCWebView) {
view.showsVerticalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_CUSTOM_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, BOOL, RNCWebView) {
view.keyboardDisplayRequiresUserAction = json == nil ? true : [RCTConvert BOOL: json];
}
RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view injectJavaScript:script];
}
}];
}
RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view goBack];
}
}];
}
RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view goForward];
}
}];
}
RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view reload];
}
}];
}
RCT_EXPORT_METHOD(stopLoading:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWebView *> *viewRegistry) {
RNCWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RNCWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view);
} else {
[view stopLoading];
}
}];
}
#pragma mark - Exported synchronous methods
- (BOOL) webView:(RNCWebView *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback
{
_shouldStartLoadLock = [[NSConditionLock alloc] initWithCondition:arc4random()];
_shouldStartLoad = YES;
request[@"lockIdentifier"] = @(_shouldStartLoadLock.condition);
callback(request);
// Block the main thread for a maximum of 250ms until the JS thread returns
if ([_shouldStartLoadLock lockWhenCondition:0 beforeDate:[NSDate dateWithTimeIntervalSinceNow:.25]]) {
BOOL returnValue = _shouldStartLoad;
[_shouldStartLoadLock unlock];
_shouldStartLoadLock = nil;
return returnValue;
} else {
RCTLogWarn(@"Did not receive response to shouldStartLoad in time, defaulting to YES");
return YES;
}
}
RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier)
{
if ([_shouldStartLoadLock tryLockWhenCondition:lockIdentifier]) {
_shouldStartLoad = result;
[_shouldStartLoadLock unlockWithCondition:0];
} else {
RCTLogWarn(@"startLoadWithResult invoked with invalid lockIdentifier: "
"got %lld, expected %lld", (long long)lockIdentifier, (long long)_shouldStartLoadLock.condition);
}
}
@end