Skip to content

Commit

Permalink
refactor(plugins): Add CDVPluginNavigationHandler protocol
Browse files Browse the repository at this point in the history
This also adds a dictionary parameter containing the other navigation
action details so that plugins can make choices based on frames.

Closes apacheGH-1272.
Closes apacheGH-1333.

Co-Authored-By: Michael Tamburro <61243400+msmtamburro@users.noreply.github.com>
  • Loading branch information
dpogue and msmtamburro committed Dec 14, 2024
1 parent bdf99d2 commit 8c23b72
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@
#import <Cordova/CDVPlugin.h>
#import "CDVAllowList.h"

#define CDVWebViewNavigationType int

typedef NS_ENUM(NSInteger, CDVIntentAndNavigationFilterValue) {
CDVIntentAndNavigationFilterValueIntentAllowed,
CDVIntentAndNavigationFilterValueNavigationAllowed,
CDVIntentAndNavigationFilterValueNoneAllowed
};

@interface CDVIntentAndNavigationFilter : CDVPlugin <NSXMLParserDelegate>
@interface CDVIntentAndNavigationFilter : CDVPlugin <CDVPluginNavigationHandler, NSXMLParserDelegate>

+ (CDVIntentAndNavigationFilterValue) filterUrl:(NSURL*)url allowIntentsList:(CDVAllowList*)allowIntentsList navigationsAllowList:(CDVAllowList*)navigationsAllowList;
+ (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType filterValue:(CDVIntentAndNavigationFilterValue)filterValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ + (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDV
}
}

- (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType
- (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(CDVWebViewNavigationType)navigationType info:(NSDictionary *)navInfo
{
return [[self class] shouldOverrideLoadWithRequest:request navigationType:navigationType filterValue:[self filterUrl:request.URL]];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ - (UIView*)webView
return self.engineWebView;
}

#pragma mark WKScriptMessageHandler implementation
#pragma mark - WKScriptMessageHandler implementation

- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message
{
Expand Down Expand Up @@ -489,7 +489,7 @@ - (void)userContentController:(WKUserContentController*)userContentController di
}
}

#pragma mark WKNavigationDelegate implementation
#pragma mark - WKNavigationDelegate implementation

- (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation
{
Expand Down Expand Up @@ -537,45 +537,64 @@ - (BOOL)defaultResourcePolicyForURL:(NSURL*)url
return NO;
}

- (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction*) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL* url = [navigationAction.request URL];
CDVViewController* vc = (CDVViewController*)self.viewController;
CDVViewController *vc = (CDVViewController *)self.viewController;

/*
* Give plugins the chance to handle the url
*/
BOOL anyPluginsResponded = NO;
BOOL shouldAllowRequest = NO;
NSURLRequest *request = navigationAction.request;
CDVWebViewNavigationType navType = (CDVWebViewNavigationType)navigationAction.navigationType;
NSMutableDictionary *info = [NSMutableDictionary dictionary];
info[@"sourceFrame"] = navigationAction.sourceFrame;
info[@"targetFrame"] = navigationAction.targetFrame;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 140500
if (@available(iOS 14.5, *)) {
info[@"shouldPerformDownload"] = [NSNumber numberWithBool:navigationAction.shouldPerformDownload];
}
#endif

for (CDVPlugin *plugin in vc.enumerablePlugins) {
SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:");
if ([plugin respondsToSelector:selector]) {
anyPluginsResponded = YES;
// https://issues.apache.org/jira/browse/CB-12497
int navType = (int)navigationAction.navigationType;
shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, navigationAction.request, navType));
if (!shouldAllowRequest) {
break;
// Give plugins the chance to handle the url, as long as this WebViewEngine is still the WKNavigationDelegate.
// This allows custom delegates to choose to call this method for `default` cordova behavior without querying all plugins.
if (webView.navigationDelegate == self) {
BOOL anyPluginsResponded = NO;
BOOL shouldAllowRequest = NO;

for (CDVPlugin *plugin in vc.enumerablePlugins) {
if ([plugin respondsToSelector:@selector(shouldOverrideLoadWithRequest:navigationType:info:)] || [plugin respondsToSelector:@selector(shouldOverrideLoadWithRequest:navigationType:)]) {
CDVPlugin <CDVPluginNavigationHandler> *navPlugin = (CDVPlugin <CDVPluginNavigationHandler> *)plugin;
anyPluginsResponded = YES;

if ([navPlugin respondsToSelector:@selector(shouldOverrideLoadWithRequest:navigationType:info:)]) {
shouldAllowRequest = [navPlugin shouldOverrideLoadWithRequest:request navigationType:navType info:info];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
shouldAllowRequest = [navPlugin shouldOverrideLoadWithRequest:request navigationType:navType];
#pragma clang diagnostic pop
}

if (!shouldAllowRequest) {
break;
}
}
}
}

if (anyPluginsResponded) {
return decisionHandler(shouldAllowRequest);
}

/*
* Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
*/
BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:url];
if (shouldAllowNavigation) {
return decisionHandler(YES);
if (anyPluginsResponded) {
return decisionHandler(shouldAllowRequest ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}
} else {
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
CDVPlugin <CDVPluginNavigationHandler> *intentAndNavFilter = (CDVPlugin <CDVPluginNavigationHandler> *)[vc getCommandInstance:@"IntentAndNavigationFilter"];
if (intentAndNavFilter) {
BOOL shouldAllowRequest = [intentAndNavFilter shouldOverrideLoadWithRequest:request navigationType:navType info:info];
return decisionHandler(shouldAllowRequest ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}
}

return decisionHandler(NO);
// Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:request.URL];
if (!shouldAllowNavigation) {
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:request.URL userInfo:@{}]];
}
return decisionHandler(shouldAllowNavigation ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
Expand Down
3 changes: 2 additions & 1 deletion CordovaLib/CordovaLib.docc/CordovaLib.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ For more information about Apache Cordova, visit [https://cordova.apache.org](ht
### Cordova plugins

- ``CDVPlugin``
- ``CDVPluginSchemeHandler``
- ``CDVPluginAuthenticationHandler``
- ``CDVPluginNavigationHandler``
- ``CDVPluginSchemeHandler``

### Plugin communication
- ``CDVPluginResult``
Expand Down
95 changes: 65 additions & 30 deletions CordovaLib/include/Cordova/CDVPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
// Forward declaration to avoid bringing WebKit API into public headers
@protocol WKURLSchemeTask;

typedef int CDVWebViewNavigationType;

#ifndef __swift__
// This global extension to the UIView class causes issues for Swift subclasses
// of UIView with their own scrollView properties, so we're removing it from
Expand Down Expand Up @@ -87,6 +89,69 @@ extern const NSNotificationName CDVViewWillTransitionToSizeNotification;

#pragma mark - Plugin protocols

/**
A protocol for Cordova plugins to intercept and respond to server
authentication challenges through WebKit.
Your plugin should implement this protocol and the
``didReceiveAuthenticationChallenge:completionHandler:`` if it wants to
support responses to server-side authentication challenges, otherwise the
default NSURLSession handling for authentication challenges will be used.
*/
@protocol CDVPluginAuthenticationHandler <NSObject>

/**
Asks your plugin to respond to an authentication challenge.
- Parameters:
- challenge: The authentication challenge.
- completionHandler: A completion handler block to execute with the response.
This handler has no return value and takes the following parameters:
- disposition: The option to use to handle the challenge. For a list of
options, see `NSURLSessionAuthChallengeDisposition`.
- credential: The credential to use for authentication when the
`disposition` parameter contains the value
`NSURLSessionAuthChallengeUseCredential`. Specify `nil` to continue
without a credential.
*/
- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler;

@end


/**
A protocol for Cordova plugins to manage permitting and denying of webview
navigations.
You plugin should implement this protocol if it wants to control whether the
webview is allowed to navigate to a requested URL.
*/
@protocol CDVPluginNavigationHandler <NSObject>

/**
Asks your plugin to decide whether a navigation request should be permitted or
denied.
- Parameters:
- request: The navigation request.
- navigationType: The type of action triggering the navigation.
- navInfo: Descriptive information about the action triggering the navigation.
- Returns: A Boolean representing whether the navigation should be allowed or not.
*/
- (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest *)request navigationType:(CDVWebViewNavigationType)navigationType info:(NSDictionary *)navInfo;

@optional
/**
@DeprecationSummary {
Use ``shouldOverrideLoadWithRequest:navigationType:info:`` instead.
}
*/
- (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest *)request navigationType:(CDVWebViewNavigationType)navigationType CDV_DEPRECATED_WITH_REPLACEMENT(8, "Use shouldOverrideLoadWithRequest:navigationType:info: instead", "shouldOverrideLoadWithRequest:navigationType:info:");

@end


/**
A protocol for Cordova plugins to intercept handling of WebKit resource
loading for a custom URL scheme.
Expand Down Expand Up @@ -131,34 +196,4 @@ extern const NSNotificationName CDVViewWillTransitionToSizeNotification;
- (void)stopSchemeTask:(id <WKURLSchemeTask>)task;
@end


/**
A protocol for Cordova plugins to intercept and respond to server
authentication challenges through WebKit.
Your plugin should implement this protocol and the
``didReceiveAuthenticationChallenge:completionHandler:`` if it wants to
support responses to server-side authentication challenges, otherwise the
default NSURLSession handling for authentication challenges will be used.
*/
@protocol CDVPluginAuthenticationHandler <NSObject>

/**
Asks your plugin to respond to an authentication challenge.
- Parameters:
- challenge: The authentication challenge.
- completionHandler: A completion handler block to execute with the response.
This handler has no return value and takes the following parameters:
- disposition: The option to use to handle the challenge. For a list of
options, see `NSURLSessionAuthChallengeDisposition`.
- credential: The credential to use for authentication when the
`disposition` parameter contains the value
`NSURLSessionAuthChallengeUseCredential`. Specify `nil` to continue
without a credential.
*/
- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler;

@end

NS_ASSUME_NONNULL_END

0 comments on commit 8c23b72

Please sign in to comment.