From 436ef670549ee94a961d4e820d5d57bdcfb0651d Mon Sep 17 00:00:00 2001 From: "Tamburro, Michael (WM Technology)" Date: Fri, 11 Dec 2020 13:56:03 -0500 Subject: [PATCH 1/7] feat: add CDVWebViewEngineConfigurationDelegate to fully expose WKWebView configuration (#900) --- .../CDVWebViewEngine/CDVWebViewEngine.m | 18 +++++++++---- CordovaLib/Classes/Public/CDVViewController.h | 25 ++++++++++++++++++- CordovaLib/Classes/Public/CDVViewController.m | 7 +++--- .../Classes/Public/CDVWebViewEngineProtocol.h | 15 ++++++++--- 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m index a07bf290a..029c2d9a8 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m +++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m @@ -45,6 +45,7 @@ @interface CDVWebViewEngine () @property (nonatomic, strong) CDVURLSchemeHandler * schemeHandler; @property (nonatomic, readwrite) NSString *CDV_ASSETS_URL; @property (nonatomic, readwrite) Boolean cdvIsFileScheme; +@property (nullable, nonatomic, strong, readwrite) WKWebViewConfiguration *configuration; @end @@ -55,15 +56,16 @@ @implementation CDVWebViewEngine @synthesize engineWebView = _engineWebView; -- (instancetype)initWithFrame:(CGRect)frame +- (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKWebViewConfiguration *)configuration { self = [super init]; if (self) { if (NSClassFromString(@"WKWebView") == nil) { return nil; } - - self.engineWebView = [[WKWebView alloc] initWithFrame:frame]; + + self.configuration = configuration; + self.engineWebView = configuration ? [[WKWebView alloc] initWithFrame:frame configuration:configuration] : [[WKWebView alloc] initWithFrame:frame]; } return self; @@ -71,8 +73,14 @@ - (instancetype)initWithFrame:(CGRect)frame - (WKWebViewConfiguration*) createConfigurationFromSettings:(NSDictionary*)settings { - WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; - configuration.processPool = [[CDVWebViewProcessPoolFactory sharedFactory] sharedProcessPool]; + WKWebViewConfiguration* configuration; + if (_configuration) { + configuration = _configuration; + } else { + configuration = [[WKWebViewConfiguration alloc] init]; + configuration.processPool = [[CDVWebViewProcessPoolFactory sharedFactory] sharedProcessPool]; + } + if (settings == nil) { return configuration; } diff --git a/CordovaLib/Classes/Public/CDVViewController.h b/CordovaLib/Classes/Public/CDVViewController.h index 4b5e423a4..cc92a829c 100644 --- a/CordovaLib/Classes/Public/CDVViewController.h +++ b/CordovaLib/Classes/Public/CDVViewController.h @@ -26,6 +26,25 @@ #import "CDVScreenOrientationDelegate.h" #import "CDVPlugin.h" #import "CDVWebViewEngineProtocol.h" +@import WebKit; + +@protocol CDVWebViewEngineConfigurationDelegate + +@optional +/// Provides a fully configured WKWebViewConfiguration which will be overriden with +/// any related settings you add to config.xml (e.g., `PreferredContentMode`). +/// Useful for more complex configuration, including websiteDataStore. +/// +/// Example usage: +/// +/// extension CDVViewController: CDVWebViewEngineConfigurationDelegate { +/// public func configuration() -> WKWebViewConfiguration { +/// // return your config here +/// } +/// } +- (nonnull WKWebViewConfiguration*)configuration; + +@end @interface CDVViewController : UIViewController { @protected @@ -36,9 +55,11 @@ CDVCommandQueue* _commandQueue; } +NS_ASSUME_NONNULL_BEGIN + @property (nonatomic, readonly, weak) IBOutlet UIView* webView; -@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; +@property (nullable, nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; @property (nonatomic, readonly, strong) NSDictionary* pluginsMap; @property (nonatomic, readonly, strong) NSMutableDictionary* settings; @property (nonatomic, readonly, strong) NSXMLParser* configParser; @@ -74,4 +95,6 @@ - (void)showLaunchScreen:(BOOL)visible; +NS_ASSUME_NONNULL_END + @end diff --git a/CordovaLib/Classes/Public/CDVViewController.m b/CordovaLib/Classes/Public/CDVViewController.m index 9318d4651..415728348 100644 --- a/CordovaLib/Classes/Public/CDVViewController.m +++ b/CordovaLib/Classes/Public/CDVViewController.m @@ -27,7 +27,7 @@ Licensed to the Apache Software Foundation (ASF) under one #import "CDVCommandDelegateImpl.h" #import -@interface CDVViewController () { } +@interface CDVViewController () { } @property (nonatomic, readwrite, strong) NSXMLParser* configParser; @property (nonatomic, readwrite, strong) NSMutableDictionary* settings; @@ -505,9 +505,10 @@ - (UIView*)newCordovaViewWithFrame:(CGRect)bounds self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds]; } } else { - self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds]; + WKWebViewConfiguration *config = [self respondsToSelector:@selector(configuration)] ? [self configuration] : nil; + self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds configuration:config]; } - + if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) { [self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass]; } diff --git a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h index e6126a4b4..b976fa707 100644 --- a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h +++ b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h @@ -18,6 +18,7 @@ */ #import +@import WebKit; #define kCDVWebViewEngineScriptMessageHandlers @"kCDVWebViewEngineScriptMessageHandlers" #define kCDVWebViewEngineWKNavigationDelegate @"kCDVWebViewEngineWKNavigationDelegate" @@ -26,16 +27,24 @@ @protocol CDVWebViewEngineProtocol +NS_ASSUME_NONNULL_BEGIN + @property (nonatomic, strong, readonly) UIView* engineWebView; - (id)loadRequest:(NSURLRequest*)request; -- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL; -- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^)(id, NSError*))completionHandler; +- (id)loadHTMLString:(NSString*)string baseURL:(nullable NSURL*)baseURL; +- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^_Nullable)(id, NSError*))completionHandler; - (NSURL*)URL; - (BOOL)canLoadRequest:(NSURLRequest*)request; -- (instancetype)initWithFrame:(CGRect)frame; +/// Designated Initializer +/// @param frame The frame for the new web view. +/// @param configuration The configuration for the new web view. +- (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKWebViewConfiguration *)configuration; + - (void)updateWithInfo:(NSDictionary*)info; +NS_ASSUME_NONNULL_END + @end From d2de6207fb390d2b0eaf4dea53e598c27c88e852 Mon Sep 17 00:00:00 2001 From: "Tamburro, Michael (WM Technology)" Date: Fri, 11 Dec 2020 16:06:51 -0500 Subject: [PATCH 2/7] fix: reduce crashing, clarify documentation, and improve performance; note: works around, rather than addresses existing architectural issues (#1031) --- .../CDVWebViewEngine/CDVWebViewEngine.m | 95 +++++++++++-------- CordovaLib/Classes/Public/CDVViewController.h | 25 ++++- CordovaLib/Classes/Public/CDVViewController.m | 7 +- .../Classes/Public/CDVWebViewEngineProtocol.h | 21 +++- 4 files changed, 101 insertions(+), 47 deletions(-) diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m index a07bf290a..8297b9448 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m +++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m @@ -45,6 +45,7 @@ @interface CDVWebViewEngine () @property (nonatomic, strong) CDVURLSchemeHandler * schemeHandler; @property (nonatomic, readwrite) NSString *CDV_ASSETS_URL; @property (nonatomic, readwrite) Boolean cdvIsFileScheme; +@property (nullable, nonatomic, strong, readwrite) WKWebViewConfiguration *configuration; @end @@ -55,15 +56,12 @@ @implementation CDVWebViewEngine @synthesize engineWebView = _engineWebView; -- (instancetype)initWithFrame:(CGRect)frame +- (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKWebViewConfiguration *)configuration { self = [super init]; if (self) { - if (NSClassFromString(@"WKWebView") == nil) { - return nil; - } - - self.engineWebView = [[WKWebView alloc] initWithFrame:frame]; + self.configuration = configuration; + self.engineWebView = configuration ? [[WKWebView alloc] initWithFrame:frame configuration:configuration] : [[WKWebView alloc] initWithFrame:frame]; } return self; @@ -71,8 +69,14 @@ - (instancetype)initWithFrame:(CGRect)frame - (WKWebViewConfiguration*) createConfigurationFromSettings:(NSDictionary*)settings { - WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; - configuration.processPool = [[CDVWebViewProcessPoolFactory sharedFactory] sharedProcessPool]; + WKWebViewConfiguration* configuration; + if (_configuration) { + configuration = _configuration; + } else { + configuration = [[WKWebViewConfiguration alloc] init]; + configuration.processPool = [[CDVWebViewProcessPoolFactory sharedFactory] sharedProcessPool]; + } + if (settings == nil) { return configuration; } @@ -481,7 +485,7 @@ - (void)userContentController:(WKUserContentController*)userContentController di } } -#pragma mark WKNavigationDelegate implementation +#pragma mark - WKNavigationDelegate implementation - (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation { @@ -531,44 +535,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/Classes/Public/CDVViewController.h b/CordovaLib/Classes/Public/CDVViewController.h index 4b5e423a4..cc92a829c 100644 --- a/CordovaLib/Classes/Public/CDVViewController.h +++ b/CordovaLib/Classes/Public/CDVViewController.h @@ -26,6 +26,25 @@ #import "CDVScreenOrientationDelegate.h" #import "CDVPlugin.h" #import "CDVWebViewEngineProtocol.h" +@import WebKit; + +@protocol CDVWebViewEngineConfigurationDelegate + +@optional +/// Provides a fully configured WKWebViewConfiguration which will be overriden with +/// any related settings you add to config.xml (e.g., `PreferredContentMode`). +/// Useful for more complex configuration, including websiteDataStore. +/// +/// Example usage: +/// +/// extension CDVViewController: CDVWebViewEngineConfigurationDelegate { +/// public func configuration() -> WKWebViewConfiguration { +/// // return your config here +/// } +/// } +- (nonnull WKWebViewConfiguration*)configuration; + +@end @interface CDVViewController : UIViewController { @protected @@ -36,9 +55,11 @@ CDVCommandQueue* _commandQueue; } +NS_ASSUME_NONNULL_BEGIN + @property (nonatomic, readonly, weak) IBOutlet UIView* webView; -@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; +@property (nullable, nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; @property (nonatomic, readonly, strong) NSDictionary* pluginsMap; @property (nonatomic, readonly, strong) NSMutableDictionary* settings; @property (nonatomic, readonly, strong) NSXMLParser* configParser; @@ -74,4 +95,6 @@ - (void)showLaunchScreen:(BOOL)visible; +NS_ASSUME_NONNULL_END + @end diff --git a/CordovaLib/Classes/Public/CDVViewController.m b/CordovaLib/Classes/Public/CDVViewController.m index 9318d4651..415728348 100644 --- a/CordovaLib/Classes/Public/CDVViewController.m +++ b/CordovaLib/Classes/Public/CDVViewController.m @@ -27,7 +27,7 @@ Licensed to the Apache Software Foundation (ASF) under one #import "CDVCommandDelegateImpl.h" #import -@interface CDVViewController () { } +@interface CDVViewController () { } @property (nonatomic, readwrite, strong) NSXMLParser* configParser; @property (nonatomic, readwrite, strong) NSMutableDictionary* settings; @@ -505,9 +505,10 @@ - (UIView*)newCordovaViewWithFrame:(CGRect)bounds self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds]; } } else { - self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds]; + WKWebViewConfiguration *config = [self respondsToSelector:@selector(configuration)] ? [self configuration] : nil; + self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds configuration:config]; } - + if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) { [self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass]; } diff --git a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h index e6126a4b4..4230dfc66 100644 --- a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h +++ b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h @@ -18,24 +18,37 @@ */ #import +@import WebKit; #define kCDVWebViewEngineScriptMessageHandlers @"kCDVWebViewEngineScriptMessageHandlers" #define kCDVWebViewEngineWKNavigationDelegate @"kCDVWebViewEngineWKNavigationDelegate" #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 WKNaviagationDelegate +/// [b] called `updateWithInfo` and specified a kCDVWebViewEngineWKNavigationDelegate +/// It would be more explicit to set the navigation delegate in the designated initializer below, but I'll avoid doing that because these two other ways already exist. +@protocol CDVWebViewEngineProtocol + +NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly) UIView* engineWebView; - (id)loadRequest:(NSURLRequest*)request; -- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL; -- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^)(id, NSError*))completionHandler; +- (id)loadHTMLString:(NSString*)string baseURL:(nullable NSURL*)baseURL; +- (void)evaluateJavaScript:(NSString*)javaScriptString completionHandler:(void (^_Nullable)(id, NSError*))completionHandler; - (NSURL*)URL; - (BOOL)canLoadRequest:(NSURLRequest*)request; -- (instancetype)initWithFrame:(CGRect)frame; +/// Designated Initializer +/// @param frame The frame for the new web view. +/// @param configuration The configuration for the new web view. +- (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKWebViewConfiguration *)configuration; + - (void)updateWithInfo:(NSDictionary*)info; +NS_ASSUME_NONNULL_END + @end From cecab7ce50b291b8238aaa155c5c44b7cd053183 Mon Sep 17 00:00:00 2001 From: "Tamburro, Michael (WM Technology)" Date: Tue, 5 Jan 2021 10:50:16 -0500 Subject: [PATCH 3/7] fix: update the existing tests of the default behavior --- tests/CordovaLibTests/CDVWebViewEngineTest.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CordovaLibTests/CDVWebViewEngineTest.m b/tests/CordovaLibTests/CDVWebViewEngineTest.m index 0facc70fa..1f00694bd 100644 --- a/tests/CordovaLibTests/CDVWebViewEngineTest.m +++ b/tests/CordovaLibTests/CDVWebViewEngineTest.m @@ -53,7 +53,7 @@ - (void)setUp { // Put setup code here. This method is called before the invocation of each test method in the class. // NOTE: no app settings are set, so it will rely on default WKWebViewConfiguration settings - self.plugin = [[CDVWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + self.plugin = [[CDVWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100) configuration:nil]; XCTAssert([self.plugin conformsToProtocol:@protocol(CDVWebViewEngineProtocol)], @"Plugin does not conform to CDVWebViewEngineProtocol"); } @@ -126,7 +126,7 @@ - (void) testUpdateInfo { - (void) testConfigurationFromSettings { // we need to re-set the plugin from the "setup" to take in the app settings we need - self.plugin = [[CDVWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + self.plugin = [[CDVWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100) configuration:nil]; self.viewController = [[CDVViewController alloc] init]; // generate the app settings From 3cd8bdac97169835e55eec023ceb2df812cad24c Mon Sep 17 00:00:00 2001 From: "Tamburro, Michael (WM Technology)" Date: Tue, 5 Jan 2021 12:31:48 -0500 Subject: [PATCH 4/7] chore: bring back initWithFrame, as existing CDVWebViewEngineProtocol conforming plugins use it --- .../Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m | 5 +++++ CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h | 3 ++- tests/CordovaLibTests/CDVWebViewEngineTest.m | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m index 029c2d9a8..5be8a41e7 100644 --- a/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m +++ b/CordovaLib/Classes/Private/Plugins/CDVWebViewEngine/CDVWebViewEngine.m @@ -71,6 +71,11 @@ - (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKW return self; } +- (nullable instancetype)initWithFrame:(CGRect)frame +{ + return [self initWithFrame:frame configuration:nil]; +} + - (WKWebViewConfiguration*) createConfigurationFromSettings:(NSDictionary*)settings { WKWebViewConfiguration* configuration; diff --git a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h index b976fa707..8a562a704 100644 --- a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h +++ b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h @@ -37,8 +37,9 @@ NS_ASSUME_NONNULL_BEGIN - (NSURL*)URL; - (BOOL)canLoadRequest:(NSURLRequest*)request; +- (nullable instancetype)initWithFrame:(CGRect)frame; -/// Designated Initializer +/// Convenience Initializer /// @param frame The frame for the new web view. /// @param configuration The configuration for the new web view. - (nullable instancetype)initWithFrame:(CGRect)frame configuration:(nullable WKWebViewConfiguration *)configuration; diff --git a/tests/CordovaLibTests/CDVWebViewEngineTest.m b/tests/CordovaLibTests/CDVWebViewEngineTest.m index 1f00694bd..962d9434d 100644 --- a/tests/CordovaLibTests/CDVWebViewEngineTest.m +++ b/tests/CordovaLibTests/CDVWebViewEngineTest.m @@ -53,7 +53,7 @@ - (void)setUp { // Put setup code here. This method is called before the invocation of each test method in the class. // NOTE: no app settings are set, so it will rely on default WKWebViewConfiguration settings - self.plugin = [[CDVWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100) configuration:nil]; + self.plugin = [[CDVWebViewEngine alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; XCTAssert([self.plugin conformsToProtocol:@protocol(CDVWebViewEngineProtocol)], @"Plugin does not conform to CDVWebViewEngineProtocol"); } From 9f86588c1f508818c0693eebf5d46c7d29d23f13 Mon Sep 17 00:00:00 2001 From: "Tamburro, Michael (WM Technology)" Date: Tue, 5 Jan 2021 12:51:00 -0500 Subject: [PATCH 5/7] chore: clean up comments --- CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h index 1eb95ba76..27e7f34a8 100644 --- a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h +++ b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h @@ -26,9 +26,9 @@ #define kCDVWebViewEngineWebViewPreferences @"kCDVWebViewEngineWebViewPreferences" /// Worth noting: by default, the CDVWebViewEngine is the WKNavigationDelegate, but only if you haven't either: -/// [a] extended CDVViewController to be a WKNaviagationDelegate +/// [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 designated initializer below, but I'll avoid doing that because these two other ways already exist. +/// 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 From d121e10cfd5236d376b69fc50a54e91513eee7fa Mon Sep 17 00:00:00 2001 From: "Tamburro, Michael (WM Technology)" Date: Wed, 6 Jan 2021 16:37:38 -0500 Subject: [PATCH 6/7] chore: clean up newCordovaViewWithFrame in order to extract initialization with configuration --- CordovaLib/Classes/Public/CDVViewController.m | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/CordovaLib/Classes/Public/CDVViewController.m b/CordovaLib/Classes/Public/CDVViewController.m index 415728348..8277b1627 100644 --- a/CordovaLib/Classes/Public/CDVViewController.m +++ b/CordovaLib/Classes/Public/CDVViewController.m @@ -485,37 +485,61 @@ - (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation return [self.supportedOrientations containsObject:@(orientation)]; } -- (UIView*)newCordovaViewWithFrame:(CGRect)bounds +/// Retrieves the view from a newwly initialized webViewEngine +/// @param bounds The bounds with which the webViewEngine will be initialized +- (nonnull UIView*)newCordovaViewWithFrame:(CGRect)bounds { - NSString* defaultWebViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaDefaultWebViewEngine"]; - NSString* webViewEngineClass = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"]; + NSString* defaultWebViewEngineClassName = [self.settings cordovaSettingForKey:@"CordovaDefaultWebViewEngine"]; + NSString* webViewEngineClassName = [self.settings cordovaSettingForKey:@"CordovaWebViewEngine"]; - if (!defaultWebViewEngineClass) { - defaultWebViewEngineClass = @"CDVWebViewEngine"; + if (!defaultWebViewEngineClassName) { + defaultWebViewEngineClassName = @"CDVWebViewEngine"; } - if (!webViewEngineClass) { - webViewEngineClass = defaultWebViewEngineClass; + if (!webViewEngineClassName) { + webViewEngineClassName = defaultWebViewEngineClassName; } - // Find webViewEngine - if (NSClassFromString(webViewEngineClass)) { - self.webViewEngine = [[NSClassFromString(webViewEngineClass) alloc] initWithFrame:bounds]; - // if a webView engine returns nil (not supported by the current iOS version) or doesn't conform to the protocol, or can't load the request, we use WKWebView - if (!self.webViewEngine || ![self.webViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)] || ![self.webViewEngine canLoadRequest:[NSURLRequest requestWithURL:self.appUrl]]) { - self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds]; + // Determine if a provided custom web view engine is sufficient + id engine; + Class customWebViewEngineClass = NSClassFromString(webViewEngineClassName); + if (customWebViewEngineClass) { + id customWebViewEngine = [self initWebViewEngine:customWebViewEngineClass bounds:bounds]; + BOOL customConformsToProtocol = [customWebViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)]; + BOOL customCanLoad = [customWebViewEngine canLoadRequest:[NSURLRequest requestWithURL:self.appUrl]]; + if (customConformsToProtocol && customCanLoad) { + engine = customWebViewEngine; } - } else { - WKWebViewConfiguration *config = [self respondsToSelector:@selector(configuration)] ? [self configuration] : nil; - self.webViewEngine = [[NSClassFromString(defaultWebViewEngineClass) alloc] initWithFrame:bounds configuration:config]; } - if ([self.webViewEngine isKindOfClass:[CDVPlugin class]]) { - [self registerPlugin:(CDVPlugin*)self.webViewEngine withClassName:webViewEngineClass]; + // Otherwise use the default web view engine + if (!engine) { + Class defaultWebViewEngineClass = NSClassFromString(defaultWebViewEngineClassName); + id defaultWebViewEngine = [self initWebViewEngine:defaultWebViewEngineClass bounds:bounds]; + NSAssert([defaultWebViewEngine conformsToProtocol:@protocol(CDVWebViewEngineProtocol)], + @"we expected the default web view engine to conform to the CDVWebViewEngineProtocol"); + engine = defaultWebViewEngine; + } + + if ([engine isKindOfClass:[CDVPlugin class]]) { + [self registerPlugin:(CDVPlugin*)engine withClassName:webViewEngineClassName]; } + self.webViewEngine = engine; return self.webViewEngine.engineWebView; } +/// Initialiizes the webViewEngine, with config, if supported and provided +/// @param engineClass A class that must conform to the `CDVWebViewEngineProtocol` +/// @param bounds with which the webview will be initialized +- (id _Nullable) initWebViewEngine:(nonnull Class)engineClass bounds:(CGRect)bounds { + if ([engineClass respondsToSelector:@selector(initWithFrame:configuration:)]) { + WKWebViewConfiguration *config = [self respondsToSelector:@selector(configuration)] ? [self configuration] : nil; + return [[engineClass alloc] initWithFrame:bounds configuration:config]; + } else { + return [[engineClass alloc] initWithFrame:bounds]; + } +} + - (void)createLaunchView { CGRect webViewBounds = self.view.bounds; From 35b452ad66ac384bd5bf05ef2f07141428b7e84c Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 19 Oct 2022 11:58:49 -0400 Subject: [PATCH 7/7] chore: manually resolve merge issues (#1031) --- CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h index 09307df04..27e7f34a8 100644 --- a/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h +++ b/CordovaLib/Classes/Public/CDVWebViewEngineProtocol.h @@ -33,8 +33,6 @@ NS_ASSUME_NONNULL_BEGIN -NS_ASSUME_NONNULL_BEGIN - @property (nonatomic, strong, readonly) UIView* engineWebView; - (id)loadRequest:(NSURLRequest*)request;