@exodus/react-native-webview 11.26.1-exodus.21 → 11.26.1-exodus.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1062,9 +1062,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
|
1062
1062
|
for (String requestedResource : request.getResources()) {
|
|
1063
1063
|
String androidPermission = null;
|
|
1064
1064
|
|
|
1065
|
-
if (
|
|
1066
|
-
|
|
1067
|
-
|
|
1065
|
+
if (cameraPermissionOriginWhitelist.contains(request.getOrigin().toString())) {
|
|
1066
|
+
if (requestedResource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
|
|
1067
|
+
androidPermission = Manifest.permission.CAMERA;
|
|
1068
|
+
} else if (requestedResource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) {
|
|
1069
|
+
androidPermission = Manifest.permission.RECORD_AUDIO;
|
|
1070
|
+
} else {
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1068
1073
|
} else {
|
|
1069
1074
|
continue;
|
|
1070
1075
|
}
|
|
@@ -1107,6 +1112,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|
|
1107
1112
|
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
|
1108
1113
|
if (permissions[i].equals(Manifest.permission.CAMERA)) {
|
|
1109
1114
|
grantedPermissions.add(PermissionRequest.RESOURCE_VIDEO_CAPTURE);
|
|
1115
|
+
} else if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) {
|
|
1116
|
+
grantedPermissions.add(PermissionRequest.RESOURCE_AUDIO_CAPTURE);
|
|
1110
1117
|
}
|
|
1111
1118
|
}
|
|
1112
1119
|
}
|
package/lib/WebViewShared.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { OnShouldStartLoadWithRequest, ShouldStartLoadRequestEvent, WebViewError, WebViewErrorEvent, WebViewMessageEvent, WebViewMessage, WebViewNavigationEvent, WebViewOpenWindowEvent, WebViewProgressEvent, WebViewNativeEvent } from './WebViewTypes';
|
|
3
3
|
declare const defaultOriginWhitelist: readonly ["https://*"];
|
|
4
|
-
declare const defaultDeeplinkWhitelist: readonly ["https
|
|
4
|
+
declare const defaultDeeplinkWhitelist: readonly ["https:"];
|
|
5
5
|
declare const createOnShouldStartLoadWithRequest: (loadRequest: (shouldStart: boolean, url: string, lockIdentifier: number) => void, originWhitelist: readonly string[], deepLinkWhitelist: readonly string[], onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest | undefined) => ({ nativeEvent }: ShouldStartLoadRequestEvent) => void;
|
|
6
6
|
declare const defaultRenderLoading: () => JSX.Element;
|
|
7
7
|
declare const defaultRenderError: (errorDomain: string | undefined, errorCode: number, errorDesc: string) => JSX.Element;
|
package/lib/WebViewShared.js
CHANGED
|
@@ -3,8 +3,8 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import { Linking, View, ActivityIndicator, Text, Platform } from 'react-native';
|
|
4
4
|
import styles from './WebView.styles';
|
|
5
5
|
const defaultOriginWhitelist = ['https://*'];
|
|
6
|
-
const defaultDeeplinkWhitelist = ['https
|
|
7
|
-
const defaultDeeplinkBlocklist = [
|
|
6
|
+
const defaultDeeplinkWhitelist = ['https:'];
|
|
7
|
+
const defaultDeeplinkBlocklist = [`http:`, `file:`, `javascript:`];
|
|
8
8
|
const extractOrigin = (url) => {
|
|
9
9
|
const result = /^[A-Za-z][A-Za-z0-9+\-.]+:(\/\/)?[^/]*/.exec(url);
|
|
10
10
|
return result === null ? '' : result[0];
|
|
@@ -13,20 +13,36 @@ const stringWhitelistToRegex = (originWhitelist) => new RegExp(`^${escapeStringR
|
|
|
13
13
|
const matchWithRegexList = (compiledRegexList, value) => {
|
|
14
14
|
return compiledRegexList.some(x => x.test(value));
|
|
15
15
|
};
|
|
16
|
-
const
|
|
16
|
+
const matchWithStringList = (prefixes, value) => {
|
|
17
17
|
if (typeof value !== 'string')
|
|
18
18
|
throw new Error(`value was not a string`);
|
|
19
|
-
return
|
|
19
|
+
return Array.prototype.includes.call(prefixes, value);
|
|
20
20
|
};
|
|
21
21
|
const _passesWhitelist = (compiledWhitelist, url) => {
|
|
22
22
|
const origin = extractOrigin(url);
|
|
23
23
|
if (!origin)
|
|
24
24
|
return false;
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
try {
|
|
26
|
+
const decodedUrl = new URL(url);
|
|
27
|
+
if (origin !== decodedUrl.origin)
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
27
33
|
return matchWithRegexList(compiledWhitelist, origin);
|
|
28
34
|
};
|
|
29
35
|
const compileWhitelist = (originWhitelist) => ['about:blank', ...(originWhitelist || [])].map(stringWhitelistToRegex);
|
|
36
|
+
const urlToProtocolScheme = (url) => {
|
|
37
|
+
try {
|
|
38
|
+
return new URL(url).protocol;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Protocol schemes must start with a letter and cannot start with digits, underscores etc.
|
|
42
|
+
// e.g 0invalid, _invalid, +invalid, -invalid, .invalid will all become null
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
30
46
|
const createOnShouldStartLoadWithRequest = (loadRequest, originWhitelist, deepLinkWhitelist, onShouldStartLoadWithRequest) => {
|
|
31
47
|
const compiledWhiteList = compileWhitelist(originWhitelist);
|
|
32
48
|
return ({ nativeEvent }) => {
|
|
@@ -34,24 +50,31 @@ const createOnShouldStartLoadWithRequest = (loadRequest, originWhitelist, deepLi
|
|
|
34
50
|
const { url, lockIdentifier, isTopFrame } = nativeEvent;
|
|
35
51
|
/** Check if the url passes the origin whitelist */
|
|
36
52
|
if (!_passesWhitelist(compiledWhiteList, url)) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
/** Check if the
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
const protocol = urlToProtocolScheme(url);
|
|
54
|
+
/* Check that the protocol was properly parsed */
|
|
55
|
+
if (protocol !== null) {
|
|
56
|
+
/** Check if the protocol passes the hardcoded deeplink blocklist */
|
|
57
|
+
const foundMatchInBlocklist = matchWithStringList(defaultDeeplinkBlocklist, protocol);
|
|
58
|
+
if (!foundMatchInBlocklist) {
|
|
59
|
+
/** Check if the protocol passes the dynamic deeplink allow list */
|
|
60
|
+
const foundMatchInAllowlist = matchWithStringList(deepLinkWhitelist, protocol);
|
|
61
|
+
if (foundMatchInAllowlist) {
|
|
62
|
+
Linking.canOpenURL(url).then((supported) => {
|
|
63
|
+
if (supported && isTopFrame) {
|
|
64
|
+
return Linking.openURL(url);
|
|
65
|
+
}
|
|
66
|
+
console.warn(`Can't open url: ${url}`);
|
|
67
|
+
return undefined;
|
|
68
|
+
}).catch(e => {
|
|
69
|
+
console.warn('Error opening URL: ', e);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.warn(`Failed to pass whitelist for deep link url: ${url}`);
|
|
74
|
+
}
|
|
52
75
|
}
|
|
53
76
|
else {
|
|
54
|
-
console.warn(`Failed to pass default block list
|
|
77
|
+
console.warn(`Failed to pass default block list for deep link url: ${url}`);
|
|
55
78
|
}
|
|
56
79
|
}
|
|
57
80
|
shouldStart = false;
|
package/lib/WebViewTypes.d.ts
CHANGED
|
@@ -714,9 +714,8 @@ export interface WebViewSharedProps extends ViewProps {
|
|
|
714
714
|
*/
|
|
715
715
|
readonly originWhitelist?: string[];
|
|
716
716
|
/**
|
|
717
|
-
* List of
|
|
718
|
-
*
|
|
719
|
-
* The default behavior is to only allow "https://".
|
|
717
|
+
* List of protocol schemes to allow being deep linked to. This requires
|
|
718
|
+
* an exact match. The default behavior is to only allow "https:".
|
|
720
719
|
*/
|
|
721
720
|
readonly deeplinkWhitelist?: string[];
|
|
722
721
|
/**
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"Thibault Malbranche <malbranche.thibault@gmail.com>"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "11.26.1-exodus.
|
|
12
|
+
"version": "11.26.1-exodus.24",
|
|
13
13
|
"homepage": "https://github.com/ExodusMovement/react-native-webview#readme",
|
|
14
14
|
"scripts": {
|
|
15
15
|
"android": "react-native run-android",
|