From 5599299cc5ae787fbd55e73d8bffb1f2ce169ec8 Mon Sep 17 00:00:00 2001 From: Jacob Parker Date: Sun, 16 Oct 2016 06:29:14 -0700 Subject: [PATCH] =?UTF-8?q?Implement=20a=20postMessage=20function=20and=20?= =?UTF-8?q?an=20onMessage=20event=20for=20webviews=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: JS API very similar to web workers and node's child process. Work has been done by somebody else for the Android implementation over at #7020, so we'd need to have these in sync before anything gets merged. I've made a prop `messagingEnabled` to be more explicit about creating globals—it might be sufficient to just check for an onMessage handler though. ![screen shot 2016-09-06 at 10 28 23](https://cloud.githubusercontent.com/assets/7275322/18268669/b1a12348-741c-11e6-91a1-ad39d5a8bc03.png) Closes https://github.com/facebook/react-native/pull/9762 Differential Revision: D4008260 fbshipit-source-id: 84b1afafbc0ab1edc3dfbf1a8fb870218e171a4c --- Examples/UIExplorer/js/WebViewExample.js | 446 +++++++++++++ .../Components/WebView/WebView.android.js | 355 +++++++++++ Libraries/Components/WebView/WebView.ios.js | 590 ++++++++++++++++++ RNTester/js/assets/messagingtest.html | 4 +- React/Views/RCTWebView.h | 47 ++ React/Views/RCTWebView.m | 311 +++++++++ React/Views/RCTWebViewManager.m | 148 +++++ .../uimanager/UIManagerModuleConstants.java | 10 + .../com/facebook/react/views/webview/BUCK | 21 + .../views/webview/ReactWebViewManager.java | 521 ++++++++++++++++ .../views/webview/events/TopMessageEvent.java | 52 ++ 11 files changed, 2503 insertions(+), 2 deletions(-) create mode 100644 Examples/UIExplorer/js/WebViewExample.js create mode 100644 Libraries/Components/WebView/WebView.android.js create mode 100644 Libraries/Components/WebView/WebView.ios.js create mode 100644 React/Views/RCTWebView.h create mode 100644 React/Views/RCTWebView.m create mode 100644 React/Views/RCTWebViewManager.m create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/BUCK create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopMessageEvent.java diff --git a/Examples/UIExplorer/js/WebViewExample.js b/Examples/UIExplorer/js/WebViewExample.js new file mode 100644 index 00000000000000..be1fb44aec1148 --- /dev/null +++ b/Examples/UIExplorer/js/WebViewExample.js @@ -0,0 +1,446 @@ +/** + * Copyright (c) 2013-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. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var { + StyleSheet, + Text, + TextInput, + TouchableWithoutFeedback, + TouchableOpacity, + View, + WebView +} = ReactNative; + +var HEADER = '#3b5998'; +var BGWASH = 'rgba(255,255,255,0.8)'; +var DISABLED_WASH = 'rgba(255,255,255,0.25)'; + +var TEXT_INPUT_REF = 'urlInput'; +var WEBVIEW_REF = 'webview'; +var DEFAULT_URL = 'https://m.facebook.com'; + +class WebViewExample extends React.Component { + state = { + url: DEFAULT_URL, + status: 'No Page Loaded', + backButtonEnabled: false, + forwardButtonEnabled: false, + loading: true, + scalesPageToFit: true, + }; + + inputText = ''; + + handleTextInputChange = (event) => { + var url = event.nativeEvent.text; + if (!/^[a-zA-Z-_]+:/.test(url)) { + url = 'http://' + url; + } + this.inputText = url; + }; + + render() { + this.inputText = this.state.url; + + return ( + + + + + {'<'} + + + + + {'>'} + + + + + + + Go! + + + + + + + {this.state.status} + + + ); + } + + goBack = () => { + this.refs[WEBVIEW_REF].goBack(); + }; + + goForward = () => { + this.refs[WEBVIEW_REF].goForward(); + }; + + reload = () => { + this.refs[WEBVIEW_REF].reload(); + }; + + onShouldStartLoadWithRequest = (event) => { + // Implement any custom loading logic here, don't forget to return! + return true; + }; + + onNavigationStateChange = (navState) => { + this.setState({ + backButtonEnabled: navState.canGoBack, + forwardButtonEnabled: navState.canGoForward, + url: navState.url, + status: navState.title, + loading: navState.loading, + scalesPageToFit: true + }); + }; + + onSubmitEditing = (event) => { + this.pressGoButton(); + }; + + pressGoButton = () => { + var url = this.inputText.toLowerCase(); + if (url === this.state.url) { + this.reload(); + } else { + this.setState({ + url: url, + }); + } + // dismiss keyboard + this.refs[TEXT_INPUT_REF].blur(); + }; +} + +class Button extends React.Component { + _handlePress = () => { + if (this.props.enabled !== false && this.props.onPress) { + this.props.onPress(); + } + }; + + render() { + return ( + + + {this.props.text} + + + ); + } +} + +class ScaledWebView extends React.Component { + state = { + scalingEnabled: true, + }; + + render() { + return ( + + + + { this.state.scalingEnabled ? +