@exodus/react-native-webview 11.26.1-exodus.3 → 11.26.1-exodus.30

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.
@@ -1,10 +1,10 @@
1
1
  import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
2
- import { View, NativeModules, } from 'react-native';
2
+ import { Text, View, NativeModules, Platform, } from 'react-native';
3
3
  import invariant from 'invariant';
4
4
  // @ts-expect-error react-native doesn't have this type
5
5
  import codegenNativeCommandsUntyped from 'react-native/Libraries/Utilities/codegenNativeCommands';
6
6
  import RNCWebView from "./WebViewNativeComponent.ios";
7
- import { defaultOriginWhitelist, defaultRenderError, defaultRenderLoading, useWebWiewLogic, } from './WebViewShared';
7
+ import { defaultOriginWhitelist, defaultDeeplinkWhitelist, defaultRenderError, defaultRenderLoading, useWebWiewLogic, versionPasses, } from './WebViewShared';
8
8
  import styles from './WebView.styles';
9
9
  const codegenNativeCommands = codegenNativeCommandsUntyped;
10
10
  const Commands = codegenNativeCommands({
@@ -31,57 +31,63 @@ const useWarnIfChanges = (value, name) => {
31
31
  /**
32
32
  * Harcoded defaults for security.
33
33
  */
34
- const allowFileAccessFromFileURLs = false;
35
- const allowUniversalAccessFromFileURLs = false;
36
- const injectedJavaScriptForMainFrameOnly = true;
37
- const injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true;
38
34
  const mediaPlaybackRequiresUserAction = true;
39
35
  // iOS only configs
40
36
  const allowsInlineMediaPlayback = true;
41
- const allowsAirPlayForMediaPlayback = false;
42
37
  const useSharedProcessPool = false;
43
38
  const sharedCookiesEnabled = false;
44
39
  const enableApplePay = false;
45
- const onFileDownload = () => console.error('tried to download file');
46
40
  const dataDetectorTypes = 'none';
47
- const WebViewComponent = forwardRef(({ javaScriptEnabled = true, cacheEnabled = true, originWhitelist = defaultOriginWhitelist, textInteractionEnabled = true, injectedJavaScript, injectedJavaScriptBeforeContentLoaded, startInLoadingState, onNavigationStateChange, onLoadStart, onError, onLoad, onLoadEnd, onLoadProgress, onContentProcessDidTerminate: onContentProcessDidTerminateProp, onHttpError: onHttpErrorProp, onMessage: onMessageProp, renderLoading, renderError, style, containerStyle, source, incognito, decelerationRate: decelerationRateProp, onShouldStartLoadWithRequest: onShouldStartLoadWithRequestProp, ...otherProps }, ref) => {
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, deeplinkWhitelist = defaultDeeplinkWhitelist, 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) => {
48
43
  const webViewRef = useRef(null);
49
44
  const onShouldStartLoadWithRequestCallback = useCallback((shouldStart, _url, lockIdentifier = 0) => {
50
45
  const viewManager = RNCWebViewManager;
51
46
  viewManager.startLoadWithResult(!!shouldStart, lockIdentifier);
52
47
  }, []);
53
- const { onLoadingStart, onShouldStartLoadWithRequest, onMessage, viewState, setViewState, lastErrorEvent, onHttpError, onLoadingError, onLoadingFinish, onLoadingProgress, onContentProcessDidTerminate } = useWebWiewLogic({
54
- onNavigationStateChange,
48
+ const { onLoadingStart, onShouldStartLoadWithRequest, onMessage, viewState, setViewState, lastErrorEvent, onLoadingError, onLoadingFinish, onLoadingProgress } = useWebWiewLogic({
55
49
  onLoad,
56
50
  onError,
57
- onHttpErrorProp,
58
51
  onLoadEnd,
59
- onLoadProgress,
60
52
  onLoadStart,
61
53
  onMessageProp,
62
54
  startInLoadingState,
63
55
  originWhitelist,
56
+ deeplinkWhitelist,
64
57
  onShouldStartLoadWithRequestProp,
65
58
  onShouldStartLoadWithRequestCallback,
66
- onContentProcessDidTerminateProp,
59
+ validateMeta,
60
+ validateData,
67
61
  });
68
62
  useImperativeHandle(ref, () => ({
69
- goForward: () => Commands.goForward(webViewRef.current),
70
- goBack: () => Commands.goBack(webViewRef.current),
63
+ goForward: () => webViewRef.current && Commands.goForward(webViewRef.current),
64
+ goBack: () => webViewRef.current && Commands.goBack(webViewRef.current),
71
65
  reload: () => {
72
66
  setViewState('LOADING');
73
- Commands.reload(webViewRef.current);
67
+ if (webViewRef.current) {
68
+ Commands.reload(webViewRef.current);
69
+ }
74
70
  },
75
- stopLoading: () => Commands.stopLoading(webViewRef.current),
76
- postMessage: (data) => Commands.postMessage(webViewRef.current, data),
71
+ stopLoading: () => webViewRef.current && Commands.stopLoading(webViewRef.current),
72
+ postMessage: (data) => webViewRef.current && Commands.postMessage(webViewRef.current, data),
77
73
  // injectJavaScript: (data: string) => Commands.injectJavaScript(webViewRef.current, data),
78
- requestFocus: () => Commands.requestFocus(webViewRef.current),
74
+ requestFocus: () => webViewRef.current && Commands.requestFocus(webViewRef.current),
79
75
  }), [setViewState, webViewRef]);
80
76
  useWarnIfChanges(allowsInlineMediaPlayback, 'allowsInlineMediaPlayback');
81
- useWarnIfChanges(allowsAirPlayForMediaPlayback, 'allowsAirPlayForMediaPlayback');
82
77
  useWarnIfChanges(incognito, 'incognito');
83
78
  useWarnIfChanges(mediaPlaybackRequiresUserAction, 'mediaPlaybackRequiresUserAction');
84
79
  useWarnIfChanges(dataDetectorTypes, 'dataDetectorTypes');
80
+ const version = String(Platform.Version);
81
+ if (!(versionPasses(version, minimumIOSVersion) && versionPasses(version, hardMinimumIOSVersion))) {
82
+ if (UnsupportedVersionComponent) {
83
+ return <UnsupportedVersionComponent />;
84
+ }
85
+ return (<View style={{ alignSelf: 'flex-start' }}>
86
+ <Text style={{ color: 'red' }}>
87
+ iOS version is outdated and insecure. Update it to continue.
88
+ </Text>
89
+ </View>);
90
+ }
85
91
  let otherView = null;
86
92
  if (viewState === 'LOADING') {
87
93
  otherView = (renderLoading || defaultRenderLoading)();
@@ -97,7 +103,7 @@ const WebViewComponent = forwardRef(({ javaScriptEnabled = true, cacheEnabled =
97
103
  const webViewContainerStyle = [styles.container, containerStyle];
98
104
  const decelerationRate = processDecelerationRate(decelerationRateProp);
99
105
  const NativeWebView = RNCWebView;
100
- const webView = (<NativeWebView key="webViewKey" {...otherProps} allowFileAccessFromFileURLs={allowFileAccessFromFileURLs} allowUniversalAccessFromFileURLs={allowUniversalAccessFromFileURLs} enableApplePay={enableApplePay} javaScriptEnabled={javaScriptEnabled} cacheEnabled={cacheEnabled} dataDetectorTypes={dataDetectorTypes} useSharedProcessPool={useSharedProcessPool} textInteractionEnabled={textInteractionEnabled} decelerationRate={decelerationRate} messagingEnabled={typeof onMessageProp === 'function'} onLoadingError={onLoadingError} onLoadingFinish={onLoadingFinish} onLoadingProgress={onLoadingProgress} onFileDownload={onFileDownload} onLoadingStart={onLoadingStart} onHttpError={onHttpError} onMessage={onMessage} onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} onContentProcessDidTerminate={onContentProcessDidTerminate} injectedJavaScript={injectedJavaScript} injectedJavaScriptBeforeContentLoaded={injectedJavaScriptBeforeContentLoaded} injectedJavaScriptForMainFrameOnly={injectedJavaScriptForMainFrameOnly} injectedJavaScriptBeforeContentLoadedForMainFrameOnly={injectedJavaScriptBeforeContentLoadedForMainFrameOnly} allowsAirPlayForMediaPlayback={allowsAirPlayForMediaPlayback} allowsInlineMediaPlayback={allowsInlineMediaPlayback} incognito={incognito} mediaPlaybackRequiresUserAction={mediaPlaybackRequiresUserAction} ref={webViewRef} sharedCookiesEnabled={sharedCookiesEnabled}
106
+ 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}
101
107
  // TODO: find a better way to type this.
102
108
  source={source} style={webViewStyles}/>);
103
109
  return (<View style={webViewContainerStyle}>
@@ -1,37 +1,37 @@
1
1
  import React from 'react';
2
- import { OnShouldStartLoadWithRequest, ShouldStartLoadRequestEvent, WebViewError, WebViewErrorEvent, WebViewHttpErrorEvent, WebViewMessageEvent, WebViewNavigation, WebViewNavigationEvent, WebViewProgressEvent, WebViewRenderProcessGoneEvent, WebViewTerminatedEvent } from './WebViewTypes';
3
- declare const defaultOriginWhitelist: readonly ["http://*", "https://*"];
4
- declare const createOnShouldStartLoadWithRequest: (loadRequest: (shouldStart: boolean, url: string, lockIdentifier: number) => void, originWhitelist: readonly string[], onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest | undefined) => ({ nativeEvent }: ShouldStartLoadRequestEvent) => void;
2
+ import { OnShouldStartLoadWithRequest, ShouldStartLoadRequestEvent, WebViewError, WebViewErrorEvent, WebViewMessageEvent, WebViewMessage, WebViewNavigationEvent, WebViewOpenWindowEvent, WebViewProgressEvent, WebViewNativeEvent } from './WebViewTypes';
3
+ declare const defaultOriginWhitelist: readonly ["https://*"];
4
+ declare const defaultDeeplinkWhitelist: readonly ["https:"];
5
+ declare const createOnShouldStartLoadWithRequest: (loadRequest: (shouldStart: boolean, url: string, lockIdentifier: number) => void, originWhitelist: readonly string[], deepLinkWhitelist: readonly string[], onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest | undefined) => ({ nativeEvent }: ShouldStartLoadRequestEvent) => void;
5
6
  declare const defaultRenderLoading: () => JSX.Element;
6
7
  declare const defaultRenderError: (errorDomain: string | undefined, errorCode: number, errorDesc: string) => JSX.Element;
7
- export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, defaultRenderLoading, defaultRenderError, };
8
- export declare const useWebWiewLogic: ({ startInLoadingState, onNavigationStateChange, onLoadStart, onLoad, onLoadProgress, onLoadEnd, onError, onHttpErrorProp, onMessageProp, onRenderProcessGoneProp, onContentProcessDidTerminateProp, originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, }: {
8
+ export { defaultOriginWhitelist, defaultDeeplinkWhitelist, createOnShouldStartLoadWithRequest, defaultRenderLoading, defaultRenderError, };
9
+ export declare const useWebWiewLogic: ({ startInLoadingState, onLoadStart, onLoad, onLoadEnd, onError, onMessageProp, onOpenWindowProp, originWhitelist, deeplinkWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, validateMeta, validateData, }: {
9
10
  startInLoadingState?: boolean | undefined;
10
- onNavigationStateChange?: ((event: WebViewNavigation) => void) | undefined;
11
11
  onLoadStart?: ((event: WebViewNavigationEvent) => void) | undefined;
12
12
  onLoad?: ((event: WebViewNavigationEvent) => void) | undefined;
13
- onLoadProgress?: ((event: WebViewProgressEvent) => void) | undefined;
14
13
  onLoadEnd?: ((event: WebViewNavigationEvent | WebViewErrorEvent) => void) | undefined;
15
14
  onError?: ((event: WebViewErrorEvent) => void) | undefined;
16
- onHttpErrorProp?: ((event: WebViewHttpErrorEvent) => void) | undefined;
17
- onMessageProp?: ((event: WebViewMessageEvent) => void) | undefined;
18
- onRenderProcessGoneProp?: ((event: WebViewRenderProcessGoneEvent) => void) | undefined;
19
- onContentProcessDidTerminateProp?: ((event: WebViewTerminatedEvent) => void) | undefined;
15
+ onMessageProp?: ((event: WebViewMessage) => void) | undefined;
16
+ onOpenWindowProp?: ((event: WebViewOpenWindowEvent) => void) | undefined;
20
17
  originWhitelist: readonly string[];
18
+ deeplinkWhitelist: readonly string[];
21
19
  onShouldStartLoadWithRequestProp?: OnShouldStartLoadWithRequest | undefined;
22
20
  onShouldStartLoadWithRequestCallback: (shouldStart: boolean, url: string, lockIdentifier?: number | undefined) => void;
21
+ validateMeta: (event: WebViewNativeEvent) => WebViewNativeEvent;
22
+ validateData: (data: object) => object;
23
23
  }) => {
24
24
  onShouldStartLoadWithRequest: ({ nativeEvent }: ShouldStartLoadRequestEvent) => void;
25
25
  onLoadingStart: (event: WebViewNavigationEvent) => void;
26
26
  onLoadingProgress: (event: WebViewProgressEvent) => void;
27
27
  onLoadingError: (event: WebViewErrorEvent) => void;
28
28
  onLoadingFinish: (event: WebViewNavigationEvent) => void;
29
- onHttpError: (event: WebViewHttpErrorEvent) => void;
30
- onRenderProcessGone: (event: WebViewRenderProcessGoneEvent) => void;
31
- onContentProcessDidTerminate: (event: WebViewTerminatedEvent) => void;
32
29
  onMessage: (event: WebViewMessageEvent) => void;
30
+ onOpenWindow: (event: WebViewOpenWindowEvent) => void;
31
+ passesWhitelist: (url: string) => boolean | "";
33
32
  viewState: "IDLE" | "LOADING" | "ERROR";
34
33
  setViewState: React.Dispatch<React.SetStateAction<"IDLE" | "LOADING" | "ERROR">>;
35
34
  lastErrorEvent: WebViewError | null;
36
35
  };
36
+ export declare const versionPasses: (version: string | undefined, minimum: string | undefined) => boolean;
37
37
  //# sourceMappingURL=WebViewShared.d.ts.map
@@ -1,23 +1,73 @@
1
1
  import escapeStringRegexp from 'escape-string-regexp';
2
2
  import React, { useCallback, useMemo, useRef, useState } from 'react';
3
- import { View, ActivityIndicator, Text, Platform } from 'react-native';
3
+ import { Linking, View, ActivityIndicator, Text, Platform } from 'react-native';
4
4
  import styles from './WebView.styles';
5
- const defaultOriginWhitelist = ['http://*', 'https://*'];
6
- const extractOrigin = (url) => {
7
- const result = /^[A-Za-z][A-Za-z0-9+\-.]+:(\/\/)?[^/]*/.exec(url);
8
- return result === null ? '' : result[0];
5
+ const defaultOriginWhitelist = ['https://*'];
6
+ const defaultDeeplinkWhitelist = ['https:'];
7
+ const defaultDeeplinkBlocklist = [`http:`, `file:`, `javascript:`];
8
+ const stringWhitelistToRegex = (originWhitelist) => new RegExp(`^${escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*')}$`);
9
+ const matchWithRegexList = (compiledRegexList, value) => {
10
+ return compiledRegexList.some(x => x.test(value));
9
11
  };
10
- const originWhitelistToRegex = (originWhitelist) => `^${escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*')}`;
11
- const passesWhitelist = (compiledWhitelist, url) => {
12
- const origin = extractOrigin(url);
13
- return compiledWhitelist.some(x => new RegExp(x).test(origin));
12
+ const matchWithStringList = (prefixes, value) => {
13
+ if (typeof value !== 'string')
14
+ throw new Error(`value was not a string`);
15
+ return Array.prototype.includes.call(prefixes, value);
14
16
  };
15
- const compileWhitelist = (originWhitelist) => ['about:blank', ...(originWhitelist || [])].map(originWhitelistToRegex);
16
- const createOnShouldStartLoadWithRequest = (loadRequest, originWhitelist, onShouldStartLoadWithRequest) => {
17
+ const _passesWhitelist = (compiledWhitelist, url) => {
18
+ try {
19
+ const { origin } = new URL(url);
20
+ return origin && matchWithRegexList(compiledWhitelist, origin);
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ };
26
+ const compileWhitelist = (originWhitelist) => ['about:blank', ...(originWhitelist || [])].map(stringWhitelistToRegex);
27
+ const urlToProtocolScheme = (url) => {
28
+ try {
29
+ return new URL(url).protocol;
30
+ }
31
+ catch {
32
+ // Protocol schemes must start with a letter and cannot start with digits, underscores etc.
33
+ // e.g 0invalid, _invalid, +invalid, -invalid, .invalid will all become null
34
+ return null;
35
+ }
36
+ };
37
+ const createOnShouldStartLoadWithRequest = (loadRequest, originWhitelist, deepLinkWhitelist, onShouldStartLoadWithRequest) => {
38
+ const compiledWhiteList = compileWhitelist(originWhitelist);
17
39
  return ({ nativeEvent }) => {
18
40
  let shouldStart = true;
19
- const { url, lockIdentifier } = nativeEvent;
20
- if (!passesWhitelist(compileWhitelist(originWhitelist), url)) {
41
+ const { url, lockIdentifier, isTopFrame } = nativeEvent;
42
+ /** Check if the url passes the origin whitelist */
43
+ if (!_passesWhitelist(compiledWhiteList, url)) {
44
+ const protocol = urlToProtocolScheme(url);
45
+ /* Check that the protocol was properly parsed */
46
+ if (protocol !== null) {
47
+ /** Check if the protocol passes the hardcoded deeplink blocklist */
48
+ const foundMatchInBlocklist = matchWithStringList(defaultDeeplinkBlocklist, protocol);
49
+ if (!foundMatchInBlocklist) {
50
+ /** Check if the protocol passes the dynamic deeplink allow list */
51
+ const foundMatchInAllowlist = matchWithStringList(deepLinkWhitelist, protocol);
52
+ if (foundMatchInAllowlist) {
53
+ Linking.canOpenURL(url).then((supported) => {
54
+ if (supported && isTopFrame) {
55
+ return Linking.openURL(url);
56
+ }
57
+ console.warn(`Can't open url: ${url}`);
58
+ return undefined;
59
+ }).catch(e => {
60
+ console.warn('Error opening URL: ', e);
61
+ });
62
+ }
63
+ else {
64
+ console.warn(`Failed to pass whitelist for deep link url: ${url}`);
65
+ }
66
+ }
67
+ else {
68
+ console.warn(`Failed to pass default block list for deep link url: ${url}`);
69
+ }
70
+ }
21
71
  shouldStart = false;
22
72
  }
23
73
  else if (onShouldStartLoadWithRequest) {
@@ -35,21 +85,31 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => (<View style={
35
85
  <Text style={styles.errorText}>{`Error Code: ${errorCode}`}</Text>
36
86
  <Text style={styles.errorText}>{`Description: ${errorDesc}`}</Text>
37
87
  </View>);
38
- export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, defaultRenderLoading, defaultRenderError, };
39
- export const useWebWiewLogic = ({ startInLoadingState, onNavigationStateChange, onLoadStart, onLoad, onLoadProgress, onLoadEnd, onError, onHttpErrorProp, onMessageProp, onRenderProcessGoneProp, onContentProcessDidTerminateProp, originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, }) => {
88
+ export { defaultOriginWhitelist, defaultDeeplinkWhitelist, createOnShouldStartLoadWithRequest, defaultRenderLoading, defaultRenderError, };
89
+ export const useWebWiewLogic = ({ startInLoadingState, onLoadStart, onLoad, onLoadEnd, onError, onMessageProp, onOpenWindowProp, originWhitelist, deeplinkWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback, validateMeta, validateData, }) => {
40
90
  const [viewState, setViewState] = useState(startInLoadingState ? "LOADING" : "IDLE");
41
91
  const [lastErrorEvent, setLastErrorEvent] = useState(null);
42
92
  const startUrl = useRef(null);
43
- const updateNavigationState = useCallback((event) => {
44
- onNavigationStateChange === null || onNavigationStateChange === void 0 ? void 0 : onNavigationStateChange(event.nativeEvent);
45
- }, [onNavigationStateChange]);
93
+ const passesWhitelist = (url) => {
94
+ if (!url || typeof url !== 'string')
95
+ return false;
96
+ return _passesWhitelist(compileWhitelist(originWhitelist), url);
97
+ };
98
+ const passesWhitelistUse = useCallback(passesWhitelist, [originWhitelist]);
99
+ const extractMeta = (nativeEvent) => ({
100
+ url: String(nativeEvent.url),
101
+ loading: Boolean(nativeEvent.loading),
102
+ title: String(nativeEvent.title),
103
+ canGoBack: Boolean(nativeEvent.canGoBack),
104
+ canGoForward: Boolean(nativeEvent.canGoForward),
105
+ lockIdentifier: Number(nativeEvent.lockIdentifier),
106
+ });
46
107
  const onLoadingStart = useCallback((event) => {
47
108
  // Needed for android
48
109
  startUrl.current = event.nativeEvent.url;
49
110
  // !Needed for android
50
111
  onLoadStart === null || onLoadStart === void 0 ? void 0 : onLoadStart(event);
51
- updateNavigationState(event);
52
- }, [onLoadStart, updateNavigationState]);
112
+ }, [onLoadStart]);
53
113
  const onLoadingError = useCallback((event) => {
54
114
  event.persist();
55
115
  if (onError) {
@@ -66,55 +126,92 @@ export const useWebWiewLogic = ({ startInLoadingState, onNavigationStateChange,
66
126
  setViewState('ERROR');
67
127
  setLastErrorEvent(event.nativeEvent);
68
128
  }, [onError, onLoadEnd]);
69
- const onHttpError = useCallback((event) => {
70
- onHttpErrorProp === null || onHttpErrorProp === void 0 ? void 0 : onHttpErrorProp(event);
71
- }, [onHttpErrorProp]);
72
- // Android Only
73
- const onRenderProcessGone = useCallback((event) => {
74
- onRenderProcessGoneProp === null || onRenderProcessGoneProp === void 0 ? void 0 : onRenderProcessGoneProp(event);
75
- }, [onRenderProcessGoneProp]);
76
- // !Android Only
77
- // iOS Only
78
- const onContentProcessDidTerminate = useCallback((event) => {
79
- onContentProcessDidTerminateProp === null || onContentProcessDidTerminateProp === void 0 ? void 0 : onContentProcessDidTerminateProp(event);
80
- }, [onContentProcessDidTerminateProp]);
81
- // !iOS Only
82
129
  const onLoadingFinish = useCallback((event) => {
83
130
  onLoad === null || onLoad === void 0 ? void 0 : onLoad(event);
84
131
  onLoadEnd === null || onLoadEnd === void 0 ? void 0 : onLoadEnd(event);
85
132
  const { nativeEvent: { url } } = event;
133
+ if (!passesWhitelistUse(url))
134
+ return;
86
135
  // on Android, only if url === startUrl
87
136
  if (Platform.OS !== "android" || url === startUrl.current) {
88
137
  setViewState('IDLE');
89
138
  }
90
139
  // !on Android, only if url === startUrl
91
- updateNavigationState(event);
92
- }, [onLoad, onLoadEnd, updateNavigationState]);
140
+ // REMOVED: updateNavigationState(event);
141
+ }, [onLoad, onLoadEnd, passesWhitelistUse]);
93
142
  const onMessage = useCallback((event) => {
94
- onMessageProp === null || onMessageProp === void 0 ? void 0 : onMessageProp(event);
95
- }, [onMessageProp]);
143
+ const { nativeEvent } = event;
144
+ if (!passesWhitelistUse(nativeEvent.url))
145
+ return;
146
+ // TODO: can/should we perform any other validation?
147
+ const data = JSON.stringify(validateData(JSON.parse(nativeEvent.data)));
148
+ const meta = validateMeta(extractMeta(nativeEvent));
149
+ onMessageProp === null || onMessageProp === void 0 ? void 0 : onMessageProp({ ...meta, data });
150
+ }, [onMessageProp, passesWhitelistUse, validateData, validateMeta]);
96
151
  const onLoadingProgress = useCallback((event) => {
97
152
  const { nativeEvent: { progress } } = event;
153
+ if (!passesWhitelistUse(event.nativeEvent.url))
154
+ return;
98
155
  // patch for Android only
99
156
  if (Platform.OS === "android" && progress === 1) {
100
157
  setViewState(prevViewState => prevViewState === 'LOADING' ? 'IDLE' : prevViewState);
101
158
  }
102
159
  // !patch for Android only
103
- onLoadProgress === null || onLoadProgress === void 0 ? void 0 : onLoadProgress(event);
104
- }, [onLoadProgress]);
105
- const onShouldStartLoadWithRequest = useMemo(() => createOnShouldStartLoadWithRequest(onShouldStartLoadWithRequestCallback, originWhitelist, onShouldStartLoadWithRequestProp), [originWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback]);
160
+ // REMOVED: onLoadProgress?.(event);
161
+ }, [passesWhitelistUse]);
162
+ const onShouldStartLoadWithRequest = useMemo(() => createOnShouldStartLoadWithRequest(onShouldStartLoadWithRequestCallback, originWhitelist, deeplinkWhitelist, onShouldStartLoadWithRequestProp), [originWhitelist, deeplinkWhitelist, onShouldStartLoadWithRequestProp, onShouldStartLoadWithRequestCallback]);
163
+ // Android Only
164
+ const onOpenWindow = useCallback((event) => {
165
+ onOpenWindowProp === null || onOpenWindowProp === void 0 ? void 0 : onOpenWindowProp(event);
166
+ }, [onOpenWindowProp]);
167
+ // !Android Only
106
168
  return {
107
169
  onShouldStartLoadWithRequest,
108
170
  onLoadingStart,
109
171
  onLoadingProgress,
110
172
  onLoadingError,
111
173
  onLoadingFinish,
112
- onHttpError,
113
- onRenderProcessGone,
114
- onContentProcessDidTerminate,
115
174
  onMessage,
175
+ onOpenWindow,
176
+ passesWhitelist,
116
177
  viewState,
117
178
  setViewState,
118
179
  lastErrorEvent,
119
180
  };
120
181
  };
182
+ export const versionPasses = (version, minimum) => {
183
+ if (!version || !minimum)
184
+ return false;
185
+ if (typeof version !== 'string' || typeof minimum !== 'string')
186
+ return false;
187
+ if (minimum.includes(', ')) {
188
+ // We have a set of possible versions
189
+ const variants = minimum.split(', ');
190
+ // Every entry but the last one should be with an upper bound
191
+ if (!variants.slice(0, -1).every(x => x.includes(' <')))
192
+ return false;
193
+ return variants.some(x => versionPasses(version, x)); // Any match passes
194
+ }
195
+ if (minimum.includes(' <')) {
196
+ const [min, max, ...rest] = minimum.split(' <');
197
+ if (rest.length > 0)
198
+ return false;
199
+ // Last check is required for correctness/formatting validation
200
+ return versionPasses(version, min) && !versionPasses(version, max) && versionPasses(max, version);
201
+ }
202
+ const versionRegex = /^[0-9]+(\.[0-9]+)*$/;
203
+ if (!versionRegex.test(version) || !versionRegex.test(minimum))
204
+ return false;
205
+ const versionParts = version.split('.').map(Number);
206
+ const minimumParts = minimum.split('.').map(Number);
207
+ const len = Math.max(versionParts.length, minimumParts.length);
208
+ for (let i = 0; i < len; i += 1) {
209
+ const ver = versionParts[i] || 0;
210
+ const min = minimumParts[i] || 0;
211
+ if (ver > min)
212
+ return true;
213
+ if (ver < min)
214
+ return false;
215
+ }
216
+ return true; // equals
217
+ };