react-native/Libraries/Components/WebView/WebView.ios.js

324 lines
8.7 KiB
JavaScript
Raw Normal View History

2015-03-14 08:22:25 +00:00
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
2015-03-14 08:22:25 +00:00
*
* @providesModule WebView
2015-03-25 19:55:10 +00:00
* @flow
2015-03-14 08:22:25 +00:00
*/
'use strict';
var ActivityIndicatorIOS = require('ActivityIndicatorIOS');
2015-03-14 08:22:25 +00:00
var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
2015-03-14 08:22:25 +00:00
var View = require('View');
var invariant = require('invariant');
2015-03-14 08:22:25 +00:00
var keyMirror = require('keyMirror');
var requireNativeComponent = require('requireNativeComponent');
2015-03-14 08:22:25 +00:00
var PropTypes = React.PropTypes;
var RCTWebViewManager = require('NativeModules').WebViewManager;
2015-03-14 08:22:25 +00:00
var BGWASH = 'rgba(255,255,255,0.8)';
2015-03-17 10:08:46 +00:00
var RCT_WEBVIEW_REF = 'webview';
2015-03-14 08:22:25 +00:00
var WebViewState = keyMirror({
IDLE: null,
LOADING: null,
ERROR: null,
});
var NavigationType = {
2015-03-17 10:08:46 +00:00
click: RCTWebViewManager.NavigationType.LinkClicked,
formsubmit: RCTWebViewManager.NavigationType.FormSubmitted,
backforward: RCTWebViewManager.NavigationType.BackForward,
reload: RCTWebViewManager.NavigationType.Reload,
formresubmit: RCTWebViewManager.NavigationType.FormResubmitted,
other: RCTWebViewManager.NavigationType.Other,
2015-03-14 08:22:25 +00:00
};
var JSNavigationScheme = RCTWebViewManager.JSNavigationScheme;
2015-03-25 19:55:10 +00:00
type ErrorEvent = {
domain: any;
code: any;
description: any;
}
type Event = Object;
var defaultRenderLoading = () => (
<View style={styles.loadingView}>
<ActivityIndicatorIOS />
</View>
);
var 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>
);
/**
* Renders a native WebView.
*/
2015-03-14 08:22:25 +00:00
var WebView = React.createClass({
statics: {
JSNavigationScheme: JSNavigationScheme,
2015-03-14 08:22:25 +00:00
NavigationType: NavigationType,
},
propTypes: {
...View.propTypes,
url: PropTypes.string,
html: PropTypes.string,
/**
* 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,
/**
* @platform ios
*/
bounces: PropTypes.bool,
/**
* @platform ios
*/
scrollEnabled: PropTypes.bool,
2015-03-14 08:22:25 +00:00
automaticallyAdjustContentInsets: PropTypes.bool,
contentInset: EdgeInsetsPropType,
onNavigationStateChange: PropTypes.func,
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
style: View.propTypes.style,
/**
* Used on Android only, JS is enabled by default for WebView on iOS
* @platform android
*/
javaScriptEnabledAndroid: PropTypes.bool,
/**
* Used on Android only, controls whether DOM Storage is enabled or not
* @platform android
*/
domStorageEnabledAndroid: 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.
* @platform ios
*/
scalesPageToFit: PropTypes.bool,
/**
* Allows custom handling of any webview requests by a JS handler. Return true
* or false from this method to continue loading the request.
* @platform ios
*/
onShouldStartLoadWithRequest: PropTypes.func,
/**
* Determines whether HTML5 videos play inline or use the native full-screen
* controller.
* default value `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,
2015-03-14 08:22:25 +00:00
},
getInitialState: function() {
return {
viewState: WebViewState.IDLE,
2015-03-25 19:55:10 +00:00
lastErrorEvent: (null: ?ErrorEvent),
2015-03-14 08:22:25 +00:00
startInLoadingState: true,
};
},
componentWillMount: function() {
if (this.props.startInLoadingState) {
this.setState({viewState: WebViewState.LOADING});
}
},
render: function() {
var otherView = null;
if (this.state.viewState === WebViewState.LOADING) {
otherView = (this.props.renderLoading || defaultRenderLoading)();
2015-03-14 08:22:25 +00:00
} else if (this.state.viewState === WebViewState.ERROR) {
var errorEvent = this.state.lastErrorEvent;
2015-03-25 19:55:10 +00:00
invariant(
errorEvent != null,
'lastErrorEvent expected to be non-null'
);
otherView = (this.props.renderError || defaultRenderError)(
2015-03-14 08:22:25 +00:00
errorEvent.domain,
errorEvent.code,
errorEvent.description
);
2015-03-14 08:22:25 +00:00
} else if (this.state.viewState !== WebViewState.IDLE) {
console.error(
'RCTWebView invalid state encountered: ' + this.state.loading
);
2015-03-14 08:22:25 +00:00
}
var webViewStyles = [styles.container, styles.webView, this.props.style];
2015-03-14 08:22:25 +00:00
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);
}
var onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => {
var shouldStart = this.props.onShouldStartLoadWithRequest &&
this.props.onShouldStartLoadWithRequest(event.nativeEvent);
RCTWebViewManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier);
});
2015-03-14 08:22:25 +00:00
var webView =
<RCTWebView
2015-03-17 10:08:46 +00:00
ref={RCT_WEBVIEW_REF}
2015-03-14 08:22:25 +00:00
key="webViewKey"
style={webViewStyles}
url={this.props.url}
html={this.props.html}
injectedJavaScript={this.props.injectedJavaScript}
bounces={this.props.bounces}
scrollEnabled={this.props.scrollEnabled}
2015-03-14 08:22:25 +00:00
contentInset={this.props.contentInset}
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
onLoadingStart={this.onLoadingStart}
onLoadingFinish={this.onLoadingFinish}
onLoadingError={this.onLoadingError}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
scalesPageToFit={this.props.scalesPageToFit}
allowsInlineMediaPlayback={this.props.allowsInlineMediaPlayback}
2015-03-14 08:22:25 +00:00
/>;
return (
<View style={styles.container}>
{webView}
{otherView}
</View>
);
},
goForward: function() {
RCTWebViewManager.goForward(this.getWebViewHandle());
2015-03-14 08:22:25 +00:00
},
goBack: function() {
RCTWebViewManager.goBack(this.getWebViewHandle());
2015-03-14 08:22:25 +00:00
},
reload: function() {
RCTWebViewManager.reload(this.getWebViewHandle());
2015-03-14 08:22:25 +00:00
},
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
*/
2015-03-25 19:55:10 +00:00
updateNavigationState: function(event: Event) {
2015-03-14 08:22:25 +00:00
if (this.props.onNavigationStateChange) {
this.props.onNavigationStateChange(event.nativeEvent);
}
},
getWebViewHandle: function(): any {
return React.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
2015-03-14 08:22:25 +00:00
},
2015-03-25 19:55:10 +00:00
onLoadingStart: function(event: Event) {
2015-03-14 08:22:25 +00:00
this.updateNavigationState(event);
},
2015-03-25 19:55:10 +00:00
onLoadingError: function(event: Event) {
2015-03-14 08:22:25 +00:00
event.persist(); // persist this event because we need to store it
2015-08-27 21:04:59 +00:00
console.warn('Encountered an error loading page', event.nativeEvent);
2015-03-14 08:22:25 +00:00
this.setState({
lastErrorEvent: event.nativeEvent,
viewState: WebViewState.ERROR
});
},
2015-03-25 19:55:10 +00:00
onLoadingFinish: function(event: Event) {
2015-03-14 08:22:25 +00:00
this.setState({
viewState: WebViewState.IDLE,
});
this.updateNavigationState(event);
},
});
var RCTWebView = requireNativeComponent('RCTWebView', WebView, {
nativeOnly: {
onLoadingStart: true,
onLoadingError: true,
onLoadingFinish: true,
},
});
2015-03-14 08:22:25 +00:00
var 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,
},
2015-03-14 08:22:25 +00:00
hidden: {
height: 0,
flex: 0, // disable 'flex:1' when hiding a View
},
loadingView: {
backgroundColor: BGWASH,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
webView: {
backgroundColor: '#ffffff',
}
2015-03-14 08:22:25 +00:00
});
module.exports = WebView;