mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
Move WebView JS files to FB internal
Summary: This moves all the JS files and updates the BUCK files. The next step is to move the Android and iOS native files. Reviewed By: TheSavior Differential Revision: D14618890 fbshipit-source-id: 42984acdf5920e712f272d5742c778943be37ac5
This commit is contained in:
committed by
Facebook Github Bot
parent
a47746b851
commit
5fa8258138
@@ -1,510 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ActivityIndicator = require('ActivityIndicator');
|
||||
const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes');
|
||||
const DeprecatedEdgeInsetsPropType = require('DeprecatedEdgeInsetsPropType');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('React');
|
||||
const ReactNative = require('ReactNative');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const UIManager = require('UIManager');
|
||||
const View = require('View');
|
||||
const WebViewShared = require('WebViewShared');
|
||||
|
||||
const deprecatedPropType = require('deprecatedPropType');
|
||||
const keyMirror = require('fbjs/lib/keyMirror');
|
||||
const requireNativeComponent = require('requireNativeComponent');
|
||||
const resolveAssetSource = require('resolveAssetSource');
|
||||
|
||||
const RCT_WEBVIEW_REF = 'webview';
|
||||
|
||||
const WebViewState = keyMirror({
|
||||
IDLE: null,
|
||||
LOADING: null,
|
||||
ERROR: null,
|
||||
});
|
||||
|
||||
const defaultRenderLoading = () => (
|
||||
<View style={styles.loadingView}>
|
||||
<ActivityIndicator style={styles.loadingProgressBar} />
|
||||
</View>
|
||||
);
|
||||
|
||||
type Props = any;
|
||||
type State = any;
|
||||
|
||||
/**
|
||||
* Renders a native WebView.
|
||||
*/
|
||||
class WebView extends React.Component<Props, State> {
|
||||
static propTypes = {
|
||||
...DeprecatedViewPropTypes,
|
||||
renderError: PropTypes.func,
|
||||
renderLoading: PropTypes.func,
|
||||
onLoad: PropTypes.func,
|
||||
onLoadEnd: PropTypes.func,
|
||||
onLoadStart: PropTypes.func,
|
||||
onError: PropTypes.func,
|
||||
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||
contentInset: DeprecatedEdgeInsetsPropType,
|
||||
onNavigationStateChange: PropTypes.func,
|
||||
onMessage: PropTypes.func,
|
||||
onContentSizeChange: PropTypes.func,
|
||||
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
|
||||
style: DeprecatedViewPropTypes.style,
|
||||
|
||||
html: deprecatedPropType(
|
||||
PropTypes.string,
|
||||
'Use the `source` prop instead.',
|
||||
),
|
||||
|
||||
url: deprecatedPropType(PropTypes.string, 'Use the `source` prop instead.'),
|
||||
|
||||
/**
|
||||
* Loads static html or a uri (with optional headers) in the WebView.
|
||||
*/
|
||||
source: PropTypes.oneOfType([
|
||||
PropTypes.shape({
|
||||
/*
|
||||
* The URI to load in the WebView. Can be a local or remote file.
|
||||
*/
|
||||
uri: PropTypes.string,
|
||||
/*
|
||||
* The HTTP Method to use. Defaults to GET if not specified.
|
||||
* NOTE: On Android, only GET and POST are supported.
|
||||
*/
|
||||
method: PropTypes.oneOf(['GET', 'POST']),
|
||||
/*
|
||||
* Additional HTTP headers to send with the request.
|
||||
* NOTE: On Android, this can only be used with GET requests.
|
||||
*/
|
||||
headers: PropTypes.object,
|
||||
/*
|
||||
* The HTTP body to send with the request. This must be a valid
|
||||
* UTF-8 string, and will be sent exactly as specified, with no
|
||||
* additional encoding (e.g. URL-escaping or base64) applied.
|
||||
* NOTE: On Android, this can only be used with POST requests.
|
||||
*/
|
||||
body: PropTypes.string,
|
||||
}),
|
||||
PropTypes.shape({
|
||||
/*
|
||||
* A static HTML page to display in the WebView.
|
||||
*/
|
||||
html: PropTypes.string,
|
||||
/*
|
||||
* The base URL to be used for any relative links in the HTML.
|
||||
*/
|
||||
baseUrl: PropTypes.string,
|
||||
}),
|
||||
/*
|
||||
* Used internally by packager.
|
||||
*/
|
||||
PropTypes.number,
|
||||
]),
|
||||
|
||||
/**
|
||||
* If true, use WKWebView instead of UIWebView.
|
||||
* @platform ios
|
||||
*/
|
||||
useWebKit: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Used on Android only to disable Hardware Acceleration if needed
|
||||
* Hardware acceleration can not be enabled at view level but it can be
|
||||
* disabled see:
|
||||
* https://developer.android.com/guide/topics/graphics/hardware-accel
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
hardwareAccelerationEnabledExperimental: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Used on Android only, JS is enabled by default for WebView on iOS
|
||||
* @platform android
|
||||
*/
|
||||
javaScriptEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Used on Android Lollipop and above only, third party cookies are enabled
|
||||
* by default for WebView on Android Kitkat and below and on iOS
|
||||
* @platform android
|
||||
*/
|
||||
thirdPartyCookiesEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Used on Android only, controls whether DOM Storage is enabled or not
|
||||
* @platform android
|
||||
*/
|
||||
domStorageEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Sets whether Geolocation is enabled. The default is false.
|
||||
* @platform android
|
||||
*/
|
||||
geolocationEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Sets the JS to be injected when the webpage loads.
|
||||
*/
|
||||
injectedJavaScript: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Sets whether the webpage scales to fit the view and the user can change the scale.
|
||||
*/
|
||||
scalesPageToFit: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Sets whether the webview allow access to file system.
|
||||
* @platform android
|
||||
*/
|
||||
allowFileAccess: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Sets the user-agent for this WebView. The user-agent can also be set in native using
|
||||
* WebViewConfig. This prop will overwrite that config.
|
||||
*/
|
||||
userAgent: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Used to locate this view in end-to-end tests.
|
||||
*/
|
||||
testID: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Determines whether HTML5 audio & videos require the user to tap before they can
|
||||
* start playing. The default value is `false`.
|
||||
*/
|
||||
mediaPlaybackRequiresUserAction: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Boolean that sets whether JavaScript running in the context of a file
|
||||
* scheme URL should be allowed to access content from any origin.
|
||||
* Including accessing content from other file scheme URLs
|
||||
* @platform android
|
||||
*/
|
||||
allowUniversalAccessFromFileURLs: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* List of origin strings to allow being navigated to. The strings allow
|
||||
* wildcards and get matched against *just* the origin (not the full URL).
|
||||
* If the user taps to navigate to a new page but the new page is not in
|
||||
* this whitelist, the URL will be opened by the Android OS.
|
||||
* The default whitelisted origins are "http://*" and "https://*".
|
||||
*/
|
||||
originWhitelist: PropTypes.arrayOf(PropTypes.string),
|
||||
|
||||
/**
|
||||
* Function that accepts a string that will be passed to the WebView and
|
||||
* executed immediately as JavaScript.
|
||||
*/
|
||||
injectJavaScript: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
|
||||
*
|
||||
* Possible values for `mixedContentMode` are:
|
||||
*
|
||||
* - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin.
|
||||
* - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
|
||||
* - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
|
||||
* @platform android
|
||||
*/
|
||||
mixedContentMode: PropTypes.oneOf(['never', 'always', 'compatibility']),
|
||||
|
||||
/**
|
||||
* Used on Android only, controls whether form autocomplete data should be saved
|
||||
* @platform android
|
||||
*/
|
||||
saveFormDataDisabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Override the native component used to render the WebView. Enables a custom native
|
||||
* WebView which uses the same JavaScript as the original WebView.
|
||||
*/
|
||||
nativeConfig: PropTypes.shape({
|
||||
/*
|
||||
* The native component used to render the WebView.
|
||||
*/
|
||||
component: PropTypes.any,
|
||||
/*
|
||||
* Set props directly on the native component WebView. Enables custom props which the
|
||||
* original WebView doesn't pass through.
|
||||
*/
|
||||
props: PropTypes.object,
|
||||
/*
|
||||
* Set the ViewManager to use for communication with the native side.
|
||||
* @platform ios
|
||||
*/
|
||||
viewManager: PropTypes.object,
|
||||
}),
|
||||
/*
|
||||
* Used on Android only, controls whether the given list of URL prefixes should
|
||||
* make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
|
||||
* default activity intent for those URL instead of loading it within the webview.
|
||||
* Use this to list URLs that WebView cannot handle, e.g. a PDF url.
|
||||
* @platform android
|
||||
*/
|
||||
urlPrefixesForDefaultIntent: PropTypes.arrayOf(PropTypes.string),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
javaScriptEnabled: true,
|
||||
thirdPartyCookiesEnabled: true,
|
||||
scalesPageToFit: true,
|
||||
hardwareAccelerationEnabledExperimental: true,
|
||||
saveFormDataDisabled: false,
|
||||
originWhitelist: WebViewShared.defaultOriginWhitelist,
|
||||
};
|
||||
|
||||
state = {
|
||||
viewState: WebViewState.IDLE,
|
||||
lastErrorEvent: null,
|
||||
startInLoadingState: true,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
if (this.props.startInLoadingState) {
|
||||
this.setState({viewState: WebViewState.LOADING});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let otherView = null;
|
||||
|
||||
if (this.state.viewState === WebViewState.LOADING) {
|
||||
otherView = (this.props.renderLoading || defaultRenderLoading)();
|
||||
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||
const errorEvent = this.state.lastErrorEvent;
|
||||
otherView =
|
||||
this.props.renderError &&
|
||||
this.props.renderError(
|
||||
errorEvent.domain,
|
||||
errorEvent.code,
|
||||
errorEvent.description,
|
||||
);
|
||||
} else if (this.state.viewState !== WebViewState.IDLE) {
|
||||
console.error(
|
||||
'RCTWebView invalid state encountered: ' + this.state.loading,
|
||||
);
|
||||
}
|
||||
|
||||
const webViewStyles = [styles.container, this.props.style];
|
||||
if (
|
||||
this.state.viewState === WebViewState.LOADING ||
|
||||
this.state.viewState === WebViewState.ERROR
|
||||
) {
|
||||
// if we're in either LOADING or ERROR states, don't show the webView
|
||||
webViewStyles.push(styles.hidden);
|
||||
}
|
||||
|
||||
const source = this.props.source || {};
|
||||
if (this.props.html) {
|
||||
source.html = this.props.html;
|
||||
} else if (this.props.url) {
|
||||
source.uri = this.props.url;
|
||||
}
|
||||
|
||||
if (source.method === 'POST' && source.headers) {
|
||||
console.warn(
|
||||
'WebView: `source.headers` is not supported when using POST.',
|
||||
);
|
||||
} else if (source.method === 'GET' && source.body) {
|
||||
console.warn('WebView: `source.body` is not supported when using GET.');
|
||||
}
|
||||
|
||||
const nativeConfig = this.props.nativeConfig || {};
|
||||
|
||||
const originWhitelist = (this.props.originWhitelist || []).map(
|
||||
WebViewShared.originWhitelistToRegex,
|
||||
);
|
||||
|
||||
let NativeWebView = nativeConfig.component || RCTWebView;
|
||||
|
||||
const webView = (
|
||||
<NativeWebView
|
||||
ref={RCT_WEBVIEW_REF}
|
||||
key="webViewKey"
|
||||
style={webViewStyles}
|
||||
source={resolveAssetSource(source)}
|
||||
scalesPageToFit={this.props.scalesPageToFit}
|
||||
allowFileAccess={this.props.allowFileAccess}
|
||||
injectedJavaScript={this.props.injectedJavaScript}
|
||||
userAgent={this.props.userAgent}
|
||||
javaScriptEnabled={this.props.javaScriptEnabled}
|
||||
hardwareAccelerationEnabledExperimental={
|
||||
this.props.hardwareAccelerationEnabledExperimental
|
||||
}
|
||||
thirdPartyCookiesEnabled={this.props.thirdPartyCookiesEnabled}
|
||||
domStorageEnabled={this.props.domStorageEnabled}
|
||||
messagingEnabled={typeof this.props.onMessage === 'function'}
|
||||
onMessage={this.onMessage}
|
||||
contentInset={this.props.contentInset}
|
||||
automaticallyAdjustContentInsets={
|
||||
this.props.automaticallyAdjustContentInsets
|
||||
}
|
||||
onContentSizeChange={this.props.onContentSizeChange}
|
||||
onLoadingStart={this.onLoadingStart}
|
||||
onLoadingFinish={this.onLoadingFinish}
|
||||
onLoadingError={this.onLoadingError}
|
||||
testID={this.props.testID}
|
||||
geolocationEnabled={this.props.geolocationEnabled}
|
||||
mediaPlaybackRequiresUserAction={
|
||||
this.props.mediaPlaybackRequiresUserAction
|
||||
}
|
||||
allowUniversalAccessFromFileURLs={
|
||||
this.props.allowUniversalAccessFromFileURLs
|
||||
}
|
||||
originWhitelist={originWhitelist}
|
||||
mixedContentMode={this.props.mixedContentMode}
|
||||
saveFormDataDisabled={this.props.saveFormDataDisabled}
|
||||
urlPrefixesForDefaultIntent={this.props.urlPrefixesForDefaultIntent}
|
||||
{...nativeConfig.props}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{webView}
|
||||
{otherView}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
goForward = () => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.getViewManagerConfig('RCTWebView').Commands.goForward,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
goBack = () => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.getViewManagerConfig('RCTWebView').Commands.goBack,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
reload = () => {
|
||||
this.setState({
|
||||
viewState: WebViewState.LOADING,
|
||||
});
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.getViewManagerConfig('RCTWebView').Commands.reload,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
stopLoading = () => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.getViewManagerConfig('RCTWebView').Commands.stopLoading,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
postMessage = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.getViewManagerConfig('RCTWebView').Commands.postMessage,
|
||||
[String(data)],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Injects a javascript string into the referenced WebView. Deliberately does not
|
||||
* return a response because using eval() to return a response breaks this method
|
||||
* on pages with a Content Security Policy that disallows eval(). If you need that
|
||||
* functionality, look into postMessage/onMessage.
|
||||
*/
|
||||
injectJavaScript = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
UIManager.getViewManagerConfig('RCTWebView').Commands.injectJavaScript,
|
||||
[data],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* We return an event with a bunch of fields including:
|
||||
* url, title, loading, canGoBack, canGoForward
|
||||
*/
|
||||
updateNavigationState = (event: any) => {
|
||||
if (this.props.onNavigationStateChange) {
|
||||
this.props.onNavigationStateChange(event.nativeEvent);
|
||||
}
|
||||
};
|
||||
|
||||
getWebViewHandle = () => {
|
||||
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
|
||||
};
|
||||
|
||||
onLoadingStart = (event: any) => {
|
||||
const onLoadStart = this.props.onLoadStart;
|
||||
onLoadStart && onLoadStart(event);
|
||||
this.updateNavigationState(event);
|
||||
};
|
||||
|
||||
onLoadingError = (event: any) => {
|
||||
event.persist(); // persist this event because we need to store it
|
||||
const {onError, onLoadEnd} = this.props;
|
||||
onError && onError(event);
|
||||
onLoadEnd && onLoadEnd(event);
|
||||
console.warn('Encountered an error loading page', event.nativeEvent);
|
||||
|
||||
this.setState({
|
||||
lastErrorEvent: event.nativeEvent,
|
||||
viewState: WebViewState.ERROR,
|
||||
});
|
||||
};
|
||||
|
||||
onLoadingFinish = (event: any) => {
|
||||
const {onLoad, onLoadEnd} = this.props;
|
||||
onLoad && onLoad(event);
|
||||
onLoadEnd && onLoadEnd(event);
|
||||
this.setState({
|
||||
viewState: WebViewState.IDLE,
|
||||
});
|
||||
this.updateNavigationState(event);
|
||||
};
|
||||
|
||||
onMessage = (event: any) => {
|
||||
const {onMessage} = this.props;
|
||||
onMessage && onMessage(event);
|
||||
};
|
||||
}
|
||||
|
||||
const RCTWebView = requireNativeComponent('RCTWebView');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
hidden: {
|
||||
height: 0,
|
||||
flex: 0, // disable 'flex:1' when hiding a View
|
||||
},
|
||||
loadingView: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
loadingProgressBar: {
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = WebView;
|
||||
@@ -1,774 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ActivityIndicator = require('ActivityIndicator');
|
||||
const DeprecatedViewPropTypes = require('DeprecatedViewPropTypes');
|
||||
const DeprecatedEdgeInsetsPropType = require('DeprecatedEdgeInsetsPropType');
|
||||
const Linking = require('Linking');
|
||||
const PropTypes = require('prop-types');
|
||||
const React = require('React');
|
||||
const ReactNative = require('ReactNative');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
const Text = require('Text');
|
||||
const UIManager = require('UIManager');
|
||||
const View = require('View');
|
||||
const WebViewShared = require('WebViewShared');
|
||||
|
||||
const deprecatedPropType = require('deprecatedPropType');
|
||||
const invariant = require('invariant');
|
||||
const keyMirror = require('fbjs/lib/keyMirror');
|
||||
const processDecelerationRate = require('processDecelerationRate');
|
||||
const requireNativeComponent = require('requireNativeComponent');
|
||||
const resolveAssetSource = require('resolveAssetSource');
|
||||
|
||||
const RCTWebViewManager = require('NativeModules').WebViewManager;
|
||||
const RCTWKWebViewManager = require('NativeModules').WKWebViewManager;
|
||||
|
||||
const BGWASH = 'rgba(255,255,255,0.8)';
|
||||
const RCT_WEBVIEW_REF = 'webview';
|
||||
|
||||
const WebViewState = keyMirror({
|
||||
IDLE: null,
|
||||
LOADING: null,
|
||||
ERROR: null,
|
||||
});
|
||||
|
||||
const NavigationType = keyMirror({
|
||||
click: true,
|
||||
formsubmit: true,
|
||||
backforward: true,
|
||||
reload: true,
|
||||
formresubmit: true,
|
||||
other: true,
|
||||
});
|
||||
|
||||
const JSNavigationScheme = 'react-js-navigation';
|
||||
|
||||
type ErrorEvent = {
|
||||
domain: any,
|
||||
code: any,
|
||||
description: any,
|
||||
};
|
||||
|
||||
type Event = Object;
|
||||
|
||||
const DataDetectorTypes = [
|
||||
'phoneNumber',
|
||||
'link',
|
||||
'address',
|
||||
'calendarEvent',
|
||||
'trackingNumber',
|
||||
'flightNumber',
|
||||
'lookupSuggestion',
|
||||
'none',
|
||||
'all',
|
||||
];
|
||||
|
||||
const defaultRenderLoading = () => (
|
||||
<View style={styles.loadingView}>
|
||||
<ActivityIndicator />
|
||||
</View>
|
||||
);
|
||||
const defaultRenderError = (errorDomain, errorCode, errorDesc) => (
|
||||
<View style={styles.errorContainer}>
|
||||
<Text style={styles.errorTextTitle}>Error loading page</Text>
|
||||
<Text style={styles.errorText}>{'Domain: ' + errorDomain}</Text>
|
||||
<Text style={styles.errorText}>{'Error Code: ' + errorCode}</Text>
|
||||
<Text style={styles.errorText}>{'Description: ' + errorDesc}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
type Props = any;
|
||||
type State = any;
|
||||
|
||||
/**
|
||||
* `WebView` renders web content in a native view.
|
||||
*
|
||||
*```
|
||||
* import React, { Component } from 'react';
|
||||
* import { WebView } from 'react-native';
|
||||
*
|
||||
* class MyWeb extends Component {
|
||||
* render() {
|
||||
* return (
|
||||
* <WebView
|
||||
* source={{uri: 'https://github.com/facebook/react-native'}}
|
||||
* style={{marginTop: 20}}
|
||||
* />
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
*```
|
||||
*
|
||||
* You can use this component to navigate back and forth in the web view's
|
||||
* history and configure various properties for the web content.
|
||||
*/
|
||||
class WebView extends React.Component<Props, State> {
|
||||
static JSNavigationScheme = JSNavigationScheme;
|
||||
static NavigationType = NavigationType;
|
||||
static propTypes = {
|
||||
...DeprecatedViewPropTypes,
|
||||
|
||||
html: deprecatedPropType(
|
||||
PropTypes.string,
|
||||
'Use the `source` prop instead.',
|
||||
),
|
||||
|
||||
url: deprecatedPropType(PropTypes.string, 'Use the `source` prop instead.'),
|
||||
|
||||
/**
|
||||
* Loads static html or a uri (with optional headers) in the WebView.
|
||||
*/
|
||||
source: PropTypes.oneOfType([
|
||||
PropTypes.shape({
|
||||
/*
|
||||
* The URI to load in the `WebView`. Can be a local or remote file.
|
||||
*/
|
||||
uri: PropTypes.string,
|
||||
/*
|
||||
* The HTTP Method to use. Defaults to GET if not specified.
|
||||
* NOTE: On Android, only GET and POST are supported.
|
||||
*/
|
||||
method: PropTypes.string,
|
||||
/*
|
||||
* Additional HTTP headers to send with the request.
|
||||
* NOTE: On Android, this can only be used with GET requests.
|
||||
*/
|
||||
headers: PropTypes.object,
|
||||
/*
|
||||
* The HTTP body to send with the request. This must be a valid
|
||||
* UTF-8 string, and will be sent exactly as specified, with no
|
||||
* additional encoding (e.g. URL-escaping or base64) applied.
|
||||
* NOTE: On Android, this can only be used with POST requests.
|
||||
*/
|
||||
body: PropTypes.string,
|
||||
}),
|
||||
PropTypes.shape({
|
||||
/*
|
||||
* A static HTML page to display in the WebView.
|
||||
*/
|
||||
html: PropTypes.string,
|
||||
/*
|
||||
* The base URL to be used for any relative links in the HTML.
|
||||
*/
|
||||
baseUrl: PropTypes.string,
|
||||
}),
|
||||
/*
|
||||
* Used internally by packager.
|
||||
*/
|
||||
PropTypes.number,
|
||||
]),
|
||||
|
||||
/**
|
||||
* If true, use WKWebView instead of UIWebView.
|
||||
* @platform ios
|
||||
*/
|
||||
useWebKit: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Function that returns a view to show if there's an error.
|
||||
*/
|
||||
renderError: PropTypes.func, // view to show if there's an error
|
||||
/**
|
||||
* Function that returns a loading indicator.
|
||||
*/
|
||||
renderLoading: PropTypes.func,
|
||||
/**
|
||||
* Function that is invoked when the `WebView` has finished loading.
|
||||
*/
|
||||
onLoad: PropTypes.func,
|
||||
/**
|
||||
* Function that is invoked when the `WebView` load succeeds or fails.
|
||||
*/
|
||||
onLoadEnd: PropTypes.func,
|
||||
/**
|
||||
* Function that is invoked when the `WebView` starts loading.
|
||||
*/
|
||||
onLoadStart: PropTypes.func,
|
||||
/**
|
||||
* Function that is invoked when the `WebView` load fails.
|
||||
*/
|
||||
onError: PropTypes.func,
|
||||
/**
|
||||
* Boolean value that determines whether the web view bounces
|
||||
* when it reaches the edge of the content. The default value is `true`.
|
||||
* @platform ios
|
||||
*/
|
||||
bounces: PropTypes.bool,
|
||||
/**
|
||||
* A floating-point number that determines how quickly the scroll view
|
||||
* decelerates after the user lifts their finger. You may also use the
|
||||
* string shortcuts `"normal"` and `"fast"` which match the underlying iOS
|
||||
* settings for `UIScrollViewDecelerationRateNormal` and
|
||||
* `UIScrollViewDecelerationRateFast` respectively:
|
||||
*
|
||||
* - normal: 0.998
|
||||
* - fast: 0.99 (the default for iOS web view)
|
||||
* @platform ios
|
||||
*/
|
||||
decelerationRate: PropTypes.oneOfType([
|
||||
PropTypes.oneOf(['fast', 'normal']),
|
||||
PropTypes.number,
|
||||
]),
|
||||
/**
|
||||
* Boolean value that determines whether scrolling is enabled in the
|
||||
* `WebView`. The default value is `true`.
|
||||
* @platform ios
|
||||
*/
|
||||
scrollEnabled: PropTypes.bool,
|
||||
/**
|
||||
* Controls whether to adjust the content inset for web views that are
|
||||
* placed behind a navigation bar, tab bar, or toolbar. The default value
|
||||
* is `true`.
|
||||
*/
|
||||
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||
/**
|
||||
* The amount by which the web view content is inset from the edges of
|
||||
* the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}.
|
||||
* @platform ios
|
||||
*/
|
||||
contentInset: DeprecatedEdgeInsetsPropType,
|
||||
/**
|
||||
* Function that is invoked when the `WebView` loading starts or ends.
|
||||
*/
|
||||
onNavigationStateChange: PropTypes.func,
|
||||
/**
|
||||
* A function that is invoked when the webview calls `window.postMessage`.
|
||||
* Setting this property will inject a `postMessage` global into your
|
||||
* webview, but will still call pre-existing values of `postMessage`.
|
||||
*
|
||||
* `window.postMessage` accepts one argument, `data`, which will be
|
||||
* available on the event object, `event.nativeEvent.data`. `data`
|
||||
* must be a string.
|
||||
*/
|
||||
onMessage: PropTypes.func,
|
||||
/**
|
||||
* Boolean value that forces the `WebView` to show the loading view
|
||||
* on the first load.
|
||||
*/
|
||||
startInLoadingState: PropTypes.bool,
|
||||
/**
|
||||
* The style to apply to the `WebView`.
|
||||
*/
|
||||
style: DeprecatedViewPropTypes.style,
|
||||
|
||||
/**
|
||||
* Determines the types of data converted to clickable URLs in the web view's content.
|
||||
* By default only phone numbers are detected.
|
||||
*
|
||||
* You can provide one type or an array of many types.
|
||||
*
|
||||
* Possible values for `dataDetectorTypes` are:
|
||||
*
|
||||
* - `'phoneNumber'`
|
||||
* - `'link'`
|
||||
* - `'address'`
|
||||
* - `'calendarEvent'`
|
||||
* - `'none'`
|
||||
* - `'all'`
|
||||
*
|
||||
* With the new WebKit implementation, we have three new values:
|
||||
* - `'trackingNumber'`,
|
||||
* - `'flightNumber'`,
|
||||
* - `'lookupSuggestion'`,
|
||||
*
|
||||
* @platform ios
|
||||
*/
|
||||
dataDetectorTypes: PropTypes.oneOfType([
|
||||
PropTypes.oneOf(DataDetectorTypes),
|
||||
PropTypes.arrayOf(PropTypes.oneOf(DataDetectorTypes)),
|
||||
]),
|
||||
|
||||
/**
|
||||
* Used on Android only to disable Hardware Acceleration if needed
|
||||
* Hardware acceleration can not be enabled at view level but it can be
|
||||
* disabled see:
|
||||
* https://developer.android.com/guide/topics/graphics/hardware-accel
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
hardwareAccelerationEnabledExperimental: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Boolean value to enable JavaScript in the `WebView`. Used on Android only
|
||||
* as JavaScript is enabled by default on iOS. The default value is `true`.
|
||||
* @platform android
|
||||
*/
|
||||
javaScriptEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Boolean value to enable third party cookies in the `WebView`. Used on
|
||||
* Android Lollipop and above only as third party cookies are enabled by
|
||||
* default on Android Kitkat and below and on iOS. The default value is `true`.
|
||||
* @platform android
|
||||
*/
|
||||
thirdPartyCookiesEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Boolean value to control whether DOM Storage is enabled. Used only in
|
||||
* Android.
|
||||
* @platform android
|
||||
*/
|
||||
domStorageEnabled: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Set this to provide JavaScript that will be injected into the web page
|
||||
* when the view loads.
|
||||
*/
|
||||
injectedJavaScript: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Sets the user-agent for the `WebView`.
|
||||
* @platform android
|
||||
*/
|
||||
userAgent: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Boolean that controls whether the web content is scaled to fit
|
||||
* the view and enables the user to change the scale. The default value
|
||||
* is `true`.
|
||||
*
|
||||
* On iOS, when `useWebKit=true`, this prop will not work.
|
||||
*/
|
||||
scalesPageToFit: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Function that allows custom handling of any web view requests. Return
|
||||
* `true` from the function to continue loading the request and `false`
|
||||
* to stop loading.
|
||||
* @platform ios
|
||||
*/
|
||||
onShouldStartLoadWithRequest: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Boolean that determines whether HTML5 videos play inline or use the
|
||||
* native full-screen controller. The default value is `false`.
|
||||
*
|
||||
* **NOTE** : In order for video to play inline, not only does this
|
||||
* property need to be set to `true`, but the video element in the HTML
|
||||
* document must also include the `webkit-playsinline` attribute.
|
||||
* @platform ios
|
||||
*/
|
||||
allowsInlineMediaPlayback: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Boolean that determines whether HTML5 audio and video requires the user
|
||||
* to tap them before they start playing. The default value is `true`.
|
||||
*/
|
||||
mediaPlaybackRequiresUserAction: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* List of origin strings to allow being navigated to. The strings allow
|
||||
* wildcards and get matched against *just* the origin (not the full URL).
|
||||
* If the user taps to navigate to a new page but the new page is not in
|
||||
* this whitelist, we will open the URL in Safari.
|
||||
* The default whitelisted origins are "http://*" and "https://*".
|
||||
*/
|
||||
originWhitelist: PropTypes.arrayOf(PropTypes.string),
|
||||
|
||||
/**
|
||||
* Function that accepts a string that will be passed to the WebView and
|
||||
* executed immediately as JavaScript.
|
||||
*/
|
||||
injectJavaScript: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
|
||||
*
|
||||
* Possible values for `mixedContentMode` are:
|
||||
*
|
||||
* - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin.
|
||||
* - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
|
||||
* - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
|
||||
* @platform android
|
||||
*/
|
||||
mixedContentMode: PropTypes.oneOf(['never', 'always', 'compatibility']),
|
||||
|
||||
/**
|
||||
* Override the native component used to render the WebView. Enables a custom native
|
||||
* WebView which uses the same JavaScript as the original WebView.
|
||||
*/
|
||||
nativeConfig: PropTypes.shape({
|
||||
/*
|
||||
* The native component used to render the WebView.
|
||||
*/
|
||||
component: PropTypes.any,
|
||||
/*
|
||||
* Set props directly on the native component WebView. Enables custom props which the
|
||||
* original WebView doesn't pass through.
|
||||
*/
|
||||
props: PropTypes.object,
|
||||
/*
|
||||
* Set the ViewManager to use for communication with the native side.
|
||||
* @platform ios
|
||||
*/
|
||||
viewManager: PropTypes.object,
|
||||
}),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
originWhitelist: WebViewShared.defaultOriginWhitelist,
|
||||
};
|
||||
|
||||
state = {
|
||||
viewState: WebViewState.IDLE,
|
||||
lastErrorEvent: (null: ?ErrorEvent),
|
||||
startInLoadingState: true,
|
||||
};
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
if (this.props.startInLoadingState) {
|
||||
this.setState({viewState: WebViewState.LOADING});
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.useWebKit === true &&
|
||||
this.props.scalesPageToFit !== undefined
|
||||
) {
|
||||
console.warn(
|
||||
'The scalesPageToFit property is not supported when useWebKit = true',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let otherView = null;
|
||||
|
||||
let scalesPageToFit;
|
||||
|
||||
if (this.props.useWebKit) {
|
||||
({scalesPageToFit} = this.props);
|
||||
} else {
|
||||
({scalesPageToFit = true} = this.props);
|
||||
}
|
||||
|
||||
if (this.state.viewState === WebViewState.LOADING) {
|
||||
otherView = (this.props.renderLoading || defaultRenderLoading)();
|
||||
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||
const errorEvent = this.state.lastErrorEvent;
|
||||
invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
|
||||
otherView = (this.props.renderError || defaultRenderError)(
|
||||
errorEvent.domain,
|
||||
errorEvent.code,
|
||||
errorEvent.description,
|
||||
);
|
||||
} else if (this.state.viewState !== WebViewState.IDLE) {
|
||||
console.error(
|
||||
'RCTWebView invalid state encountered: ' + this.state.loading,
|
||||
);
|
||||
}
|
||||
|
||||
const webViewStyles = [styles.container, styles.webView, this.props.style];
|
||||
if (
|
||||
this.state.viewState === WebViewState.LOADING ||
|
||||
this.state.viewState === WebViewState.ERROR
|
||||
) {
|
||||
// if we're in either LOADING or ERROR states, don't show the webView
|
||||
webViewStyles.push(styles.hidden);
|
||||
}
|
||||
|
||||
const nativeConfig = this.props.nativeConfig || {};
|
||||
|
||||
let viewManager = nativeConfig.viewManager;
|
||||
|
||||
if (this.props.useWebKit) {
|
||||
viewManager = viewManager || RCTWKWebViewManager;
|
||||
} else {
|
||||
viewManager = viewManager || RCTWebViewManager;
|
||||
}
|
||||
|
||||
const compiledWhitelist = [
|
||||
'about:blank',
|
||||
...(this.props.originWhitelist || []),
|
||||
].map(WebViewShared.originWhitelistToRegex);
|
||||
const onShouldStartLoadWithRequest = (event: Event) => {
|
||||
let shouldStart = true;
|
||||
const {url} = event.nativeEvent;
|
||||
const origin = WebViewShared.extractOrigin(url);
|
||||
const passesWhitelist = compiledWhitelist.some(
|
||||
x => origin && new RegExp(x).test(origin),
|
||||
);
|
||||
shouldStart = shouldStart && passesWhitelist;
|
||||
if (!passesWhitelist) {
|
||||
Linking.openURL(url);
|
||||
}
|
||||
if (this.props.onShouldStartLoadWithRequest) {
|
||||
shouldStart =
|
||||
shouldStart &&
|
||||
this.props.onShouldStartLoadWithRequest(event.nativeEvent);
|
||||
}
|
||||
viewManager.startLoadWithResult(
|
||||
!!shouldStart,
|
||||
event.nativeEvent.lockIdentifier,
|
||||
);
|
||||
};
|
||||
|
||||
const decelerationRate = processDecelerationRate(
|
||||
this.props.decelerationRate,
|
||||
);
|
||||
|
||||
const source = this.props.source || {};
|
||||
if (this.props.html) {
|
||||
source.html = this.props.html;
|
||||
} else if (this.props.url) {
|
||||
source.uri = this.props.url;
|
||||
}
|
||||
|
||||
const messagingEnabled = typeof this.props.onMessage === 'function';
|
||||
|
||||
let NativeWebView = nativeConfig.component;
|
||||
|
||||
if (this.props.useWebKit) {
|
||||
NativeWebView = NativeWebView || RCTWKWebView;
|
||||
} else {
|
||||
NativeWebView = NativeWebView || RCTWebView;
|
||||
}
|
||||
|
||||
const webView = (
|
||||
<NativeWebView
|
||||
ref={RCT_WEBVIEW_REF}
|
||||
key="webViewKey"
|
||||
style={webViewStyles}
|
||||
source={resolveAssetSource(source)}
|
||||
injectedJavaScript={this.props.injectedJavaScript}
|
||||
bounces={this.props.bounces}
|
||||
scrollEnabled={this.props.scrollEnabled}
|
||||
decelerationRate={decelerationRate}
|
||||
contentInset={this.props.contentInset}
|
||||
automaticallyAdjustContentInsets={
|
||||
this.props.automaticallyAdjustContentInsets
|
||||
}
|
||||
onLoadingStart={this._onLoadingStart}
|
||||
onLoadingFinish={this._onLoadingFinish}
|
||||
onLoadingError={this._onLoadingError}
|
||||
messagingEnabled={messagingEnabled}
|
||||
onMessage={this._onMessage}
|
||||
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
||||
scalesPageToFit={scalesPageToFit}
|
||||
allowsInlineMediaPlayback={this.props.allowsInlineMediaPlayback}
|
||||
mediaPlaybackRequiresUserAction={
|
||||
this.props.mediaPlaybackRequiresUserAction
|
||||
}
|
||||
dataDetectorTypes={this.props.dataDetectorTypes}
|
||||
{...nativeConfig.props}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{webView}
|
||||
{otherView}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_getCommands() {
|
||||
if (!this.props.useWebKit) {
|
||||
return UIManager.getViewManagerConfig('RCTWebView').Commands;
|
||||
}
|
||||
|
||||
return UIManager.getViewManagerConfig('RCTWKWebView').Commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go forward one page in the web view's history.
|
||||
*/
|
||||
goForward = () => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().goForward,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Go back one page in the web view's history.
|
||||
*/
|
||||
goBack = () => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().goBack,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reloads the current page.
|
||||
*/
|
||||
reload = () => {
|
||||
this.setState({viewState: WebViewState.LOADING});
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().reload,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop loading the current page.
|
||||
*/
|
||||
stopLoading = () => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().stopLoading,
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Posts a message to the web view, which will emit a `message` event.
|
||||
* Accepts one argument, `data`, which must be a string.
|
||||
*
|
||||
* In your webview, you'll need to something like the following.
|
||||
*
|
||||
* ```js
|
||||
* document.addEventListener('message', e => { document.title = e.data; });
|
||||
* ```
|
||||
*/
|
||||
postMessage = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().postMessage,
|
||||
[String(data)],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Injects a javascript string into the referenced WebView. Deliberately does not
|
||||
* return a response because using eval() to return a response breaks this method
|
||||
* on pages with a Content Security Policy that disallows eval(). If you need that
|
||||
* functionality, look into postMessage/onMessage.
|
||||
*/
|
||||
injectJavaScript = (data: string) => {
|
||||
UIManager.dispatchViewManagerCommand(
|
||||
this.getWebViewHandle(),
|
||||
this._getCommands().injectJavaScript,
|
||||
[data],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* We return an event with a bunch of fields including:
|
||||
* url, title, loading, canGoBack, canGoForward
|
||||
*/
|
||||
_updateNavigationState = (event: Event) => {
|
||||
if (this.props.onNavigationStateChange) {
|
||||
this.props.onNavigationStateChange(event.nativeEvent);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the native `WebView` node.
|
||||
*/
|
||||
getWebViewHandle = (): any => {
|
||||
return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
|
||||
};
|
||||
|
||||
_onLoadingStart = (event: Event) => {
|
||||
const onLoadStart = this.props.onLoadStart;
|
||||
onLoadStart && onLoadStart(event);
|
||||
this._updateNavigationState(event);
|
||||
};
|
||||
|
||||
_onLoadingError = (event: Event) => {
|
||||
event.persist(); // persist this event because we need to store it
|
||||
const {onError, onLoadEnd} = this.props;
|
||||
onError && onError(event);
|
||||
onLoadEnd && onLoadEnd(event);
|
||||
console.warn('Encountered an error loading page', event.nativeEvent);
|
||||
|
||||
this.setState({
|
||||
lastErrorEvent: event.nativeEvent,
|
||||
viewState: WebViewState.ERROR,
|
||||
});
|
||||
};
|
||||
|
||||
_onLoadingFinish = (event: Event) => {
|
||||
const {onLoad, onLoadEnd} = this.props;
|
||||
onLoad && onLoad(event);
|
||||
onLoadEnd && onLoadEnd(event);
|
||||
this.setState({
|
||||
viewState: WebViewState.IDLE,
|
||||
});
|
||||
this._updateNavigationState(event);
|
||||
};
|
||||
|
||||
_onMessage = (event: Event) => {
|
||||
const {onMessage} = this.props;
|
||||
onMessage && onMessage(event);
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (!(prevProps.useWebKit && this.props.useWebKit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._showRedboxOnPropChanges(prevProps, 'allowsInlineMediaPlayback');
|
||||
this._showRedboxOnPropChanges(prevProps, 'mediaPlaybackRequiresUserAction');
|
||||
this._showRedboxOnPropChanges(prevProps, 'dataDetectorTypes');
|
||||
|
||||
if (this.props.scalesPageToFit !== undefined) {
|
||||
console.warn(
|
||||
'The scalesPageToFit property is not supported when useWebKit = true',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_showRedboxOnPropChanges(prevProps, propName: string) {
|
||||
if (this.props[propName] !== prevProps[propName]) {
|
||||
console.error(
|
||||
`Changes to property ${propName} do nothing after the initial render.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RCTWebView = requireNativeComponent('RCTWebView');
|
||||
const RCTWKWebView = requireNativeComponent('RCTWKWebView');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
errorContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: BGWASH,
|
||||
},
|
||||
errorText: {
|
||||
fontSize: 14,
|
||||
textAlign: 'center',
|
||||
marginBottom: 2,
|
||||
},
|
||||
errorTextTitle: {
|
||||
fontSize: 15,
|
||||
fontWeight: '500',
|
||||
marginBottom: 10,
|
||||
},
|
||||
hidden: {
|
||||
height: 0,
|
||||
flex: 0, // disable 'flex:1' when hiding a View
|
||||
},
|
||||
loadingView: {
|
||||
backgroundColor: BGWASH,
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: 100,
|
||||
},
|
||||
webView: {
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = WebView;
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const escapeStringRegexp = require('escape-string-regexp');
|
||||
|
||||
const WebViewShared = {
|
||||
defaultOriginWhitelist: ['http://*', 'https://*'],
|
||||
extractOrigin: (url: string): ?string => {
|
||||
const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
|
||||
return result === null ? null : result[0];
|
||||
},
|
||||
originWhitelistToRegex: (originWhitelist: string): string => {
|
||||
return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = WebViewShared;
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const WebViewShared = require('WebViewShared');
|
||||
|
||||
describe('WebViewShared', () => {
|
||||
it('extracts the origin correctly', () => {
|
||||
expect(WebViewShared.extractOrigin('http://facebook.com')).toBe(
|
||||
'http://facebook.com',
|
||||
);
|
||||
expect(WebViewShared.extractOrigin('https://facebook.com')).toBe(
|
||||
'https://facebook.com',
|
||||
);
|
||||
expect(WebViewShared.extractOrigin('http://facebook.com:8081')).toBe(
|
||||
'http://facebook.com:8081',
|
||||
);
|
||||
expect(WebViewShared.extractOrigin('ftp://facebook.com')).toBe(
|
||||
'ftp://facebook.com',
|
||||
);
|
||||
expect(WebViewShared.extractOrigin('myweirdscheme://')).toBe(
|
||||
'myweirdscheme://',
|
||||
);
|
||||
expect(WebViewShared.extractOrigin('http://facebook.com/')).toBe(
|
||||
'http://facebook.com',
|
||||
);
|
||||
expect(WebViewShared.extractOrigin('http://facebook.com/longerurl')).toBe(
|
||||
'http://facebook.com',
|
||||
);
|
||||
expect(
|
||||
WebViewShared.extractOrigin('http://facebook.com/http://facebook.com'),
|
||||
).toBe('http://facebook.com');
|
||||
expect(
|
||||
WebViewShared.extractOrigin('http://facebook.com//http://facebook.com'),
|
||||
).toBe('http://facebook.com');
|
||||
expect(
|
||||
WebViewShared.extractOrigin('http://facebook.com//http://facebook.com//'),
|
||||
).toBe('http://facebook.com');
|
||||
expect(WebViewShared.extractOrigin('about:blank')).toBe('about:blank');
|
||||
});
|
||||
|
||||
it('rejects bad urls', () => {
|
||||
expect(WebViewShared.extractOrigin('a/b')).toBeNull();
|
||||
expect(WebViewShared.extractOrigin('a//b')).toBeNull();
|
||||
});
|
||||
|
||||
it('creates a whitelist regex correctly', () => {
|
||||
expect(WebViewShared.originWhitelistToRegex('http://*')).toBe('http://.*');
|
||||
expect(WebViewShared.originWhitelistToRegex('*')).toBe('.*');
|
||||
expect(WebViewShared.originWhitelistToRegex('*//test')).toBe('.*//test');
|
||||
expect(WebViewShared.originWhitelistToRegex('*/*')).toBe('.*/.*');
|
||||
expect(WebViewShared.originWhitelistToRegex('*.com')).toBe('.*\\.com');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user