Skip to content

Commit ec8bccb

Browse files
committed
Exported a callback for native webview delegate method webview:shouldStartLoadWithRequest:navigationType:
1 parent 9069bdf commit ec8bccb

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

Libraries/Components/WebView/WebView.ios.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ var WebView = React.createClass({
107107
* user can change the scale
108108
*/
109109
scalesPageToFit: PropTypes.bool,
110+
shouldStartLoadWithRequest: PropTypes.func,
110111
},
111112

112113
getInitialState: function() {
@@ -167,6 +168,7 @@ var WebView = React.createClass({
167168
onLoadingStart={this.onLoadingStart}
168169
onLoadingFinish={this.onLoadingFinish}
169170
onLoadingError={this.onLoadingError}
171+
onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
170172
scalesPageToFit={this.props.scalesPageToFit}
171173
/>;
172174

@@ -178,6 +180,10 @@ var WebView = React.createClass({
178180
);
179181
},
180182

183+
startLoadWithResult: function(result, lockIdentifier) {
184+
RCTWebViewManager.startLoadWithResult(result, lockIdentifier);
185+
},
186+
181187
goForward: function() {
182188
RCTWebViewManager.goForward(this.getWebViewHandle());
183189
},
@@ -224,13 +230,20 @@ var WebView = React.createClass({
224230
});
225231
this.updateNavigationState(event);
226232
},
233+
234+
onShouldStartLoadWithRequest: function(event: Event) {
235+
if (this.props.shouldStartLoadWithRequest) {
236+
this.props.shouldStartLoadWithRequest(event.nativeEvent);
237+
}
238+
},
227239
});
228240

229241
var RCTWebView = requireNativeComponent('RCTWebView', WebView, {
230242
nativeOnly: {
231243
onLoadingStart: true,
232244
onLoadingError: true,
233245
onLoadingFinish: true,
246+
onShouldStartLoadWithRequest: true
234247
},
235248
});
236249

React/Views/RCTWebView.m

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ @interface RCTWebView () <UIWebViewDelegate, RCTAutoInsetsProtocol>
2525
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
2626
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
2727
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
28+
@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
29+
@property (nonatomic, strong) NSConditionLock *conditionLock;
30+
@property (nonatomic, assign) BOOL startLoadResult;
31+
@property (nonatomic, strong) NSOperationQueue *operationQueue;
2832

2933
@end
3034

@@ -43,6 +47,8 @@ - (instancetype)initWithFrame:(CGRect)frame
4347
_webView = [[UIWebView alloc] initWithFrame:self.bounds];
4448
_webView.delegate = self;
4549
[self addSubview:_webView];
50+
51+
self.operationQueue = [[NSOperationQueue alloc] init];
4652
}
4753
return self;
4854
}
@@ -142,6 +148,16 @@ - (void)refreshContentInset
142148
- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
143149
navigationType:(UIWebViewNavigationType)navigationType
144150
{
151+
// skip this for the JS Navigation handler
152+
if (![request.URL.scheme isEqualToString:RCTJSNavigationScheme] &&
153+
_onShouldStartLoadWithRequest) {
154+
NSMutableDictionary *event = [self baseEvent];
155+
[event addEntriesFromDictionary: @{
156+
@"url": (request.URL).absoluteString,
157+
@"navigationType": @(navigationType)
158+
}];
159+
return [self onShouldStartLoadWithRequest:event];
160+
}
145161
if (_onLoadingStart) {
146162
// We have this check to filter out iframe requests and whatnot
147163
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
@@ -159,6 +175,34 @@ - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLR
159175
return ![request.URL.scheme isEqualToString:RCTJSNavigationScheme];
160176
}
161177

178+
- (BOOL)onShouldStartLoadWithRequest:(NSMutableDictionary *)event {
179+
id observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"webViewStartLoadNotification" object:nil queue:self.operationQueue usingBlock:^(NSNotification * _Nonnull note) {
180+
NSDictionary *userInfo = note.userInfo;
181+
182+
NSInteger lockIdentifier = ((NSNumber *)userInfo[@"lockIdentifier"]).longValue;
183+
self.startLoadResult = ((NSNumber *)userInfo[@"result"]).boolValue;
184+
185+
[self.conditionLock unlockWithCondition:lockIdentifier];
186+
self.conditionLock = nil;
187+
}];
188+
189+
NSInteger lockIdentifier = arc4random();
190+
self.conditionLock = [[NSConditionLock alloc] initWithCondition:lockIdentifier];
191+
[self.conditionLock lock];
192+
193+
[event addEntriesFromDictionary: @{
194+
@"lockIdentifier": @(lockIdentifier)
195+
}];
196+
_onShouldStartLoadWithRequest(event);
197+
198+
[self.conditionLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
199+
200+
[[NSNotificationCenter defaultCenter] removeObserver:observer];
201+
202+
return self.startLoadResult;
203+
}
204+
205+
162206
- (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error
163207
{
164208
if (_onLoadingError) {

React/Views/RCTWebViewManager.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ - (UIView *)view
3434
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock);
3535
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock);
3636
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock);
37+
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock);
3738

3839
- (NSDictionary *)constantsToExport
3940
{
@@ -50,6 +51,14 @@ - (NSDictionary *)constantsToExport
5051
};
5152
}
5253

54+
RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier) {
55+
NSDictionary *userInfo = @{
56+
@"result": @(result),
57+
@"lockIdentifier": @(lockIdentifier)
58+
};
59+
[[NSNotificationCenter defaultCenter] postNotificationName:@"webViewStartLoadNotification" object:self userInfo:userInfo];
60+
}
61+
5362
RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag)
5463
{
5564
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {

0 commit comments

Comments
 (0)