Skip to content

Commit

Permalink
Implement 'onShouldStartLoadWithRequest' prop
Browse files Browse the repository at this point in the history
Summary:
@public

This diff introduces the native backend for a new WKWebView prop: `onShouldStartLoadWithRequest`.

In the final component, the behaviour will be as follows: Whenever the user navigates around in the web view, we call `onShouldStartLoadWithRequest` with the navigation event. If `onShouldStartLoadWithRequest` returns `true`, we continue the navigation. Otherwise, we abort it.

Reviewed By: shergin

Differential Revision: D6370317

fbshipit-source-id: e3cdd7e2a755125aebdb6df67e7b39116228bdfb
  • Loading branch information
RSNara authored and facebook-github-bot committed Aug 16, 2018
1 parent 1584108 commit a997c0a
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
5 changes: 5 additions & 0 deletions React/Views/RCTWKWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
@class RCTWKWebView;

@protocol RCTWKWebViewDelegate <NSObject>

- (BOOL)webView:(RCTWKWebView *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback;

@end

@interface RCTWKWebView : RCTView
Expand Down
15 changes: 15 additions & 0 deletions React/Views/RCTWKWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ @interface RCTWKWebView () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageH
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
@property (nonatomic, copy) RCTDirectEventBlock onMessage;
@property (nonatomic, copy) WKWebView *webView;
@end
Expand Down Expand Up @@ -154,6 +155,20 @@ - (void) webView:(WKWebView *)webView
WKNavigationType navigationType = navigationAction.navigationType;
NSURLRequest *request = navigationAction.request;

if (_onShouldStartLoadWithRequest) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": (request.URL).absoluteString,
@"navigationType": navigationTypes[@(navigationType)]
}];
if (![self.delegate webView:self
shouldStartLoadForRequest:event
withCallback:_onShouldStartLoadWithRequest]) {
decisionHandler(WKNavigationResponsePolicyCancel);
return;
}
}

if (_onLoadingStart) {
// We have this check to filter out iframe requests and whatnot
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
Expand Down
46 changes: 45 additions & 1 deletion React/Views/RCTWKWebViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@
#import "RCTUIManager.h"
#import "RCTWKWebView.h"

@interface RCTWKWebViewManager () <RCTWKWebViewDelegate>
@end

@implementation RCTWKWebViewManager
{
NSConditionLock *_shouldStartLoadLock;
BOOL _shouldStartLoad;
}

RCT_EXPORT_MODULE()

- (UIView *)view
{
return [RCTWKWebView new];
RCTWKWebView *webView = [RCTWKWebView new];
webView.delegate = self;
return webView;
}

RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)

/**
Expand Down Expand Up @@ -105,4 +115,38 @@ - (UIView *)view
}];
}

#pragma mark - Exported synchronous methods

- (BOOL) webView:(RCTWKWebView *)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

0 comments on commit a997c0a

Please sign in to comment.