From ec8bccb217156945a97c5f3161f62264edb2fddd Mon Sep 17 00:00:00 2001 From: PJ Cabrera Date: Thu, 22 Oct 2015 17:50:51 -0700 Subject: [PATCH] Exported a callback for native webview delegate method webview:shouldStartLoadWithRequest:navigationType: --- Libraries/Components/WebView/WebView.ios.js | 13 ++++++ React/Views/RCTWebView.m | 44 +++++++++++++++++++++ React/Views/RCTWebViewManager.m | 9 +++++ 3 files changed, 66 insertions(+) diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index a3d457bfdb6f2d..c1a0f5dda62d69 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -107,6 +107,7 @@ var WebView = React.createClass({ * user can change the scale */ scalesPageToFit: PropTypes.bool, + shouldStartLoadWithRequest: PropTypes.func, }, getInitialState: function() { @@ -167,6 +168,7 @@ var WebView = React.createClass({ onLoadingStart={this.onLoadingStart} onLoadingFinish={this.onLoadingFinish} onLoadingError={this.onLoadingError} + onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest} scalesPageToFit={this.props.scalesPageToFit} />; @@ -178,6 +180,10 @@ var WebView = React.createClass({ ); }, + startLoadWithResult: function(result, lockIdentifier) { + RCTWebViewManager.startLoadWithResult(result, lockIdentifier); + }, + goForward: function() { RCTWebViewManager.goForward(this.getWebViewHandle()); }, @@ -224,6 +230,12 @@ var WebView = React.createClass({ }); this.updateNavigationState(event); }, + + onShouldStartLoadWithRequest: function(event: Event) { + if (this.props.shouldStartLoadWithRequest) { + this.props.shouldStartLoadWithRequest(event.nativeEvent); + } + }, }); var RCTWebView = requireNativeComponent('RCTWebView', WebView, { @@ -231,6 +243,7 @@ var RCTWebView = requireNativeComponent('RCTWebView', WebView, { onLoadingStart: true, onLoadingError: true, onLoadingFinish: true, + onShouldStartLoadWithRequest: true }, }); diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index 46d47c8955fee7..a092dbeff1ed50 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -25,6 +25,10 @@ @interface RCTWebView () @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; @property (nonatomic, copy) RCTDirectEventBlock onLoadingError; +@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest; +@property (nonatomic, strong) NSConditionLock *conditionLock; +@property (nonatomic, assign) BOOL startLoadResult; +@property (nonatomic, strong) NSOperationQueue *operationQueue; @end @@ -43,6 +47,8 @@ - (instancetype)initWithFrame:(CGRect)frame _webView = [[UIWebView alloc] initWithFrame:self.bounds]; _webView.delegate = self; [self addSubview:_webView]; + + self.operationQueue = [[NSOperationQueue alloc] init]; } return self; } @@ -142,6 +148,16 @@ - (void)refreshContentInset - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { + // skip this for the JS Navigation handler + if (![request.URL.scheme isEqualToString:RCTJSNavigationScheme] && + _onShouldStartLoadWithRequest) { + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"url": (request.URL).absoluteString, + @"navigationType": @(navigationType) + }]; + return [self onShouldStartLoadWithRequest:event]; + } if (_onLoadingStart) { // We have this check to filter out iframe requests and whatnot BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; @@ -159,6 +175,34 @@ - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLR return ![request.URL.scheme isEqualToString:RCTJSNavigationScheme]; } +- (BOOL)onShouldStartLoadWithRequest:(NSMutableDictionary *)event { + id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"webViewStartLoadNotification" object:nil queue:self.operationQueue usingBlock:^(NSNotification * _Nonnull note) { + NSDictionary *userInfo = note.userInfo; + + NSInteger lockIdentifier = ((NSNumber *)userInfo[@"lockIdentifier"]).longValue; + self.startLoadResult = ((NSNumber *)userInfo[@"result"]).boolValue; + + [self.conditionLock unlockWithCondition:lockIdentifier]; + self.conditionLock = nil; + }]; + + NSInteger lockIdentifier = arc4random(); + self.conditionLock = [[NSConditionLock alloc] initWithCondition:lockIdentifier]; + [self.conditionLock lock]; + + [event addEntriesFromDictionary: @{ + @"lockIdentifier": @(lockIdentifier) + }]; + _onShouldStartLoadWithRequest(event); + + [self.conditionLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; + + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + + return self.startLoadResult; +} + + - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error { if (_onLoadingError) { diff --git a/React/Views/RCTWebViewManager.m b/React/Views/RCTWebViewManager.m index 8779a970baa81d..c69636f997b22b 100644 --- a/React/Views/RCTWebViewManager.m +++ b/React/Views/RCTWebViewManager.m @@ -34,6 +34,7 @@ - (UIView *)view 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 { @@ -50,6 +51,14 @@ - (NSDictionary *)constantsToExport }; } +RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier) { + NSDictionary *userInfo = @{ + @"result": @(result), + @"lockIdentifier": @(lockIdentifier) + }; + [[NSNotificationCenter defaultCenter] postNotificationName:@"webViewStartLoadNotification" object:self userInfo:userInfo]; +} + RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {