@exodus/react-native-webview 11.26.1-exodus.1 → 11.26.1-exodus.11
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.
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java +28 -268
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java +1 -45
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java +25 -0
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewUtils.java +51 -0
- package/apple/RNCWebView.h +0 -8
- package/apple/RNCWebView.m +17 -123
- package/apple/RNCWebViewManager.m +0 -10
- package/index.js +5 -2
- package/lib/WebView.android.js +83 -73
- package/lib/WebView.ios.js +64 -113
- package/lib/WebView.js +2 -2
- package/lib/WebView.styles.js +7 -7
- package/lib/WebViewNativeComponent.android.js +1 -1
- package/lib/WebViewNativeComponent.ios.js +1 -1
- package/lib/WebViewShared.d.ts +8 -12
- package/lib/WebViewShared.js +118 -88
- package/lib/WebViewTypes.d.ts +22 -157
- package/lib/WebViewTypes.js +4 -31
- package/package.json +1 -1
- package/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.kt +0 -15
- package/android/src/main/java/com/reactnativecommunity/webview/events/TopHttpErrorEvent.kt +0 -25
- package/android/src/main/java/com/reactnativecommunity/webview/events/TopRenderProcessGoneEvent.kt +0 -26
package/lib/WebView.ios.js
CHANGED
|
@@ -1,65 +1,17 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
-
function step(op) {
|
|
15
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (_) try {
|
|
17
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
38
|
-
var t = {};
|
|
39
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
40
|
-
t[p] = s[p];
|
|
41
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
42
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
43
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
44
|
-
t[p[i]] = s[p[i]];
|
|
45
|
-
}
|
|
46
|
-
return t;
|
|
47
|
-
};
|
|
48
1
|
import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
|
|
49
|
-
import {
|
|
2
|
+
import { Text, View, NativeModules, Platform, } from 'react-native';
|
|
50
3
|
import invariant from 'invariant';
|
|
51
4
|
// @ts-expect-error react-native doesn't have this type
|
|
52
5
|
import codegenNativeCommandsUntyped from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
53
6
|
import RNCWebView from "./WebViewNativeComponent.ios";
|
|
54
|
-
import { defaultOriginWhitelist, defaultRenderError, defaultRenderLoading, useWebWiewLogic, } from './WebViewShared';
|
|
7
|
+
import { defaultOriginWhitelist, defaultRenderError, defaultRenderLoading, useWebWiewLogic, versionPasses, } from './WebViewShared';
|
|
55
8
|
import styles from './WebView.styles';
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
supportedCommands: ['goBack', 'goForward', 'reload', 'stopLoading', /* 'injectJavaScript', */ 'requestFocus', 'postMessage', 'loadUrl']
|
|
9
|
+
const codegenNativeCommands = codegenNativeCommandsUntyped;
|
|
10
|
+
const Commands = codegenNativeCommands({
|
|
11
|
+
supportedCommands: ['goBack', 'goForward', 'reload', 'stopLoading', /* 'injectJavaScript', */ 'requestFocus', 'postMessage', 'loadUrl'],
|
|
59
12
|
});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
var newDecelerationRate = decelerationRate;
|
|
13
|
+
const processDecelerationRate = (decelerationRate) => {
|
|
14
|
+
let newDecelerationRate = decelerationRate;
|
|
63
15
|
if (newDecelerationRate === 'normal') {
|
|
64
16
|
newDecelerationRate = 0.998;
|
|
65
17
|
}
|
|
@@ -68,71 +20,72 @@ var processDecelerationRate = function (decelerationRate) {
|
|
|
68
20
|
}
|
|
69
21
|
return newDecelerationRate;
|
|
70
22
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
23
|
+
const RNCWebViewManager = NativeModules.RNCWebViewManager;
|
|
24
|
+
const useWarnIfChanges = (value, name) => {
|
|
25
|
+
const ref = useRef(value);
|
|
74
26
|
if (ref.current !== value) {
|
|
75
|
-
console.warn(
|
|
27
|
+
console.warn(`Changes to property ${name} do nothing after the initial render.`);
|
|
76
28
|
ref.current = value;
|
|
77
29
|
}
|
|
78
30
|
};
|
|
79
31
|
/**
|
|
80
32
|
* Harcoded defaults for security.
|
|
81
33
|
*/
|
|
82
|
-
|
|
83
|
-
var allowUniversalAccessFromFileURLs = false;
|
|
84
|
-
var injectedJavaScriptForMainFrameOnly = true;
|
|
85
|
-
var injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true;
|
|
86
|
-
var mediaPlaybackRequiresUserAction = true;
|
|
34
|
+
const mediaPlaybackRequiresUserAction = true;
|
|
87
35
|
// iOS only configs
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
var onShouldStartLoadWithRequestCallback = useCallback(function (shouldStart, _url, lockIdentifier) {
|
|
99
|
-
if (lockIdentifier === void 0) { lockIdentifier = 0; }
|
|
100
|
-
var viewManager = RNCWebViewManager;
|
|
36
|
+
const allowsInlineMediaPlayback = true;
|
|
37
|
+
const useSharedProcessPool = false;
|
|
38
|
+
const sharedCookiesEnabled = false;
|
|
39
|
+
const enableApplePay = false;
|
|
40
|
+
const dataDetectorTypes = 'none';
|
|
41
|
+
const hardMinimumIOSVersion = '12.5.6 <13, 13.6.1 <14, 14.8.1 <15, 15.7.1';
|
|
42
|
+
const WebViewComponent = forwardRef(({ javaScriptEnabled = true, cacheEnabled = true, originWhitelist = defaultOriginWhitelist, textInteractionEnabled = true, injectedJavaScript, injectedJavaScriptBeforeContentLoaded, startInLoadingState, onLoadStart, onError, onLoad, onLoadEnd, onMessage: onMessageProp, renderLoading, renderError, style, containerStyle, source, incognito, validateMeta, validateData, decelerationRate: decelerationRateProp, onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, minimumIOSVersion, unsupportedVersionComponent: UnsupportedVersionComponent, ...otherProps }, ref) => {
|
|
43
|
+
const webViewRef = useRef(null);
|
|
44
|
+
const onShouldStartLoadWithRequestCallback = useCallback((shouldStart, _url, lockIdentifier = 0) => {
|
|
45
|
+
const viewManager = RNCWebViewManager;
|
|
101
46
|
viewManager.startLoadWithResult(!!shouldStart, lockIdentifier);
|
|
102
47
|
}, []);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
goBack: function () { return Commands.goBack(webViewRef.current); },
|
|
121
|
-
reload: function () {
|
|
48
|
+
const { onLoadingStart, onShouldStartLoadWithRequest, onMessage, viewState, setViewState, lastErrorEvent, onLoadingError, onLoadingFinish, onLoadingProgress } = useWebWiewLogic({
|
|
49
|
+
onLoad,
|
|
50
|
+
onError,
|
|
51
|
+
onLoadEnd,
|
|
52
|
+
onLoadStart,
|
|
53
|
+
onMessageProp,
|
|
54
|
+
startInLoadingState,
|
|
55
|
+
originWhitelist,
|
|
56
|
+
onShouldStartLoadWithRequestProp,
|
|
57
|
+
onShouldStartLoadWithRequestCallback,
|
|
58
|
+
validateMeta,
|
|
59
|
+
validateData,
|
|
60
|
+
});
|
|
61
|
+
useImperativeHandle(ref, () => ({
|
|
62
|
+
goForward: () => Commands.goForward(webViewRef.current),
|
|
63
|
+
goBack: () => Commands.goBack(webViewRef.current),
|
|
64
|
+
reload: () => {
|
|
122
65
|
setViewState('LOADING');
|
|
123
66
|
Commands.reload(webViewRef.current);
|
|
124
67
|
},
|
|
125
|
-
stopLoading:
|
|
126
|
-
postMessage:
|
|
68
|
+
stopLoading: () => Commands.stopLoading(webViewRef.current),
|
|
69
|
+
postMessage: (data) => Commands.postMessage(webViewRef.current, data),
|
|
127
70
|
// injectJavaScript: (data: string) => Commands.injectJavaScript(webViewRef.current, data),
|
|
128
|
-
requestFocus:
|
|
129
|
-
})
|
|
71
|
+
requestFocus: () => Commands.requestFocus(webViewRef.current),
|
|
72
|
+
}), [setViewState, webViewRef]);
|
|
130
73
|
useWarnIfChanges(allowsInlineMediaPlayback, 'allowsInlineMediaPlayback');
|
|
131
|
-
useWarnIfChanges(allowsAirPlayForMediaPlayback, 'allowsAirPlayForMediaPlayback');
|
|
132
74
|
useWarnIfChanges(incognito, 'incognito');
|
|
133
75
|
useWarnIfChanges(mediaPlaybackRequiresUserAction, 'mediaPlaybackRequiresUserAction');
|
|
134
76
|
useWarnIfChanges(dataDetectorTypes, 'dataDetectorTypes');
|
|
135
|
-
|
|
77
|
+
const version = String(Platform.Version);
|
|
78
|
+
if (!(versionPasses(version, minimumIOSVersion) && versionPasses(version, hardMinimumIOSVersion))) {
|
|
79
|
+
if (UnsupportedVersionComponent) {
|
|
80
|
+
return <UnsupportedVersionComponent />;
|
|
81
|
+
}
|
|
82
|
+
return (<View style={{ alignSelf: 'flex-start' }}>
|
|
83
|
+
<Text style={{ color: 'red' }}>
|
|
84
|
+
iOS version is outdated and insecure. Update it to continue.
|
|
85
|
+
</Text>
|
|
86
|
+
</View>);
|
|
87
|
+
}
|
|
88
|
+
let otherView = null;
|
|
136
89
|
if (viewState === 'LOADING') {
|
|
137
90
|
otherView = (renderLoading || defaultRenderLoading)();
|
|
138
91
|
}
|
|
@@ -141,23 +94,21 @@ var WebViewComponent = forwardRef(function (_a, ref) {
|
|
|
141
94
|
otherView = (renderError || defaultRenderError)(lastErrorEvent.domain, lastErrorEvent.code, lastErrorEvent.description);
|
|
142
95
|
}
|
|
143
96
|
else if (viewState !== 'IDLE') {
|
|
144
|
-
console.error(
|
|
97
|
+
console.error(`RNCWebView invalid state encountered: ${viewState}`);
|
|
145
98
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
99
|
+
const webViewStyles = [styles.container, styles.webView, style];
|
|
100
|
+
const webViewContainerStyle = [styles.container, containerStyle];
|
|
101
|
+
const decelerationRate = processDecelerationRate(decelerationRateProp);
|
|
102
|
+
const NativeWebView = RNCWebView;
|
|
103
|
+
const webView = (<NativeWebView key="webViewKey" {...otherProps} enableApplePay={enableApplePay} javaScriptEnabled={javaScriptEnabled} cacheEnabled={cacheEnabled} dataDetectorTypes={dataDetectorTypes} useSharedProcessPool={useSharedProcessPool} textInteractionEnabled={textInteractionEnabled} decelerationRate={decelerationRate} messagingEnabled={typeof onMessageProp === 'function'} onLoadingError={onLoadingError} onLoadingFinish={onLoadingFinish} onLoadingProgress={onLoadingProgress} onLoadingStart={onLoadingStart} onMessage={onMessage} onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} injectedJavaScript={injectedJavaScript} injectedJavaScriptBeforeContentLoaded={injectedJavaScriptBeforeContentLoaded} allowsInlineMediaPlayback={allowsInlineMediaPlayback} incognito={incognito} mediaPlaybackRequiresUserAction={mediaPlaybackRequiresUserAction} ref={webViewRef} sharedCookiesEnabled={sharedCookiesEnabled}
|
|
151
104
|
// TODO: find a better way to type this.
|
|
152
|
-
source={
|
|
105
|
+
source={source} style={webViewStyles}/>);
|
|
153
106
|
return (<View style={webViewContainerStyle}>
|
|
154
107
|
{webView}
|
|
155
108
|
{otherView}
|
|
156
109
|
</View>);
|
|
157
110
|
});
|
|
158
111
|
// no native implementation for iOS, depends only on permissions
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}); }); };
|
|
162
|
-
var WebView = Object.assign(WebViewComponent, { isFileUploadSupported: isFileUploadSupported });
|
|
112
|
+
const isFileUploadSupported = async () => true;
|
|
113
|
+
const WebView = Object.assign(WebViewComponent, { isFileUploadSupported });
|
|
163
114
|
export default WebView;
|
package/lib/WebView.js
CHANGED
|
@@ -2,10 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import { Text, View } from 'react-native';
|
|
3
3
|
// This "dummy" WebView is to render something for unsupported platforms,
|
|
4
4
|
// like for example Expo SDK "web" platform.
|
|
5
|
-
|
|
5
|
+
const WebView = () => (<View style={{ alignSelf: 'flex-start' }}>
|
|
6
6
|
<Text style={{ color: 'red' }}>
|
|
7
7
|
React Native WebView does not support this platform.
|
|
8
8
|
</Text>
|
|
9
|
-
</View>);
|
|
9
|
+
</View>);
|
|
10
10
|
export { WebView };
|
|
11
11
|
export default WebView;
|
package/lib/WebView.styles.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
|
-
|
|
2
|
+
const styles = StyleSheet.create({
|
|
3
3
|
container: {
|
|
4
4
|
flex: 1,
|
|
5
|
-
overflow: 'hidden'
|
|
5
|
+
overflow: 'hidden',
|
|
6
6
|
},
|
|
7
7
|
loadingOrErrorView: {
|
|
8
8
|
position: 'absolute',
|
|
@@ -14,20 +14,20 @@ var styles = StyleSheet.create({
|
|
|
14
14
|
backgroundColor: 'white'
|
|
15
15
|
},
|
|
16
16
|
loadingProgressBar: {
|
|
17
|
-
height: 20
|
|
17
|
+
height: 20,
|
|
18
18
|
},
|
|
19
19
|
errorText: {
|
|
20
20
|
fontSize: 14,
|
|
21
21
|
textAlign: 'center',
|
|
22
|
-
marginBottom: 2
|
|
22
|
+
marginBottom: 2,
|
|
23
23
|
},
|
|
24
24
|
errorTextTitle: {
|
|
25
25
|
fontSize: 15,
|
|
26
26
|
fontWeight: '500',
|
|
27
|
-
marginBottom: 10
|
|
27
|
+
marginBottom: 10,
|
|
28
28
|
},
|
|
29
29
|
webView: {
|
|
30
|
-
backgroundColor: '#ffffff'
|
|
31
|
-
}
|
|
30
|
+
backgroundColor: '#ffffff',
|
|
31
|
+
},
|
|
32
32
|
});
|
|
33
33
|
export default styles;
|
package/lib/WebViewShared.d.ts
CHANGED
|
@@ -1,37 +1,33 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { OnShouldStartLoadWithRequest, ShouldStartLoadRequestEvent, WebViewError, WebViewErrorEvent,
|
|
3
|
-
declare const defaultOriginWhitelist: readonly ["
|
|
2
|
+
import { OnShouldStartLoadWithRequest, ShouldStartLoadRequestEvent, WebViewError, WebViewErrorEvent, WebViewMessageEvent, WebViewMessage, WebViewNavigationEvent, WebViewProgressEvent, WebViewNativeEvent } from './WebViewTypes';
|
|
3
|
+
declare const defaultOriginWhitelist: readonly ["https://*"];
|
|
4
4
|
declare const createOnShouldStartLoadWithRequest: (loadRequest: (shouldStart: boolean, url: string, lockIdentifier: number) => void, originWhitelist: readonly string[], onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest | undefined) => ({ nativeEvent }: ShouldStartLoadRequestEvent) => void;
|
|
5
5
|
declare const defaultRenderLoading: () => JSX.Element;
|
|
6
6
|
declare const defaultRenderError: (errorDomain: string | undefined, errorCode: number, errorDesc: string) => JSX.Element;
|
|
7
7
|
export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, defaultRenderLoading, defaultRenderError, };
|
|
8
|
-
export declare const useWebWiewLogic: ({ startInLoadingState,
|
|
8
|
+
export declare const useWebWiewLogic: ({ startInLoadingState, onLoadStart, onLoad, onLoadEnd, onError, onMessageProp, originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, validateMeta, validateData, }: {
|
|
9
9
|
startInLoadingState?: boolean | undefined;
|
|
10
|
-
onNavigationStateChange?: ((event: WebViewNavigation) => void) | undefined;
|
|
11
10
|
onLoadStart?: ((event: WebViewNavigationEvent) => void) | undefined;
|
|
12
11
|
onLoad?: ((event: WebViewNavigationEvent) => void) | undefined;
|
|
13
|
-
onLoadProgress?: ((event: WebViewProgressEvent) => void) | undefined;
|
|
14
12
|
onLoadEnd?: ((event: WebViewNavigationEvent | WebViewErrorEvent) => void) | undefined;
|
|
15
13
|
onError?: ((event: WebViewErrorEvent) => void) | undefined;
|
|
16
|
-
|
|
17
|
-
onMessageProp?: ((event: WebViewMessageEvent) => void) | undefined;
|
|
18
|
-
onRenderProcessGoneProp?: ((event: WebViewRenderProcessGoneEvent) => void) | undefined;
|
|
19
|
-
onContentProcessDidTerminateProp?: ((event: WebViewTerminatedEvent) => void) | undefined;
|
|
14
|
+
onMessageProp?: ((event: WebViewMessage) => void) | undefined;
|
|
20
15
|
originWhitelist: readonly string[];
|
|
21
16
|
onShouldStartLoadWithRequestProp?: OnShouldStartLoadWithRequest | undefined;
|
|
22
17
|
onShouldStartLoadWithRequestCallback: (shouldStart: boolean, url: string, lockIdentifier?: number | undefined) => void;
|
|
18
|
+
validateMeta: (event: WebViewNativeEvent) => WebViewNativeEvent;
|
|
19
|
+
validateData: (data: object) => object;
|
|
23
20
|
}) => {
|
|
24
21
|
onShouldStartLoadWithRequest: ({ nativeEvent }: ShouldStartLoadRequestEvent) => void;
|
|
25
22
|
onLoadingStart: (event: WebViewNavigationEvent) => void;
|
|
26
23
|
onLoadingProgress: (event: WebViewProgressEvent) => void;
|
|
27
24
|
onLoadingError: (event: WebViewErrorEvent) => void;
|
|
28
25
|
onLoadingFinish: (event: WebViewNavigationEvent) => void;
|
|
29
|
-
onHttpError: (event: WebViewHttpErrorEvent) => void;
|
|
30
|
-
onRenderProcessGone: (event: WebViewRenderProcessGoneEvent) => void;
|
|
31
|
-
onContentProcessDidTerminate: (event: WebViewTerminatedEvent) => void;
|
|
32
26
|
onMessage: (event: WebViewMessageEvent) => void;
|
|
27
|
+
passesWhitelist: (url: string) => boolean | null;
|
|
33
28
|
viewState: "IDLE" | "LOADING" | "ERROR";
|
|
34
29
|
setViewState: React.Dispatch<React.SetStateAction<"IDLE" | "LOADING" | "ERROR">>;
|
|
35
30
|
lastErrorEvent: WebViewError | null;
|
|
36
31
|
};
|
|
32
|
+
export declare const versionPasses: (version: string | undefined, minimum: string | undefined) => boolean;
|
|
37
33
|
//# sourceMappingURL=WebViewShared.d.ts.map
|
package/lib/WebViewShared.js
CHANGED
|
@@ -1,44 +1,34 @@
|
|
|
1
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
-
if (ar || !(i in from)) {
|
|
4
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
-
ar[i] = from[i];
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
9
|
-
};
|
|
10
1
|
import escapeStringRegexp from 'escape-string-regexp';
|
|
11
2
|
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
12
3
|
import { Linking, View, ActivityIndicator, Text, Platform } from 'react-native';
|
|
13
4
|
import styles from './WebView.styles';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
const defaultOriginWhitelist = ['https://*'];
|
|
6
|
+
const extractOrigin = (url) => {
|
|
7
|
+
const result = /^[A-Za-z][A-Za-z0-9+\-.]+:(\/\/)?[^/]*/.exec(url);
|
|
17
8
|
return result === null ? '' : result[0];
|
|
18
9
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
const originWhitelistToRegex = (originWhitelist) => `^${escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*')}$`;
|
|
11
|
+
const _passesWhitelist = (compiledWhitelist, url) => {
|
|
12
|
+
const origin = extractOrigin(url);
|
|
13
|
+
if (!origin)
|
|
14
|
+
return false;
|
|
15
|
+
if (origin !== new URL(url).origin)
|
|
16
|
+
return null;
|
|
17
|
+
return compiledWhitelist.some(x => new RegExp(x).test(origin));
|
|
25
18
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (!passesWhitelist(compileWhitelist(originWhitelist), url)) {
|
|
35
|
-
Linking.canOpenURL(url).then(function (supported) {
|
|
36
|
-
if (supported) {
|
|
19
|
+
const compileWhitelist = (originWhitelist) => ['about:blank', ...(originWhitelist || [])].map(originWhitelistToRegex);
|
|
20
|
+
const createOnShouldStartLoadWithRequest = (loadRequest, originWhitelist, onShouldStartLoadWithRequest) => {
|
|
21
|
+
return ({ nativeEvent }) => {
|
|
22
|
+
let shouldStart = true;
|
|
23
|
+
const { url, lockIdentifier, isTopFrame } = nativeEvent;
|
|
24
|
+
if (!_passesWhitelist(compileWhitelist(originWhitelist), url)) {
|
|
25
|
+
Linking.canOpenURL(url).then((supported) => {
|
|
26
|
+
if (supported && isTopFrame && /^https:\/\//.test(url)) {
|
|
37
27
|
return Linking.openURL(url);
|
|
38
28
|
}
|
|
39
|
-
console.warn(
|
|
29
|
+
console.warn(`Can't open url: ${url}`);
|
|
40
30
|
return undefined;
|
|
41
|
-
})
|
|
31
|
+
}).catch(e => {
|
|
42
32
|
console.warn('Error opening URL: ', e);
|
|
43
33
|
});
|
|
44
34
|
shouldStart = false;
|
|
@@ -49,32 +39,41 @@ var createOnShouldStartLoadWithRequest = function (loadRequest, originWhitelist,
|
|
|
49
39
|
loadRequest(shouldStart, url, lockIdentifier);
|
|
50
40
|
};
|
|
51
41
|
};
|
|
52
|
-
|
|
42
|
+
const defaultRenderLoading = () => (<View style={styles.loadingOrErrorView}>
|
|
53
43
|
<ActivityIndicator />
|
|
54
|
-
</View>);
|
|
55
|
-
|
|
44
|
+
</View>);
|
|
45
|
+
const defaultRenderError = (errorDomain, errorCode, errorDesc) => (<View style={styles.loadingOrErrorView}>
|
|
56
46
|
<Text style={styles.errorTextTitle}>Error loading page</Text>
|
|
57
|
-
<Text style={styles.errorText}>{
|
|
58
|
-
<Text style={styles.errorText}>{
|
|
59
|
-
<Text style={styles.errorText}>{
|
|
60
|
-
</View>);
|
|
47
|
+
<Text style={styles.errorText}>{`Domain: ${errorDomain}`}</Text>
|
|
48
|
+
<Text style={styles.errorText}>{`Error Code: ${errorCode}`}</Text>
|
|
49
|
+
<Text style={styles.errorText}>{`Description: ${errorDesc}`}</Text>
|
|
50
|
+
</View>);
|
|
61
51
|
export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, defaultRenderLoading, defaultRenderError, };
|
|
62
|
-
export
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
export const useWebWiewLogic = ({ startInLoadingState, onLoadStart, onLoad, onLoadEnd, onError, onMessageProp, originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, validateMeta, validateData, }) => {
|
|
53
|
+
const [viewState, setViewState] = useState(startInLoadingState ? "LOADING" : "IDLE");
|
|
54
|
+
const [lastErrorEvent, setLastErrorEvent] = useState(null);
|
|
55
|
+
const startUrl = useRef(null);
|
|
56
|
+
const passesWhitelist = (url) => {
|
|
57
|
+
if (!url || typeof url !== 'string')
|
|
58
|
+
return false;
|
|
59
|
+
return _passesWhitelist(compileWhitelist(originWhitelist), url);
|
|
60
|
+
};
|
|
61
|
+
const passesWhitelistUse = useCallback(passesWhitelist, [originWhitelist]);
|
|
62
|
+
const extractMeta = (nativeEvent) => ({
|
|
63
|
+
url: String(nativeEvent.url),
|
|
64
|
+
loading: Boolean(nativeEvent.loading),
|
|
65
|
+
title: String(nativeEvent.title),
|
|
66
|
+
canGoBack: Boolean(nativeEvent.canGoBack),
|
|
67
|
+
canGoForward: Boolean(nativeEvent.canGoForward),
|
|
68
|
+
lockIdentifier: Number(nativeEvent.lockIdentifier),
|
|
69
|
+
});
|
|
70
|
+
const onLoadingStart = useCallback((event) => {
|
|
71
71
|
// Needed for android
|
|
72
72
|
startUrl.current = event.nativeEvent.url;
|
|
73
73
|
// !Needed for android
|
|
74
74
|
onLoadStart === null || onLoadStart === void 0 ? void 0 : onLoadStart(event);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
var onLoadingError = useCallback(function (event) {
|
|
75
|
+
}, [onLoadStart]);
|
|
76
|
+
const onLoadingError = useCallback((event) => {
|
|
78
77
|
event.persist();
|
|
79
78
|
if (onError) {
|
|
80
79
|
onError(event);
|
|
@@ -90,55 +89,86 @@ export var useWebWiewLogic = function (_a) {
|
|
|
90
89
|
setViewState('ERROR');
|
|
91
90
|
setLastErrorEvent(event.nativeEvent);
|
|
92
91
|
}, [onError, onLoadEnd]);
|
|
93
|
-
|
|
94
|
-
onHttpErrorProp === null || onHttpErrorProp === void 0 ? void 0 : onHttpErrorProp(event);
|
|
95
|
-
}, [onHttpErrorProp]);
|
|
96
|
-
// Android Only
|
|
97
|
-
var onRenderProcessGone = useCallback(function (event) {
|
|
98
|
-
onRenderProcessGoneProp === null || onRenderProcessGoneProp === void 0 ? void 0 : onRenderProcessGoneProp(event);
|
|
99
|
-
}, [onRenderProcessGoneProp]);
|
|
100
|
-
// !Android Only
|
|
101
|
-
// iOS Only
|
|
102
|
-
var onContentProcessDidTerminate = useCallback(function (event) {
|
|
103
|
-
onContentProcessDidTerminateProp === null || onContentProcessDidTerminateProp === void 0 ? void 0 : onContentProcessDidTerminateProp(event);
|
|
104
|
-
}, [onContentProcessDidTerminateProp]);
|
|
105
|
-
// !iOS Only
|
|
106
|
-
var onLoadingFinish = useCallback(function (event) {
|
|
92
|
+
const onLoadingFinish = useCallback((event) => {
|
|
107
93
|
onLoad === null || onLoad === void 0 ? void 0 : onLoad(event);
|
|
108
94
|
onLoadEnd === null || onLoadEnd === void 0 ? void 0 : onLoadEnd(event);
|
|
109
|
-
|
|
95
|
+
const { nativeEvent: { url } } = event;
|
|
96
|
+
if (!passesWhitelistUse(url))
|
|
97
|
+
return;
|
|
110
98
|
// on Android, only if url === startUrl
|
|
111
99
|
if (Platform.OS !== "android" || url === startUrl.current) {
|
|
112
100
|
setViewState('IDLE');
|
|
113
101
|
}
|
|
114
102
|
// !on Android, only if url === startUrl
|
|
115
|
-
updateNavigationState(event);
|
|
116
|
-
}, [onLoad, onLoadEnd,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
103
|
+
// REMOVED: updateNavigationState(event);
|
|
104
|
+
}, [onLoad, onLoadEnd, passesWhitelistUse]);
|
|
105
|
+
const onMessage = useCallback((event) => {
|
|
106
|
+
const { nativeEvent } = event;
|
|
107
|
+
if (!passesWhitelistUse(nativeEvent.url))
|
|
108
|
+
return;
|
|
109
|
+
// TODO: can/should we perform any other validation?
|
|
110
|
+
const data = JSON.stringify(validateData(JSON.parse(nativeEvent.data)));
|
|
111
|
+
const meta = validateMeta(extractMeta(nativeEvent));
|
|
112
|
+
onMessageProp === null || onMessageProp === void 0 ? void 0 : onMessageProp({ ...meta, data });
|
|
113
|
+
}, [onMessageProp, passesWhitelistUse, validateData, validateMeta]);
|
|
114
|
+
const onLoadingProgress = useCallback((event) => {
|
|
115
|
+
const { nativeEvent: { progress } } = event;
|
|
116
|
+
if (!passesWhitelistUse(event.nativeEvent.url))
|
|
117
|
+
return;
|
|
122
118
|
// patch for Android only
|
|
123
119
|
if (Platform.OS === "android" && progress === 1) {
|
|
124
|
-
setViewState(
|
|
120
|
+
setViewState(prevViewState => prevViewState === 'LOADING' ? 'IDLE' : prevViewState);
|
|
125
121
|
}
|
|
126
122
|
// !patch for Android only
|
|
127
|
-
|
|
128
|
-
}, [
|
|
129
|
-
|
|
123
|
+
// REMOVED: onLoadProgress?.(event);
|
|
124
|
+
}, [passesWhitelistUse]);
|
|
125
|
+
const onShouldStartLoadWithRequest = useMemo(() => createOnShouldStartLoadWithRequest(onShouldStartLoadWithRequestCallback, originWhitelist, onShouldStartLoadWithRequestProp), [originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback]);
|
|
130
126
|
return {
|
|
131
|
-
onShouldStartLoadWithRequest
|
|
132
|
-
onLoadingStart
|
|
133
|
-
onLoadingProgress
|
|
134
|
-
onLoadingError
|
|
135
|
-
onLoadingFinish
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
setViewState: setViewState,
|
|
142
|
-
lastErrorEvent: lastErrorEvent
|
|
127
|
+
onShouldStartLoadWithRequest,
|
|
128
|
+
onLoadingStart,
|
|
129
|
+
onLoadingProgress,
|
|
130
|
+
onLoadingError,
|
|
131
|
+
onLoadingFinish,
|
|
132
|
+
onMessage,
|
|
133
|
+
passesWhitelist,
|
|
134
|
+
viewState,
|
|
135
|
+
setViewState,
|
|
136
|
+
lastErrorEvent,
|
|
143
137
|
};
|
|
144
138
|
};
|
|
139
|
+
export const versionPasses = (version, minimum) => {
|
|
140
|
+
if (!version || !minimum)
|
|
141
|
+
return false;
|
|
142
|
+
if (typeof version !== 'string' || typeof minimum !== 'string')
|
|
143
|
+
return false;
|
|
144
|
+
if (minimum.includes(', ')) {
|
|
145
|
+
// We have a set of possible versions
|
|
146
|
+
const variants = minimum.split(', ');
|
|
147
|
+
// Every entry but the last one should be with an upper bound
|
|
148
|
+
if (!variants.slice(0, -1).every(x => x.includes(' <')))
|
|
149
|
+
return false;
|
|
150
|
+
return variants.some(x => versionPasses(version, x)); // Any match passes
|
|
151
|
+
}
|
|
152
|
+
if (minimum.includes(' <')) {
|
|
153
|
+
const [min, max, ...rest] = minimum.split(' <');
|
|
154
|
+
if (rest.length > 0)
|
|
155
|
+
return false;
|
|
156
|
+
// Last check is required for correctness/formatting validation
|
|
157
|
+
return versionPasses(version, min) && !versionPasses(version, max) && versionPasses(max, version);
|
|
158
|
+
}
|
|
159
|
+
const versionRegex = /^[0-9]+(\.[0-9]+)*$/;
|
|
160
|
+
if (!versionRegex.test(version) || !versionRegex.test(minimum))
|
|
161
|
+
return false;
|
|
162
|
+
const versionParts = version.split('.').map(Number);
|
|
163
|
+
const minimumParts = minimum.split('.').map(Number);
|
|
164
|
+
const len = Math.max(versionParts.length, minimumParts.length);
|
|
165
|
+
for (let i = 0; i < len; i += 1) {
|
|
166
|
+
const ver = versionParts[i] || 0;
|
|
167
|
+
const min = minimumParts[i] || 0;
|
|
168
|
+
if (ver > min)
|
|
169
|
+
return true;
|
|
170
|
+
if (ver < min)
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return true; // equals
|
|
174
|
+
};
|