cordova allow-navigation、allow-intent 解析

cordova 项目的配置文件 config.xml 里有 allow-navigationallow-intent 两个配置。用途如下:

allow-navigation

控制哪些URL由 WebView 打开(Controls which URLs the WebView itself can be navigated to. Applies to top-level navigations only.)。
如果想让CordovaWebView能打开https://www.google.com,那么需要在config.xml里加上如下配置

1
2
3
...
<allow-navigation href="https://www.google.com" />
...

allow-intent

控制哪些URL让系统去打开(Controls which URLs the app is allowed to ask the system to open.)。
比如 sms:*、tel:*、itms-services:* 等,这些可以让系统去打开。也可以让系统打开 http:*、https:* 的链接,这时系统会调用系统浏览器打开

allow-navigation 优先级比 allow-intent 高,比如 allow-navigation 没配置 https://www.google.com, 而 allow-intent 配置了,那么就会调用系统浏览器打开链接

注意事项

最好不要像这样配置

1
2
3
...
<allow-navigation href="*" />
...

可以先猜想会发生什么?

思考中…
思考中…
思考中…

如果像上面那样配置,那么 sms:*、tel:*、itms-services:* 这些 scheme 的URL都被 WebView 拦截了,而 WebView 默认其实只能处理 http、https、file、data 这些 scheme(可能还有其他的) 的URL,所以上面那些 scheme 无法打开,一般会报下面错误

1
Failed to load webpage with error: unsupported URL

我自己就犯了这个错误,加了 <allow-navigation href="*" /> 配置,然后调用 window.location.href=itms-services://?action=download-manifest&url=https:xxxxxx.plist 去下载应用,结果一直报 加载失败 错误,所以在这里记录下。

源码

Cordova 里处理这个过滤逻辑的源码主要在 CDVIntentAndNavigationFilter 这个类里,核心代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
+ (CDVIntentAndNavigationFilterValue) filterUrl:(NSURL*)url intentsWhitelist:(CDVWhitelist*)intentsWhitelist navigationsWhitelist:(CDVWhitelist*)navigationsWhitelist
{
// a URL can only allow-intent OR allow-navigation, if both are specified,
// only allow-navigation is allowed

BOOL allowNavigationsPass = [navigationsWhitelist URLIsAllowed:url logFailure:NO];
BOOL allowIntentPass = [intentsWhitelist URLIsAllowed:url logFailure:NO];

if (allowNavigationsPass && allowIntentPass) {
return CDVIntentAndNavigationFilterValueNavigationAllowed;
} else if (allowNavigationsPass) {
return CDVIntentAndNavigationFilterValueNavigationAllowed;
} else if (allowIntentPass) {
return CDVIntentAndNavigationFilterValueIntentAllowed;
}

return CDVIntentAndNavigationFilterValueNoneAllowed;
}

+ (BOOL)shouldOverrideLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType filterValue:(CDVIntentAndNavigationFilterValue)filterValue
{
NSString* allowIntents_whitelistRejectionFormatString = @"ERROR External navigation rejected - <allow-intent> not set for url='%@'";
NSString* allowNavigations_whitelistRejectionFormatString = @"ERROR Internal navigation rejected - <allow-navigation> not set for url='%@'";

NSURL* url = [request URL];

switch (filterValue) {
case CDVIntentAndNavigationFilterValueNavigationAllowed:
return YES;
case CDVIntentAndNavigationFilterValueIntentAllowed:
// only allow-intent if it's a UIWebViewNavigationTypeLinkClicked (anchor tag) OR
// it's a UIWebViewNavigationTypeOther, and it's an internal link
if ([[self class] shouldOpenURLRequest:request navigationType:navigationType]){
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}

// consume the request (i.e. no error) if it wasn't handled above
return NO;
case CDVIntentAndNavigationFilterValueNoneAllowed:
// allow-navigation attempt failed for sure
NSLog(@"%@", [NSString stringWithFormat:allowNavigations_whitelistRejectionFormatString, [url absoluteString]]);
// anchor tag link means it was an allow-intent attempt that failed as well
if (UIWebViewNavigationTypeLinkClicked == navigationType) {
NSLog(@"%@", [NSString stringWithFormat:allowIntents_whitelistRejectionFormatString, [url absoluteString]]);
}
return NO;
}
}

结语

由于自己犯下的这个错误导致某版本用户无法正常升级,因此学习下cordova相应源码,在此记录(吃一堑长一智)。也希望有缘人看的这篇文章能规避相应错误。