mirror of
https://github.com/status-im/react-native.git
synced 2025-02-25 07:35:25 +00:00
Ported source
prop over to iOS WebView
Summary: public https://github.com/facebook/react-native/pull/5494 added a new `source` property to WebView on Android that provides a better API, as well as allowing for request headers to be set. This diff ports that functionality over to iOS, so we can have a consistent API cross-platform. I've also extended the API to include `method` (GET or POST) and `body` when setting the WebView content with a URI, and `baseUrl` when setting static HTML. Reviewed By: javache Differential Revision: D2884643 fb-gh-sync-id: 83f24494bdbb4e1408aa8f3b7428fee33888ae3a
This commit is contained in:
parent
5ec1d354c2
commit
46106f756a
@ -49,7 +49,11 @@ var WebViewExample = React.createClass({
|
||||
inputText: '',
|
||||
|
||||
handleTextInputChange: function(event) {
|
||||
this.inputText = event.nativeEvent.text;
|
||||
var url = event.nativeEvent.text;
|
||||
if (!/^[a-zA-Z-_]:/.test(url)) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
this.inputText = url;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -93,7 +97,7 @@ var WebViewExample = React.createClass({
|
||||
ref={WEBVIEW_REF}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
style={styles.webView}
|
||||
url={this.state.url}
|
||||
source={{uri: this.state.url}}
|
||||
javaScriptEnabled={true}
|
||||
domStorageEnabled={true}
|
||||
decelerationRate="normal"
|
||||
@ -232,7 +236,26 @@ exports.title = '<WebView>';
|
||||
exports.description = 'Base component to display web content';
|
||||
exports.examples = [
|
||||
{
|
||||
title: 'WebView',
|
||||
title: 'Simple Browser',
|
||||
render(): ReactElement { return <WebViewExample />; }
|
||||
},
|
||||
{
|
||||
title: 'POST Test',
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<WebView
|
||||
style={{
|
||||
backgroundColor: BGWASH,
|
||||
height: 100,
|
||||
}}
|
||||
source={{
|
||||
uri: 'http://www.posttestserver.com/post.php',
|
||||
method: 'POST',
|
||||
body: 'foo=bar&bar=foo'
|
||||
}}
|
||||
scalesPageToFit={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -62,11 +62,43 @@ var WebView = React.createClass({
|
||||
),
|
||||
|
||||
/**
|
||||
* Used on Android only, provides html or url with optional headers to the WebView.
|
||||
* `{ html: string, uri: string, headers: map<string, string> }`
|
||||
* @platform android
|
||||
* Loads static html or a uri (with optional headers) in the WebView.
|
||||
*/
|
||||
source: PropTypes.object,
|
||||
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 on Android only, JS is enabled by default for WebView on iOS
|
||||
@ -149,6 +181,12 @@ var WebView = React.createClass({
|
||||
} 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.');
|
||||
}
|
||||
|
||||
var webView =
|
||||
<RCTWebView
|
||||
|
@ -7,7 +7,7 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule WebView
|
||||
* @flow
|
||||
* @noflow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@ -20,9 +20,10 @@ var UIManager = require('UIManager');
|
||||
var View = require('View');
|
||||
var ScrollView = require('ScrollView')
|
||||
|
||||
var processDecelerationRate = require('processDecelerationRate');
|
||||
var deprecatedPropType = require('deprecatedPropType');
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
var processDecelerationRate = require('processDecelerationRate');
|
||||
var requireNativeComponent = require('requireNativeComponent');
|
||||
|
||||
var PropTypes = React.PropTypes;
|
||||
@ -89,8 +90,56 @@ var WebView = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
...View.propTypes,
|
||||
url: PropTypes.string,
|
||||
html: PropTypes.string,
|
||||
|
||||
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,
|
||||
}),
|
||||
]),
|
||||
|
||||
/**
|
||||
* Function that returns a view to show if there's an error.
|
||||
*/
|
||||
@ -243,13 +292,19 @@ var WebView = React.createClass({
|
||||
|
||||
var decelerationRate = processDecelerationRate(this.props.decelerationRate);
|
||||
|
||||
var source = this.props.source || {};
|
||||
if (this.props.html) {
|
||||
source.html = this.props.html;
|
||||
} else if (this.props.url) {
|
||||
source.uri = this.props.url;
|
||||
}
|
||||
|
||||
var webView =
|
||||
<RCTWebView
|
||||
ref={RCT_WEBVIEW_REF}
|
||||
key="webViewKey"
|
||||
style={webViewStyles}
|
||||
url={this.props.url}
|
||||
html={this.props.html}
|
||||
source={source}
|
||||
injectedJavaScript={this.props.injectedJavaScript}
|
||||
bounces={this.props.bounces}
|
||||
scrollEnabled={this.props.scrollEnabled}
|
||||
|
@ -118,8 +118,31 @@ RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncod
|
||||
|
||||
+ (NSURLRequest *)NSURLRequest:(id)json
|
||||
{
|
||||
NSURL *URL = [self NSURL:json];
|
||||
return URL ? [NSURLRequest requestWithURL:URL] : nil;
|
||||
if ([json isKindOfClass:[NSString class]]) {
|
||||
NSURL *URL = [self NSURL:json];
|
||||
return URL ? [NSURLRequest requestWithURL:URL] : nil;
|
||||
}
|
||||
if ([json isKindOfClass:[NSDictionary class]]) {
|
||||
NSURL *URL = [self NSURL:json[@"uri"] ?: json[@"url"]];
|
||||
if (!URL) {
|
||||
return nil;
|
||||
}
|
||||
NSData *body = [self NSData:json[@"body"]];
|
||||
NSString *method = [self NSString:json[@"method"]].uppercaseString ?: @"GET";
|
||||
NSDictionary *headers = [self NSDictionary:json[@"headers"]];
|
||||
if ([method isEqualToString:@"GET"] && headers == nil && body == nil) {
|
||||
return [NSURLRequest requestWithURL:URL];
|
||||
}
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
|
||||
request.HTTPBody = body;
|
||||
request.HTTPMethod = method;
|
||||
request.allHTTPHeaderFields = headers;
|
||||
return [request copy];
|
||||
}
|
||||
if (json) {
|
||||
RCTLogConvertError(json, @"a valid URLRequest");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (RCTFileURL *)RCTFileURL:(id)json
|
||||
|
@ -31,7 +31,7 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
|
||||
|
||||
@property (nonatomic, weak) id<RCTWebViewDelegate> delegate;
|
||||
|
||||
@property (nonatomic, strong) NSURL *URL;
|
||||
@property (nonatomic, copy) NSDictionary *source;
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||
@property (nonatomic, copy) NSString *injectedJavaScript;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTAutoInsetsProtocol.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
@ -70,31 +71,34 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
[_webView reload];
|
||||
}
|
||||
|
||||
- (NSURL *)URL
|
||||
- (void)setSource:(NSDictionary *)source
|
||||
{
|
||||
return _webView.request.URL;
|
||||
}
|
||||
if (![_source isEqualToDictionary:source]) {
|
||||
_source = [source copy];
|
||||
|
||||
- (void)setURL:(NSURL *)URL
|
||||
{
|
||||
// Because of the way React works, as pages redirect, we actually end up
|
||||
// passing the redirect urls back here, so we ignore them if trying to load
|
||||
// the same url. We'll expose a call to 'reload' to allow a user to load
|
||||
// the existing page.
|
||||
if ([URL isEqual:_webView.request.URL]) {
|
||||
return;
|
||||
}
|
||||
if (!URL) {
|
||||
// Clear the webview
|
||||
[_webView loadHTMLString:@"" baseURL:nil];
|
||||
return;
|
||||
}
|
||||
[_webView loadRequest:[NSURLRequest requestWithURL:URL]];
|
||||
}
|
||||
// Check for a static html source first
|
||||
NSString *html = [RCTConvert NSString:source[@"html"]];
|
||||
if (html) {
|
||||
NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]];
|
||||
[_webView loadHTMLString:html baseURL:baseURL];
|
||||
return;
|
||||
}
|
||||
|
||||
- (void)setHTML:(NSString *)HTML
|
||||
{
|
||||
[_webView loadHTMLString:HTML baseURL:nil];
|
||||
NSURLRequest *request = [RCTConvert NSURLRequest:source];
|
||||
// Because of the way React works, as pages redirect, we actually end up
|
||||
// passing the redirect urls back here, so we ignore them if trying to load
|
||||
// the same url. We'll expose a call to 'reload' to allow a user to load
|
||||
// the existing page.
|
||||
if ([request.URL isEqual:_webView.request.URL]) {
|
||||
return;
|
||||
}
|
||||
if (!request.URL) {
|
||||
// Clear the webview
|
||||
[_webView loadHTMLString:@"" baseURL:nil];
|
||||
return;
|
||||
}
|
||||
[_webView loadRequest:request];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
|
@ -33,8 +33,7 @@ RCT_EXPORT_MODULE()
|
||||
return webView;
|
||||
}
|
||||
|
||||
RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL)
|
||||
RCT_REMAP_VIEW_PROPERTY(html, HTML, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
|
||||
RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(scalesPageToFit, _webView.scalesPageToFit, BOOL)
|
||||
|
@ -35,6 +35,8 @@ import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -68,6 +70,8 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
|
||||
private static final String HTML_ENCODING = "UTF-8";
|
||||
private static final String HTML_MIME_TYPE = "text/html; charset=utf-8";
|
||||
|
||||
private static final String HTTP_METHOD_POST = "POST";
|
||||
|
||||
public static final int COMMAND_GO_BACK = 1;
|
||||
public static final int COMMAND_GO_FORWARD = 2;
|
||||
public static final int COMMAND_RELOAD = 3;
|
||||
@ -135,9 +139,9 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
|
||||
dispatchEvent(
|
||||
webView,
|
||||
new TopLoadingStartEvent(
|
||||
webView.getId(),
|
||||
SystemClock.uptimeMillis(),
|
||||
createWebViewEvent(webView, url)));
|
||||
webView.getId(),
|
||||
SystemClock.uptimeMillis(),
|
||||
createWebViewEvent(webView, url)));
|
||||
}
|
||||
|
||||
private void emitFinishEvent(WebView webView, String url) {
|
||||
@ -208,10 +212,9 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
|
||||
}
|
||||
|
||||
public void callInjectedJavaScript() {
|
||||
if (
|
||||
getSettings().getJavaScriptEnabled() &&
|
||||
injectedJS != null &&
|
||||
!TextUtils.isEmpty(injectedJS)) {
|
||||
if (getSettings().getJavaScriptEnabled() &&
|
||||
injectedJS != null &&
|
||||
!TextUtils.isEmpty(injectedJS)) {
|
||||
loadUrl("javascript:(function() {\n" + injectedJS + ";\n})();");
|
||||
}
|
||||
}
|
||||
@ -278,10 +281,36 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
|
||||
public void setSource(WebView view, @Nullable ReadableMap source) {
|
||||
if (source != null) {
|
||||
if (source.hasKey("html")) {
|
||||
view.loadData(source.getString("html"), HTML_MIME_TYPE, HTML_ENCODING);
|
||||
String html = source.getString("html");
|
||||
if (source.hasKey("baseUrl")) {
|
||||
view.loadDataWithBaseURL(
|
||||
source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null);
|
||||
} else {
|
||||
view.loadData(html, HTML_MIME_TYPE, HTML_ENCODING);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (source.hasKey("uri")) {
|
||||
String url = source.getString("uri");
|
||||
if (source.hasKey("method")) {
|
||||
String method = source.getString("method");
|
||||
if (method.equals(HTTP_METHOD_POST)) {
|
||||
byte[] postData = null;
|
||||
if (source.hasKey("body")) {
|
||||
String body = source.getString("body");
|
||||
try {
|
||||
postData = body.getBytes("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
postData = body.getBytes();
|
||||
}
|
||||
}
|
||||
if (postData == null) {
|
||||
postData = new byte[0];
|
||||
}
|
||||
view.postUrl(url, postData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
HashMap<String, String> headerMap = new HashMap<>();
|
||||
if (source.hasKey("headers")) {
|
||||
ReadableMap headers = source.getMap("headers");
|
||||
@ -291,7 +320,7 @@ public class ReactWebViewManager extends SimpleViewManager<WebView> {
|
||||
headerMap.put(key, headers.getString(key));
|
||||
}
|
||||
}
|
||||
view.loadUrl(source.getString("uri"), headerMap);
|
||||
view.loadUrl(url, headerMap);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user