Skip to content

Commit

Permalink
feat(plugins): Better define plugin behaviour protocols (#1514)
Browse files Browse the repository at this point in the history
* feat(plugins): Add plugin support for auth challenge responses

Closes GH-1212.

Co-Authored-By: Christopher J. Brody <[email protected]>

* refactor(plugins): Add CDVPluginNavigationHandler protocol

This also adds a dictionary parameter containing the other navigation
action details so that plugins can make choices based on frames.

Closes GH-1272.
Closes GH-1333.

Co-Authored-By: Michael Tamburro <[email protected]>

* fix(scrollview): Add nullable attribute to scrollView property

---------

Co-authored-by: Christopher J. Brody <[email protected]>
Co-authored-by: Michael Tamburro <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent d2db6e6 commit 82c1767
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 36 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 @@ -477,7 +477,7 @@ - (CDVWebViewPermissionGrantType)parsePermissionGrantType:(NSString*)optionStrin
return result;
}

#pragma mark WKScriptMessageHandler implementation
#pragma mark - WKScriptMessageHandler implementation

- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message
{
Expand Down Expand Up @@ -513,7 +513,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 @@ -561,45 +561,80 @@ - (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 ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}
} else {
CDVPlugin <CDVPluginNavigationHandler> *intentAndNavFilter = (CDVPlugin <CDVPluginNavigationHandler> *)[vc getCommandInstance:@"IntentAndNavigationFilter"];
if (intentAndNavFilter) {
BOOL shouldAllowRequest = [intentAndNavFilter shouldOverrideLoadWithRequest:request navigationType:navType info:info];
return decisionHandler(shouldAllowRequest ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}
}

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:request.URL];
if (!shouldAllowNavigation) {
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:request.URL userInfo:@{}]];
}
return decisionHandler(shouldAllowNavigation ? WKNavigationActionPolicyAllow : WKNavigationActionPolicyCancel);
}

/*
* 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);
} else {
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
CDVViewController* vc = (CDVViewController*)self.viewController;

for (CDVPlugin *plugin in vc.enumerablePlugins) {
if ([plugin respondsToSelector:@selector(willHandleAuthenticationChallenge:completionHandler:)]) {
CDVPlugin <CDVPluginAuthenticationHandler> *challengePlugin = (CDVPlugin <CDVPluginAuthenticationHandler> *)plugin;
if ([challengePlugin willHandleAuthenticationChallenge:challenge completionHandler:completionHandler]) {
return;
}
}
}

return decisionHandler(NO);
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

#pragma mark - Plugin interface
Expand Down
2 changes: 1 addition & 1 deletion CordovaLib/Classes/Public/CDVPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ - (void)handleOpenURL:(NSNotification*)notification
/*
NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts
*/
- (void)handleOpenURLWithApplicationSourceAndAnnotation: (NSNotification*)notification
- (void)handleOpenURLWithApplicationSourceAndAnnotation:(NSNotification*)notification
{

// override to handle urls sent to your app
Expand Down
2 changes: 2 additions & 0 deletions CordovaLib/CordovaLib.docc/CordovaLib.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ For more information about Apache Cordova, visit [https://cordova.apache.org](ht
### Cordova plugins

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

### Plugin communication
Expand Down
6 changes: 6 additions & 0 deletions CordovaLib/CordovaLib.docc/upgrading-8.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,12 @@ The following headers are deprecated due to adding global category extensions to
* The ``CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification`` notification is now deprecated.
The existing ``CDVPluginHandleOpenURLNotification`` notification now includes the source and annotation in its `userInfo` dictionary.

* ``CDVPluginAuthenticationHandler``
* Newly added protocol for plugins wishing to handle server authentication requests.

* ``CDVPluginNavigationHandler``
* Newly added protocol for plugins wishing to handle navigation request permitting or denying within the webview.

* ``CDVPluginSchemeHandler``
* Newly added protocol for plugins wishing to override WebKit scheme handling for web requests.

Expand Down
76 changes: 75 additions & 1 deletion CordovaLib/include/Cordova/CDVPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,20 @@
// 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
// the exposed Swift API and marking it as deprecated
// TODO: Remove in Cordova 9
@interface UIView (org_apache_cordova_UIView_Extension)
@property (nonatomic, weak) UIScrollView* scrollView CDV_DEPRECATED(8, "Check for a scrollView property on the view object at runtime and invoke it dynamically.");
@property (nonatomic, weak, nullable) UIScrollView* scrollView CDV_DEPRECATED(8, "Check for a scrollView property on the view object at runtime and invoke it dynamically.");
@end
#endif

NS_ASSUME_NONNULL_BEGIN

extern const NSNotificationName CDVPageDidLoadNotification;
extern const NSNotificationName CDVPluginHandleOpenURLNotification;
extern const NSNotificationName CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification CDV_DEPRECATED(8, "Find sourceApplication and annotations in the userInfo of the CDVPluginHandleOpenURLNotification notification.");
Expand Down Expand Up @@ -85,6 +89,74 @@ 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
``willHandleAuthenticationChallenge:completionHandler:`` method to return
`YES` 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.
Return `YES` if the plugin is handling the challenge, and `NO` to fallback to
the default handling.
- 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.
- Returns: A Boolean value indicating if the plugin is handling the request.
*/
- (BOOL)willHandleAuthenticationChallenge:(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 @@ -128,3 +200,5 @@ extern const NSNotificationName CDVViewWillTransitionToSizeNotification;
*/
- (void)stopSchemeTask:(id <WKURLSchemeTask>)task;
@end

NS_ASSUME_NONNULL_END

0 comments on commit 82c1767

Please sign in to comment.