mirror of
https://github.com/status-im/react-native.git
synced 2025-01-29 02:35:41 +00:00
0a290e22da
Summary: public Original github title: Exported a callback for native webview delegate method shouldStartLoadWithRequest We have a requirement in our app, to open in mobile Safari, any http:// and https:// links displayed in a web view. Our web view is not full screen and is loaded with an HTML string from the backend. Displaying external content in that web view is outside of the scope of our app, so we open them in mobile Safari. I've forked the WebView component and added a callback property, shouldStartLoadWithRequest, and modified the RCTWebView implementation of `webView:shouldStartLoadWithRequest:navigationType:` to check if the shouldStartLoadWithRequest property is set. If the property is set, `webView:shouldStartLoadWithRequest:navigationType:` passes the URL & navigationType to the callback. The callback is then able to ignore the request, redirect it, open a full screen web view to display the URL content, or even deep link to another app with LinkingIOS.openURL(). Original author: PJ Cabrera <pj.cabrera@gmail.com> Closes https://github.com/facebook/react-native/pull/3643 Reviewed By: nicklockwood Differential Revision: D2600371 fb-gh-sync-id: 14dfdb3df442d899d9f2af831bbc8d695faefa33
134 lines
4.4 KiB
Objective-C
134 lines
4.4 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTWebViewManager.h"
|
|
|
|
#import "RCTBridge.h"
|
|
#import "RCTSparseArray.h"
|
|
#import "RCTUIManager.h"
|
|
#import "RCTWebView.h"
|
|
|
|
@interface RCTWebViewManager () <RCTWebViewDelegate>
|
|
|
|
@end
|
|
|
|
@implementation RCTWebViewManager {
|
|
NSConditionLock *_shouldStartLoadLock;
|
|
BOOL _shouldStartLoad;
|
|
}
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
- (UIView *)view
|
|
{
|
|
RCTWebView *webView = [RCTWebView new];
|
|
webView.delegate = self;
|
|
return webView;
|
|
}
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL);
|
|
RCT_REMAP_VIEW_PROPERTY(html, HTML, NSString);
|
|
RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL);
|
|
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL);
|
|
RCT_REMAP_VIEW_PROPERTY(scalesPageToFit, _webView.scalesPageToFit, BOOL);
|
|
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString);
|
|
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets);
|
|
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL);
|
|
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock);
|
|
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock);
|
|
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock);
|
|
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock);
|
|
|
|
- (NSDictionary *)constantsToExport
|
|
{
|
|
return @{
|
|
@"JSNavigationScheme": RCTJSNavigationScheme,
|
|
@"NavigationType": @{
|
|
@"LinkClicked": @(UIWebViewNavigationTypeLinkClicked),
|
|
@"FormSubmitted": @(UIWebViewNavigationTypeFormSubmitted),
|
|
@"BackForward": @(UIWebViewNavigationTypeBackForward),
|
|
@"Reload": @(UIWebViewNavigationTypeReload),
|
|
@"FormResubmitted": @(UIWebViewNavigationTypeFormResubmitted),
|
|
@"Other": @(UIWebViewNavigationTypeOther)
|
|
},
|
|
};
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag)
|
|
{
|
|
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
|
RCTWebView *view = viewRegistry[reactTag];
|
|
if (![view isKindOfClass:[RCTWebView class]]) {
|
|
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
|
|
} else {
|
|
[view goBack];
|
|
}
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag)
|
|
{
|
|
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
|
id view = viewRegistry[reactTag];
|
|
if (![view isKindOfClass:[RCTWebView class]]) {
|
|
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
|
|
} else {
|
|
[view goForward];
|
|
}
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag)
|
|
{
|
|
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
|
RCTWebView *view = viewRegistry[reactTag];
|
|
if (![view isKindOfClass:[RCTWebView class]]) {
|
|
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
|
|
} else {
|
|
[view reload];
|
|
}
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Exported synchronous methods
|
|
|
|
- (BOOL)webView:(__unused RCTWebView *)webView
|
|
shouldStartLoadForRequest:(NSMutableDictionary *)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 %zd, expected %zd", lockIdentifier, _shouldStartLoadLock.condition);
|
|
}
|
|
}
|
|
|
|
@end
|