diff options
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-wkwebview-engine/src')
7 files changed, 911 insertions, 0 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKProcessPoolFactory.h b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKProcessPoolFactory.h new file mode 100644 index 00000000..f4f8816e --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKProcessPoolFactory.h @@ -0,0 +1,27 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <WebKit/WebKit.h> + +@interface CDVWKProcessPoolFactory : NSObject +@property (nonatomic, retain) WKProcessPool* sharedPool; + ++(instancetype) sharedFactory; +-(WKProcessPool*) sharedProcessPool; +@end diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKProcessPoolFactory.m b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKProcessPoolFactory.m new file mode 100644 index 00000000..48ac09e6 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKProcessPoolFactory.m @@ -0,0 +1,49 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Foundation/Foundation.h> +#import <WebKit/WebKit.h> +#import "CDVWKProcessPoolFactory.h" + +static CDVWKProcessPoolFactory *factory = nil; + +@implementation CDVWKProcessPoolFactory + ++ (instancetype)sharedFactory +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + factory = [[CDVWKProcessPoolFactory alloc] init]; + }); + + return factory; +} + +- (instancetype)init +{ + if (self = [super init]) { + _sharedPool = [[WKProcessPool alloc] init]; + } + return self; +} + +- (WKProcessPool*) sharedProcessPool { + return _sharedPool; +} +@end diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewEngine.h b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewEngine.h new file mode 100644 index 00000000..2fe4bc21 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewEngine.h @@ -0,0 +1,27 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <WebKit/WebKit.h> +#import <Cordova/CDV.h> + +@interface CDVWKWebViewEngine : CDVPlugin <CDVWebViewEngineProtocol, WKScriptMessageHandler, WKNavigationDelegate> + +@property (nonatomic, strong, readonly) id <WKUIDelegate> uiDelegate; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewEngine.m b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewEngine.m new file mode 100644 index 00000000..e9ff2466 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewEngine.m @@ -0,0 +1,480 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVWKWebViewEngine.h" +#import "CDVWKWebViewUIDelegate.h" +#import "CDVWKProcessPoolFactory.h" +#import <Cordova/NSDictionary+CordovaPreferences.h> + +#import <objc/message.h> + +#define CDV_BRIDGE_NAME @"cordova" +#define CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR @"loadFileURL:allowingReadAccessToURL:" + +@interface CDVWKWeakScriptMessageHandler : NSObject <WKScriptMessageHandler> + +@property (nonatomic, weak, readonly) id<WKScriptMessageHandler>scriptMessageHandler; + +- (instancetype)initWithScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler; + +@end + + +@interface CDVWKWebViewEngine () + +@property (nonatomic, strong, readwrite) UIView* engineWebView; +@property (nonatomic, strong, readwrite) id <WKUIDelegate> uiDelegate; +@property (nonatomic, weak) id <WKScriptMessageHandler> weakScriptMessageHandler; + +@end + +// see forwardingTargetForSelector: selector comment for the reason for this pragma +#pragma clang diagnostic ignored "-Wprotocol" + +@implementation CDVWKWebViewEngine + +@synthesize engineWebView = _engineWebView; + +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super init]; + if (self) { + if (NSClassFromString(@"WKWebView") == nil) { + return nil; + } + + self.engineWebView = [[WKWebView alloc] initWithFrame:frame]; + } + + return self; +} + +- (WKWebViewConfiguration*) createConfigurationFromSettings:(NSDictionary*)settings +{ + WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; + configuration.processPool = [[CDVWKProcessPoolFactory sharedFactory] sharedProcessPool]; + if (settings == nil) { + return configuration; + } + + configuration.allowsInlineMediaPlayback = [settings cordovaBoolSettingForKey:@"AllowInlineMediaPlayback" defaultValue:NO]; + configuration.mediaPlaybackRequiresUserAction = [settings cordovaBoolSettingForKey:@"MediaPlaybackRequiresUserAction" defaultValue:YES]; + configuration.suppressesIncrementalRendering = [settings cordovaBoolSettingForKey:@"SuppressesIncrementalRendering" defaultValue:NO]; + configuration.mediaPlaybackAllowsAirPlay = [settings cordovaBoolSettingForKey:@"MediaPlaybackAllowsAirPlay" defaultValue:YES]; + return configuration; +} + +- (void)pluginInitialize +{ + // viewController would be available now. we attempt to set all possible delegates to it, by default + NSDictionary* settings = self.commandDelegate.settings; + + self.uiDelegate = [[CDVWKWebViewUIDelegate alloc] initWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]]; + + CDVWKWeakScriptMessageHandler *weakScriptMessageHandler = [[CDVWKWeakScriptMessageHandler alloc] initWithScriptMessageHandler:self]; + + WKUserContentController* userContentController = [[WKUserContentController alloc] init]; + [userContentController addScriptMessageHandler:weakScriptMessageHandler name:CDV_BRIDGE_NAME]; + + WKWebViewConfiguration* configuration = [self createConfigurationFromSettings:settings]; + configuration.userContentController = userContentController; + + // re-create WKWebView, since we need to update configuration + WKWebView* wkWebView = [[WKWebView alloc] initWithFrame:self.engineWebView.frame configuration:configuration]; + wkWebView.UIDelegate = self.uiDelegate; + self.engineWebView = wkWebView; + + if (IsAtLeastiOSVersion(@"9.0") && [self.viewController isKindOfClass:[CDVViewController class]]) { + wkWebView.customUserAgent = ((CDVViewController*) self.viewController).userAgent; + } + + if ([self.viewController conformsToProtocol:@protocol(WKUIDelegate)]) { + wkWebView.UIDelegate = (id <WKUIDelegate>)self.viewController; + } + + if ([self.viewController conformsToProtocol:@protocol(WKNavigationDelegate)]) { + wkWebView.navigationDelegate = (id <WKNavigationDelegate>)self.viewController; + } else { + wkWebView.navigationDelegate = (id <WKNavigationDelegate>)self; + } + + if ([self.viewController conformsToProtocol:@protocol(WKScriptMessageHandler)]) { + [wkWebView.configuration.userContentController addScriptMessageHandler:(id < WKScriptMessageHandler >)self.viewController name:CDV_BRIDGE_NAME]; + } + + [self updateSettings:settings]; + + // check if content thread has died on resume + NSLog(@"%@", @"CDVWKWebViewEngine will reload WKWebView if required on resume"); + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(onAppWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification object:nil]; + + NSLog(@"Using WKWebView"); + + [self addURLObserver]; +} + +- (void)onReset { + [self addURLObserver]; +} + +static void * KVOContext = &KVOContext; + +- (void)addURLObserver { + if(!IsAtLeastiOSVersion(@"9.0")){ + [self.webView addObserver:self forKeyPath:@"URL" options:0 context:KVOContext]; + } +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context +{ + if (context == KVOContext) { + if (object == [self webView] && [keyPath isEqualToString: @"URL"] && [object valueForKeyPath:keyPath] == nil){ + NSLog(@"URL is nil. Reloading WKWebView"); + [(WKWebView*)_engineWebView reload]; + } + } else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +- (void) onAppWillEnterForeground:(NSNotification*)notification { + if ([self shouldReloadWebView]) { + NSLog(@"%@", @"CDVWKWebViewEngine reloading!"); + [(WKWebView*)_engineWebView reload]; + } +} + +- (BOOL)shouldReloadWebView +{ + WKWebView* wkWebView = (WKWebView*)_engineWebView; + return [self shouldReloadWebView:wkWebView.URL title:wkWebView.title]; +} + +- (BOOL)shouldReloadWebView:(NSURL*)location title:(NSString*)title +{ + BOOL title_is_nil = (title == nil); + BOOL location_is_blank = [[location absoluteString] isEqualToString:@"about:blank"]; + + BOOL reload = (title_is_nil || location_is_blank); + +#ifdef DEBUG + NSLog(@"%@", @"CDVWKWebViewEngine shouldReloadWebView::"); + NSLog(@"CDVWKWebViewEngine shouldReloadWebView title: %@", title); + NSLog(@"CDVWKWebViewEngine shouldReloadWebView location: %@", [location absoluteString]); + NSLog(@"CDVWKWebViewEngine shouldReloadWebView reload: %u", reload); +#endif + + return reload; +} + + +- (id)loadRequest:(NSURLRequest*)request +{ + if ([self canLoadRequest:request]) { // can load, differentiate between file urls and other schemes + if (request.URL.fileURL) { + SEL wk_sel = NSSelectorFromString(CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR); + NSURL* readAccessUrl = [request.URL URLByDeletingLastPathComponent]; + return ((id (*)(id, SEL, id, id))objc_msgSend)(_engineWebView, wk_sel, request.URL, readAccessUrl); + } else { + return [(WKWebView*)_engineWebView loadRequest:request]; + } + } else { // can't load, print out error + NSString* errorHtml = [NSString stringWithFormat: + @"<!doctype html>" + @"<title>Error</title>" + @"<div style='font-size:2em'>" + @" <p>The WebView engine '%@' is unable to load the request: %@</p>" + @" <p>Most likely the cause of the error is that the loading of file urls is not supported in iOS %@.</p>" + @"</div>", + NSStringFromClass([self class]), + [request.URL description], + [[UIDevice currentDevice] systemVersion] + ]; + return [self loadHTMLString:errorHtml baseURL:nil]; + } +} + +- (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL +{ + return [(WKWebView*)_engineWebView loadHTMLString:string baseURL:baseURL]; +} + +- (NSURL*) URL +{ + return [(WKWebView*)_engineWebView URL]; +} + +- (BOOL) canLoadRequest:(NSURLRequest*)request +{ + // See: https://issues.apache.org/jira/browse/CB-9636 + SEL wk_sel = NSSelectorFromString(CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR); + + // if it's a file URL, check whether WKWebView has the selector (which is in iOS 9 and up only) + if (request.URL.fileURL) { + return [_engineWebView respondsToSelector:wk_sel]; + } else { + return YES; + } +} + +- (void)updateSettings:(NSDictionary*)settings +{ + WKWebView* wkWebView = (WKWebView*)_engineWebView; + + wkWebView.configuration.preferences.minimumFontSize = [settings cordovaFloatSettingForKey:@"MinimumFontSize" defaultValue:0.0]; + + /* + wkWebView.configuration.preferences.javaScriptEnabled = [settings cordovaBoolSettingForKey:@"JavaScriptEnabled" default:YES]; + wkWebView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = [settings cordovaBoolSettingForKey:@"JavaScriptCanOpenWindowsAutomatically" default:NO]; + */ + + // By default, DisallowOverscroll is false (thus bounce is allowed) + BOOL bounceAllowed = !([settings cordovaBoolSettingForKey:@"DisallowOverscroll" defaultValue:NO]); + + // prevent webView from bouncing + if (!bounceAllowed) { + if ([wkWebView respondsToSelector:@selector(scrollView)]) { + ((UIScrollView*)[wkWebView scrollView]).bounces = NO; + } else { + for (id subview in wkWebView.subviews) { + if ([[subview class] isSubclassOfClass:[UIScrollView class]]) { + ((UIScrollView*)subview).bounces = NO; + } + } + } + } + + NSString* decelerationSetting = [settings cordovaSettingForKey:@"WKWebViewDecelerationSpeed"]; + if (!decelerationSetting) { + // Fallback to the UIWebView-named preference + decelerationSetting = [settings cordovaSettingForKey:@"UIWebViewDecelerationSpeed"]; + } + + if (![@"fast" isEqualToString:decelerationSetting]) { + [wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal]; + } else { + [wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast]; + } + + wkWebView.allowsBackForwardNavigationGestures = [settings cordovaBoolSettingForKey:@"AllowBackForwardNavigationGestures" defaultValue:NO]; +} + +- (void)updateWithInfo:(NSDictionary*)info +{ + NSDictionary* scriptMessageHandlers = [info objectForKey:kCDVWebViewEngineScriptMessageHandlers]; + NSDictionary* settings = [info objectForKey:kCDVWebViewEngineWebViewPreferences]; + id navigationDelegate = [info objectForKey:kCDVWebViewEngineWKNavigationDelegate]; + id uiDelegate = [info objectForKey:kCDVWebViewEngineWKUIDelegate]; + + WKWebView* wkWebView = (WKWebView*)_engineWebView; + + if (scriptMessageHandlers && [scriptMessageHandlers isKindOfClass:[NSDictionary class]]) { + NSArray* allKeys = [scriptMessageHandlers allKeys]; + + for (NSString* key in allKeys) { + id object = [scriptMessageHandlers objectForKey:key]; + if ([object conformsToProtocol:@protocol(WKScriptMessageHandler)]) { + [wkWebView.configuration.userContentController addScriptMessageHandler:object name:key]; + } + } + } + + if (navigationDelegate && [navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]) { + wkWebView.navigationDelegate = navigationDelegate; + } + + if (uiDelegate && [uiDelegate conformsToProtocol:@protocol(WKUIDelegate)]) { + wkWebView.UIDelegate = uiDelegate; + } + + if (settings && [settings isKindOfClass:[NSDictionary class]]) { + [self updateSettings:settings]; + } +} + +// This forwards the methods that are in the header that are not implemented here. +// Both WKWebView and UIWebView implement the below: +// loadHTMLString:baseURL: +// loadRequest: +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + return _engineWebView; +} + +- (UIView*)webView +{ + return self.engineWebView; +} + +#pragma mark WKScriptMessageHandler implementation + +- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message +{ + if (![message.name isEqualToString:CDV_BRIDGE_NAME]) { + return; + } + + CDVViewController* vc = (CDVViewController*)self.viewController; + + NSArray* jsonEntry = message.body; // NSString:callbackId, NSString:service, NSString:action, NSArray:args + CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry]; + CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName); + + if (![vc.commandQueue execute:command]) { +#ifdef DEBUG + NSError* error = nil; + NSString* commandJson = nil; + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:jsonEntry + options:0 + error:&error]; + + if (error == nil) { + commandJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + } + + static NSUInteger maxLogLength = 1024; + NSString* commandString = ([commandJson length] > maxLogLength) ? + [NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] : + commandJson; + + NSLog(@"FAILED pluginJSON = %@", commandString); +#endif + } +} + +#pragma mark WKNavigationDelegate implementation + +- (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation +{ + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:webView]]; +} + +- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation +{ + CDVViewController* vc = (CDVViewController*)self.viewController; + [CDVUserAgentUtil releaseLock:vc.userAgentLockToken]; + + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:webView]]; +} + +- (void)webView:(WKWebView*)theWebView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error +{ + [self webView:theWebView didFailNavigation:navigation withError:error]; +} + +- (void)webView:(WKWebView*)theWebView didFailNavigation:(WKNavigation*)navigation withError:(NSError*)error +{ + CDVViewController* vc = (CDVViewController*)self.viewController; + [CDVUserAgentUtil releaseLock:vc.userAgentLockToken]; + + NSString* message = [NSString stringWithFormat:@"Failed to load webpage with error: %@", [error localizedDescription]]; + NSLog(@"%@", message); + + NSURL* errorUrl = vc.errorURL; + if (errorUrl) { + errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [message stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl]; + NSLog(@"%@", [errorUrl absoluteString]); + [theWebView loadRequest:[NSURLRequest requestWithURL:errorUrl]]; + } +} + +- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView +{ + [webView reload]; +} + +- (BOOL)defaultResourcePolicyForURL:(NSURL*)url +{ + // all file:// urls are allowed + if ([url isFileURL]) { + return YES; + } + + return NO; +} + +- (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; + if (WKNavigationTypeOther == navigationAction.navigationType) { + navType = (int)UIWebViewNavigationTypeOther; + } + shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, navigationAction.request, navType)); + 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); + } else { + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + } + + return decisionHandler(NO); +} + +@end + +#pragma mark - CDVWKWeakScriptMessageHandler + +@implementation CDVWKWeakScriptMessageHandler + +- (instancetype)initWithScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler +{ + self = [super init]; + if (self) { + _scriptMessageHandler = scriptMessageHandler; + } + return self; +} + +- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message +{ + [self.scriptMessageHandler userContentController:userContentController didReceiveScriptMessage:message]; +} + +@end diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewUIDelegate.h b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewUIDelegate.h new file mode 100644 index 00000000..33a179b0 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewUIDelegate.h @@ -0,0 +1,28 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <WebKit/WebKit.h> + +@interface CDVWKWebViewUIDelegate : NSObject <WKUIDelegate> + +@property (nonatomic, copy) NSString* title; + +- (instancetype)initWithTitle:(NSString*)title; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewUIDelegate.m b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewUIDelegate.m new file mode 100644 index 00000000..a7a16f2c --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/ios/CDVWKWebViewUIDelegate.m @@ -0,0 +1,123 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVWKWebViewUIDelegate.h" + +@implementation CDVWKWebViewUIDelegate + +- (instancetype)initWithTitle:(NSString*)title +{ + self = [super init]; + if (self) { + self.title = title; + } + + return self; +} + +- (void) webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + + [rootController presentViewController:alert animated:YES completion:nil]; +} + +- (void) webView:(WKWebView*)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL result))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(YES); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(NO); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + [alert addAction:cancel]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + + [rootController presentViewController:alert animated:YES completion:nil]; +} + +- (void) webView:(WKWebView*)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt + defaultText:(NSString*)defaultText initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(NSString* result))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:prompt + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(((UITextField*)alert.textFields[0]).text); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(nil); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + [alert addAction:cancel]; + + [alert addTextFieldWithConfigurationHandler:^(UITextField* textField) { + textField.text = defaultText; + }]; + + UIViewController* rootController = [UIApplication sharedApplication].delegate.window.rootViewController; + + [rootController presentViewController:alert animated:YES completion:nil]; +} + +@end diff --git a/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/www/ios/ios-wkwebview-exec.js b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/www/ios/ios-wkwebview-exec.js new file mode 100644 index 00000000..2766acc4 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-wkwebview-engine/src/www/ios/ios-wkwebview-exec.js @@ -0,0 +1,177 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +/** + * Creates the exec bridge used to notify the native code of + * commands. + */ +var cordova = require('cordova'), + utils = require('cordova/utils'), + base64 = require('cordova/base64'); + +function massageArgsJsToNative(args) { + if (!args || utils.typeName(args) != 'Array') { + return args; + } + var ret = []; + args.forEach(function(arg, i) { + if (utils.typeName(arg) == 'ArrayBuffer') { + ret.push({ + 'CDVType': 'ArrayBuffer', + 'data': base64.fromArrayBuffer(arg) + }); + } else { + ret.push(arg); + } + }); + return ret; +} + +function massageMessageNativeToJs(message) { + if (message.CDVType == 'ArrayBuffer') { + var stringToArrayBuffer = function(str) { + var ret = new Uint8Array(str.length); + for (var i = 0; i < str.length; i++) { + ret[i] = str.charCodeAt(i); + } + return ret.buffer; + }; + var base64ToArrayBuffer = function(b64) { + return stringToArrayBuffer(atob(b64)); + }; + message = base64ToArrayBuffer(message.data); + } + return message; +} + +function convertMessageToArgsNativeToJs(message) { + var args = []; + if (!message || !message.hasOwnProperty('CDVType')) { + args.push(message); + } else if (message.CDVType == 'MultiPart') { + message.messages.forEach(function(e) { + args.push(massageMessageNativeToJs(e)); + }); + } else { + args.push(massageMessageNativeToJs(message)); + } + return args; +} + +var iOSExec = function() { + + // detect change in bridge, if there is a change, we forward to new bridge + + // if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { + // bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING; + // } + + var successCallback, failCallback, service, action, actionArgs; + var callbackId = null; + if (typeof arguments[0] !== "string") { + // FORMAT ONE + successCallback = arguments[0]; + failCallback = arguments[1]; + service = arguments[2]; + action = arguments[3]; + actionArgs = arguments[4]; + + // Since we need to maintain backwards compatibility, we have to pass + // an invalid callbackId even if no callback was provided since plugins + // will be expecting it. The Cordova.exec() implementation allocates + // an invalid callbackId and passes it even if no callbacks were given. + callbackId = 'INVALID'; + } else { + throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' + + 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'); + } + + // If actionArgs is not provided, default to an empty array + actionArgs = actionArgs || []; + + // Register the callbacks and add the callbackId to the positional + // arguments if given. + if (successCallback || failCallback) { + callbackId = service + cordova.callbackId++; + cordova.callbacks[callbackId] = + {success:successCallback, fail:failCallback}; + } + + actionArgs = massageArgsJsToNative(actionArgs); + + // CB-10133 DataClone DOM Exception 25 guard (fast function remover) + var command = [callbackId, service, action, JSON.parse(JSON.stringify(actionArgs))]; + window.webkit.messageHandlers.cordova.postMessage(command); + +}; + +iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) { + + var success = status === 0 || status === 1; + var args = convertMessageToArgsNativeToJs(message); + setTimeout(function(){ + cordova.callbackFromNative(callbackId, success, status, args, keepCallback); + }, 0); +}; + +// for backwards compatibility +iOSExec.nativeEvalAndFetch = function(func) { + try { + func(); + } catch (e) { + console.log(e); + } +}; + +// Proxy the exec for bridge changes. See CB-10106 + +function cordovaExec() { + var cexec = require('cordova/exec'); + var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function'); + return (cexec_valid && execProxy !== cexec)? cexec : iOSExec; +} + +function execProxy() { + cordovaExec().apply(null, arguments); +} + +execProxy.nativeFetchMessages = function() { + return cordovaExec().nativeFetchMessages.apply(null, arguments); +}; + +execProxy.nativeEvalAndFetch = function() { + return cordovaExec().nativeEvalAndFetch.apply(null, arguments); +}; + +execProxy.nativeCallback = function() { + return cordovaExec().nativeCallback.apply(null, arguments); +}; + +module.exports = execProxy; + +if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { + // unregister the old bridge + cordova.define.remove('cordova/exec'); + // redefine bridge to our new bridge + cordova.define("cordova/exec", function(require, exports, module) { + module.exports = execProxy; + }); +}
\ No newline at end of file |
