Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apple/RNCWebView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,13 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000 /* iOS 14 */
REMAP_WEBVIEW_PROP(limitsNavigationsToAppBoundDomains)
#endif
if(oldViewProps.preventUniversalLinks != newViewProps.preventUniversalLinks) {
NSMutableArray *preventUniversalLinks = [NSMutableArray array];
for (const auto &host: newViewProps.preventUniversalLinks) {
[preventUniversalLinks addObject: RCTNSStringFromString(host)];
}
[_view setPreventUniversalLinks:preventUniversalLinks];
}
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140500 /* iOS 14.5 */
REMAP_WEBVIEW_PROP(textInteractionEnabled)
#endif
Expand Down
2 changes: 2 additions & 0 deletions apple/RNCWebViewImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
@property (nonatomic, assign) BOOL limitsNavigationsToAppBoundDomains;
#endif

@property (nonatomic, copy) NSArray<NSString *> * _Nullable preventUniversalLinks;

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140500 /* iOS 14.5 */
@property (nonatomic, assign) BOOL textInteractionEnabled;
#endif
Expand Down
45 changes: 45 additions & 0 deletions apple/RNCWebViewImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,51 @@ - (void) webView:(WKWebView *)webView
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
BOOL hasTargetFrame = navigationAction.targetFrame != nil;

// Universal Link handoff bypass.
// When the request host matches an entry in `preventUniversalLinks`
// (domain-suffix match with dot boundary), the navigation is canceled
// and re-issued via `[webView loadRequest:]` — the one nav type iOS
// does not consider for Universal Link handoff. Without this, a
// navigation to a UL-eligible URL (e.g. an OAuth redirect targeting a
// host whose AASA registers Universal Links for this app) would hand
// the user off to the installed third-party app and break the
// embedded flow.
//
// Scoped to specified hosts on purpose: reissuing every top-frame nav
// would lose POST bodies and consume single-use auth codes when the
// chain runs through other hosts (e.g. an identity provider during
// OAuth).
//
// An associated-object flag breaks the cancel/reissue loop: the
// reissued nav re-enters this method with the flag set, we clear it
// and fall through to the normal handler.
if (_preventUniversalLinks.count > 0 && isTopFrame && hasTargetFrame) {
static const void *kRNCUlBypassKey = &kRNCUlBypassKey;
NSString *host = request.URL.host.lowercaseString ?: @"";
BOOL hostMatches = NO;
for (NSString *pattern in _preventUniversalLinks) {
NSString *lowered = pattern.lowercaseString ?: @"";
if ([host isEqualToString:lowered] ||
[host hasSuffix:[@"." stringByAppendingString:lowered]]) {
hostMatches = YES;
break;
}
}
if (hostMatches) {
NSNumber *bypassFlag = objc_getAssociatedObject(webView, kRNCUlBypassKey);
if ([bypassFlag boolValue]) {
objc_setAssociatedObject(webView, kRNCUlBypassKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} else {
objc_setAssociatedObject(webView, kRNCUlBypassKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
decisionHandler(WKNavigationActionPolicyCancel);
dispatch_async(dispatch_get_main_queue(), ^{
[webView loadRequest:request];
});
return;
}
}
}

if (_onOpenWindow && !hasTargetFrame) {
// When OnOpenWindow should be called, we want to prevent the navigation
// If not prevented, the `decisionHandler` is called first and after that `createWebViewWithConfiguration` is called
Expand Down
2 changes: 2 additions & 0 deletions apple/RNCWebViewManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ - (RNCView *)view
RCT_EXPORT_VIEW_PROPERTY(limitsNavigationsToAppBoundDomains, BOOL)
#endif

RCT_EXPORT_VIEW_PROPERTY(preventUniversalLinks, NSArray)

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140500 /* iOS 14.5 */
RCT_EXPORT_VIEW_PROPERTY(textInteractionEnabled, BOOL)
#endif
Expand Down
1 change: 1 addition & 0 deletions lib/RNCWebViewNativeComponent.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export interface NativeProps extends ViewProps {
hideKeyboardAccessoryView?: boolean;
keyboardDisplayRequiresUserAction?: WithDefault<boolean, true>;
limitsNavigationsToAppBoundDomains?: boolean;
preventUniversalLinks?: ReadonlyArray<string>;
mediaCapturePermissionGrantType?: WithDefault<'prompt' | 'grant' | 'deny' | 'grantIfSameHostElsePrompt' | 'grantIfSameHostElseDeny', 'prompt'>;
pagingEnabled?: boolean;
pullToRefreshEnabled?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion lib/RNCWebViewNativeComponent.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading