diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m index ae5b47a9c..d453e71a5 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m +++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m @@ -60,10 +60,6 @@ - (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKW { self = [super init]; if (self) { - if (NSClassFromString(@"WKWebView") == nil) { - return nil; - } - self.configuration = configuration; self.engineWebView = configuration ? [[WKWebView alloc] initWithFrame:frame configuration:configuration] : [[WKWebView alloc] initWithFrame:frame]; } @@ -497,7 +493,7 @@ - (void)userContentController:(WKUserContentController*)userContentController di } } -#pragma mark WKNavigationDelegate implementation +#pragma mark - WKNavigationDelegate implementation - (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation { @@ -547,44 +543,57 @@ - (BOOL)defaultResourcePolicyForURL:(NSURL*)url - (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction*) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler { - NSURL* url = [navigationAction.request URL]; - CDVViewController* vc = (CDVViewController*)self.viewController; - - /* - * Give plugins the chance to handle the url - */ - BOOL anyPluginsResponded = NO; - BOOL shouldAllowRequest = NO; - - for (NSString* pluginName in vc.pluginObjects) { - CDVPlugin* plugin = [vc.pluginObjects objectForKey:pluginName]; - 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) { + __block BOOL anyPluginsResponded = NO; + __block BOOL shouldAllowRequest = NO; + CDVViewController* vc = (CDVViewController*)self.viewController; + NSDictionary *pluginObjects = [NSDictionary dictionaryWithDictionary:vc.pluginObjects]; + [pluginObjects enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull pluginName, CDVPlugin* _Nonnull plugin, BOOL * _Nonnull stop) { + if ([plugin respondsToSelector:[self navSelector]]) { + anyPluginsResponded = YES; + shouldAllowRequest = [self allows:plugin navigationAction:navigationAction]; + if (!shouldAllowRequest) { + *stop = YES; + } } + }]; + if (anyPluginsResponded) { + return decisionHandler(shouldAllowRequest); + } + } else { + // The default behavior is for just the CDVIntentAndNavigationFilter to handle this + CDVPlugin *intentAndNavFilter = [((CDVViewController*)self.viewController).pluginObjects objectForKey:@"CDVIntentAndNavigationFilter"]; + if ([intentAndNavFilter respondsToSelector:[self navSelector]]) { + return decisionHandler([self allows:intentAndNavFilter navigationAction:navigationAction]); } } - if (anyPluginsResponded) { - return decisionHandler(shouldAllowRequest); - } - - /* - * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview. - */ + // Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview. + NSURL* url = [navigationAction.request URL]; BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:url]; - if (shouldAllowNavigation) { - return decisionHandler(YES); - } else { + if (!shouldAllowNavigation) { [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; } - return decisionHandler(NO); + return decisionHandler(shouldAllowNavigation); +} + +#pragma mark WKNavigationDelegate private helpers + +/// Creates the selector corresponding to `shouldOverrideLoadWithRequest:navigationType:` +- (SEL)navSelector { + return NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:"); +} + +/// Determines if a request should be allowed +/// @param plugin that supports `shouldOverrideLoadWithRequest:navigationType:` +/// @param navigationAction from the WKNavigationDelegate containing a request +- (BOOL)allows:(CDVPlugin *)plugin navigationAction:(WKNavigationAction *)navigationAction { + // https://issues.apache.org/jira/browse/CB-12497 + int navType = (int)navigationAction.navigationType; + return (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, [self navSelector], navigationAction.request, navType)); } #pragma mark - Plugin interface diff --git a/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h b/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h index 2faf62d17..a3bbe342f 100644 --- a/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h +++ b/CordovaLib/include/Cordova/CDVWebViewEngineProtocol.h @@ -25,7 +25,11 @@ #define kCDVWebViewEngineWKUIDelegate @"kCDVWebViewEngineWKUIDelegate" #define kCDVWebViewEngineWebViewPreferences @"kCDVWebViewEngineWebViewPreferences" -@protocol CDVWebViewEngineProtocol +/// Worth noting: by default, the CDVWebViewEngine is the WKNavigationDelegate, but only if you haven't either: +/// [a] extended CDVViewController to be a WKNavigationDelegate +/// [b] called `updateWithInfo` and specified a kCDVWebViewEngineWKNavigationDelegate +/// It would be more explicit to set the navigation delegate in the convenience initializer below, but I'll avoid doing that because these two other ways already exist. +@protocol CDVWebViewEngineProtocol NS_ASSUME_NONNULL_BEGIN