Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugins): Better define plugin behaviour protocols #1514

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@erisu Updated this to be willHandleAuthenticationChallenge and return a BOOL so that multiple plugins can have a chance to respond


@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
Loading