"
205 | @"
The WebView engine '%@' is unable to load the request: %@
"
206 | @"
Most likely the cause of the error is that the loading of file urls is not supported in iOS %@.
"
207 | @"
",
208 | NSStringFromClass([self class]),
209 | [request.URL description],
210 | [[UIDevice currentDevice] systemVersion]
211 | ];
212 | return [self loadHTMLString:errorHtml baseURL:nil];
213 | }
214 | }
215 |
216 | - (id)loadHTMLString:(NSString*)string baseURL:(NSURL*)baseURL
217 | {
218 | return [(WKWebView*)_engineWebView loadHTMLString:string baseURL:baseURL];
219 | }
220 |
221 | - (NSURL*) URL
222 | {
223 | return [(WKWebView*)_engineWebView URL];
224 | }
225 |
226 | - (BOOL) canLoadRequest:(NSURLRequest*)request
227 | {
228 | // See: https://issues.apache.org/jira/browse/CB-9636
229 | SEL wk_sel = NSSelectorFromString(CDV_WKWEBVIEW_FILE_URL_LOAD_SELECTOR);
230 |
231 | // if it's a file URL, check whether WKWebView has the selector (which is in iOS 9 and up only)
232 | if (request.URL.fileURL) {
233 | return [_engineWebView respondsToSelector:wk_sel];
234 | } else {
235 | return YES;
236 | }
237 | }
238 |
239 | - (void)updateSettings:(NSDictionary*)settings
240 | {
241 | WKWebView* wkWebView = (WKWebView*)_engineWebView;
242 |
243 | wkWebView.configuration.preferences.minimumFontSize = [settings cordovaFloatSettingForKey:@"MinimumFontSize" defaultValue:0.0];
244 |
245 | /*
246 | wkWebView.configuration.preferences.javaScriptEnabled = [settings cordovaBoolSettingForKey:@"JavaScriptEnabled" default:YES];
247 | wkWebView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = [settings cordovaBoolSettingForKey:@"JavaScriptCanOpenWindowsAutomatically" default:NO];
248 | */
249 |
250 | // By default, DisallowOverscroll is false (thus bounce is allowed)
251 | BOOL bounceAllowed = !([settings cordovaBoolSettingForKey:@"DisallowOverscroll" defaultValue:NO]);
252 |
253 | // prevent webView from bouncing
254 | if (!bounceAllowed) {
255 | if ([wkWebView respondsToSelector:@selector(scrollView)]) {
256 | ((UIScrollView*)[wkWebView scrollView]).bounces = NO;
257 | } else {
258 | for (id subview in wkWebView.subviews) {
259 | if ([[subview class] isSubclassOfClass:[UIScrollView class]]) {
260 | ((UIScrollView*)subview).bounces = NO;
261 | }
262 | }
263 | }
264 | }
265 |
266 | NSString* decelerationSetting = [settings cordovaSettingForKey:@"WKWebViewDecelerationSpeed"];
267 | if (!decelerationSetting) {
268 | // Fallback to the UIWebView-named preference
269 | decelerationSetting = [settings cordovaSettingForKey:@"UIWebViewDecelerationSpeed"];
270 | }
271 |
272 | if (![@"fast" isEqualToString:decelerationSetting]) {
273 | [wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal];
274 | } else {
275 | [wkWebView.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast];
276 | }
277 |
278 | wkWebView.allowsBackForwardNavigationGestures = [settings cordovaBoolSettingForKey:@"AllowBackForwardNavigationGestures" defaultValue:NO];
279 | wkWebView.allowsLinkPreview = [settings cordovaBoolSettingForKey:@"Allow3DTouchLinkPreview" defaultValue:YES];
280 | }
281 |
282 | - (void)updateWithInfo:(NSDictionary*)info
283 | {
284 | NSDictionary* scriptMessageHandlers = [info objectForKey:kCDVWebViewEngineScriptMessageHandlers];
285 | NSDictionary* settings = [info objectForKey:kCDVWebViewEngineWebViewPreferences];
286 | id navigationDelegate = [info objectForKey:kCDVWebViewEngineWKNavigationDelegate];
287 | id uiDelegate = [info objectForKey:kCDVWebViewEngineWKUIDelegate];
288 |
289 | WKWebView* wkWebView = (WKWebView*)_engineWebView;
290 |
291 | if (scriptMessageHandlers && [scriptMessageHandlers isKindOfClass:[NSDictionary class]]) {
292 | NSArray* allKeys = [scriptMessageHandlers allKeys];
293 |
294 | for (NSString* key in allKeys) {
295 | id object = [scriptMessageHandlers objectForKey:key];
296 | if ([object conformsToProtocol:@protocol(WKScriptMessageHandler)]) {
297 | [wkWebView.configuration.userContentController addScriptMessageHandler:object name:key];
298 | }
299 | }
300 | }
301 |
302 | if (navigationDelegate && [navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]) {
303 | wkWebView.navigationDelegate = navigationDelegate;
304 | }
305 |
306 | if (uiDelegate && [uiDelegate conformsToProtocol:@protocol(WKUIDelegate)]) {
307 | wkWebView.UIDelegate = uiDelegate;
308 | }
309 |
310 | if (settings && [settings isKindOfClass:[NSDictionary class]]) {
311 | [self updateSettings:settings];
312 | }
313 | }
314 |
315 | // This forwards the methods that are in the header that are not implemented here.
316 | // Both WKWebView and UIWebView implement the below:
317 | // loadHTMLString:baseURL:
318 | // loadRequest:
319 | - (id)forwardingTargetForSelector:(SEL)aSelector
320 | {
321 | return _engineWebView;
322 | }
323 |
324 | - (UIView*)webView
325 | {
326 | return self.engineWebView;
327 | }
328 |
329 | #pragma mark WKScriptMessageHandler implementation
330 |
331 | - (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message
332 | {
333 | if (![message.name isEqualToString:CDV_BRIDGE_NAME]) {
334 | return;
335 | }
336 |
337 | CDVViewController* vc = (CDVViewController*)self.viewController;
338 |
339 | NSArray* jsonEntry = message.body; // NSString:callbackId, NSString:service, NSString:action, NSArray:args
340 | CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
341 | CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);
342 |
343 | if (![vc.commandQueue execute:command]) {
344 | #ifdef DEBUG
345 | NSError* error = nil;
346 | NSString* commandJson = nil;
347 | NSData* jsonData = [NSJSONSerialization dataWithJSONObject:jsonEntry
348 | options:0
349 | error:&error];
350 |
351 | if (error == nil) {
352 | commandJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
353 | }
354 |
355 | static NSUInteger maxLogLength = 1024;
356 | NSString* commandString = ([commandJson length] > maxLogLength) ?
357 | [NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] :
358 | commandJson;
359 |
360 | NSLog(@"FAILED pluginJSON = %@", commandString);
361 | #endif
362 | }
363 | }
364 |
365 | #pragma mark WKNavigationDelegate implementation
366 |
367 | - (void)webView:(WKWebView*)webView didStartProvisionalNavigation:(WKNavigation*)navigation
368 | {
369 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:webView]];
370 | }
371 |
372 | - (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
373 | {
374 | CDVViewController* vc = (CDVViewController*)self.viewController;
375 | [CDVUserAgentUtil releaseLock:vc.userAgentLockToken];
376 |
377 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:webView]];
378 | }
379 |
380 | - (void)webView:(WKWebView*)theWebView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error
381 | {
382 | [self webView:theWebView didFailNavigation:navigation withError:error];
383 | }
384 |
385 | - (void)webView:(WKWebView*)theWebView didFailNavigation:(WKNavigation*)navigation withError:(NSError*)error
386 | {
387 | CDVViewController* vc = (CDVViewController*)self.viewController;
388 | [CDVUserAgentUtil releaseLock:vc.userAgentLockToken];
389 |
390 | NSString* message = [NSString stringWithFormat:@"Failed to load webpage with error: %@", [error localizedDescription]];
391 | NSLog(@"%@", message);
392 |
393 | NSURL* errorUrl = vc.errorURL;
394 | if (errorUrl) {
395 | errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [message stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl];
396 | NSLog(@"%@", [errorUrl absoluteString]);
397 | [theWebView loadRequest:[NSURLRequest requestWithURL:errorUrl]];
398 | }
399 | }
400 |
401 | - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
402 | {
403 | [webView reload];
404 | }
405 |
406 | - (BOOL)defaultResourcePolicyForURL:(NSURL*)url
407 | {
408 | // all file:// urls are allowed
409 | if ([url isFileURL]) {
410 | return YES;
411 | }
412 |
413 | return NO;
414 | }
415 |
416 | - (void) webView: (WKWebView *) webView decidePolicyForNavigationAction: (WKNavigationAction*) navigationAction decisionHandler: (void (^)(WKNavigationActionPolicy)) decisionHandler
417 | {
418 | NSURL* url = [navigationAction.request URL];
419 | CDVViewController* vc = (CDVViewController*)self.viewController;
420 |
421 | /*
422 | * Give plugins the chance to handle the url
423 | */
424 | BOOL anyPluginsResponded = NO;
425 | BOOL shouldAllowRequest = NO;
426 |
427 | for (NSString* pluginName in vc.pluginObjects) {
428 | CDVPlugin* plugin = [vc.pluginObjects objectForKey:pluginName];
429 | SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:");
430 | if ([plugin respondsToSelector:selector]) {
431 | anyPluginsResponded = YES;
432 | // https://issues.apache.org/jira/browse/CB-12497
433 | int navType = (int)navigationAction.navigationType;
434 | if (WKNavigationTypeOther == navigationAction.navigationType) {
435 | navType = (int)UIWebViewNavigationTypeOther;
436 | }
437 | shouldAllowRequest = (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, navigationAction.request, navType));
438 | if (!shouldAllowRequest) {
439 | break;
440 | }
441 | }
442 | }
443 |
444 | if (anyPluginsResponded) {
445 | return decisionHandler(shouldAllowRequest);
446 | }
447 |
448 | /*
449 | * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
450 | */
451 | BOOL shouldAllowNavigation = [self defaultResourcePolicyForURL:url];
452 | if (shouldAllowNavigation) {
453 | return decisionHandler(YES);
454 | } else {
455 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
456 | }
457 |
458 | return decisionHandler(NO);
459 | }
460 |
461 | #pragma mark - Plugin interface
462 |
463 | - (void)allowsBackForwardNavigationGestures:(CDVInvokedUrlCommand*)command;
464 | {
465 | id value = [command argumentAtIndex:0];
466 | if (!([value isKindOfClass:[NSNumber class]])) {
467 | value = [NSNumber numberWithBool:NO];
468 | }
469 |
470 | WKWebView* wkWebView = (WKWebView*)_engineWebView;
471 | wkWebView.allowsBackForwardNavigationGestures = [value boolValue];
472 | }
473 |
474 | @end
475 |
476 | #pragma mark - CDVWKWeakScriptMessageHandler
477 |
478 | @implementation CDVWKWeakScriptMessageHandler
479 |
480 | - (instancetype)initWithScriptMessageHandler:(id