/** * 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. * * @providesModule WebView */ 'use strict'; var EdgeInsetsPropType = require('EdgeInsetsPropType'); var React = require('React'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var StyleSheet = require('StyleSheet'); var UIManager = require('UIManager'); var View = require('View'); var keyMirror = require('keyMirror'); var merge = require('merge'); var requireNativeComponent = require('requireNativeComponent'); var PropTypes = React.PropTypes; var RCT_WEBVIEW_REF = 'webview'; var WebViewState = keyMirror({ IDLE: null, LOADING: null, ERROR: null, }); /** * Renders a native WebView. */ var WebView = React.createClass({ propTypes: { ...View.propTypes, renderError: PropTypes.func, renderLoading: PropTypes.func, onLoad: PropTypes.func, onLoadEnd: PropTypes.func, onLoadStart: PropTypes.func, onError: PropTypes.func, url: PropTypes.string, html: PropTypes.string, 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 */ javaScriptEnabled: PropTypes.bool, /** * Used on Android only, controls whether DOM Storage is enabled or not * @platform android */ domStorageEnabled: PropTypes.bool, /** * Sets the JS to be injected when the webpage loads. */ injectedJavaScript: PropTypes.string, /** * 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, }, getInitialState: function() { return { viewState: WebViewState.IDLE, lastErrorEvent: null, 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 && this.props.renderLoading(); } else if (this.state.viewState === WebViewState.ERROR) { var 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); } var 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); } var {javaScriptEnabled, domStorageEnabled} = this.props; if (this.props.javaScriptEnabledAndroid) { console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); javaScriptEnabled = this.props.javaScriptEnabledAndroid; } if (this.props.domStorageEnabledAndroid) { console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); domStorageEnabled = this.props.domStorageEnabledAndroid; } var webView = ; return ( {webView} {otherView} ); }, goForward: function() { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), UIManager.RCTWebView.Commands.goForward, null ); }, goBack: function() { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), UIManager.RCTWebView.Commands.goBack, null ); }, reload: function() { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), UIManager.RCTWebView.Commands.reload, null ); }, /** * We return an event with a bunch of fields including: * url, title, loading, canGoBack, canGoForward */ updateNavigationState: function(event) { if (this.props.onNavigationStateChange) { this.props.onNavigationStateChange(event.nativeEvent); } }, getWebViewHandle: function() { return React.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); }, onLoadingStart: function(event) { var onLoadStart = this.props.onLoadStart; onLoadStart && onLoadStart(event); this.updateNavigationState(event); }, onLoadingError: function(event) { event.persist(); // persist this event because we need to store it var {onError, onLoadEnd} = this.props; onError && onError(event); onLoadEnd && onLoadEnd(event); console.error('Encountered an error loading page', event.nativeEvent); this.setState({ lastErrorEvent: event.nativeEvent, viewState: WebViewState.ERROR }); }, onLoadingFinish: function(event) { var {onLoad, onLoadEnd} = this.props; onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); this.setState({ viewState: WebViewState.IDLE, }); this.updateNavigationState(event); }, }); var RCTWebView = requireNativeComponent('RCTWebView', WebView); var styles = StyleSheet.create({ container: { flex: 1, }, hidden: { height: 0, flex: 0, // disable 'flex:1' when hiding a View }, }); module.exports = WebView;