Merge upstream
This commit is contained in:
commit
f1cbe918a4
|
@ -83,6 +83,27 @@
|
|||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jordansexton",
|
||||
"name": "Jordan Sexton",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/1173161?v=4",
|
||||
"profile": "https://stylisted.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "MalcolmScruggs",
|
||||
"name": "Malcolm Scruggs",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/22333355?v=4",
|
||||
"profile": "https://github.com/MalcolmScruggs",
|
||||
"contributions": [
|
||||
"code",
|
||||
"tool",
|
||||
"test"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# React Native WebView - a Modern, Cross-Platform WebView for React Native
|
||||
[![star this repo](http://githubbadges.com/star.svg?user=react-native-community&repo=react-native-webview&style=flat)](https://github.com/react-native-community/react-native-webview) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors) [![Known Vulnerabilities](https://snyk.io/test/github/react-native-community/react-native-webview/badge.svg?style=flat-square)](https://snyk.io/test/github/react-native-community/react-native-webview)
|
||||
[![star this repo](http://githubbadges.com/star.svg?user=react-native-community&repo=react-native-webview&style=flat)](https://github.com/react-native-community/react-native-webview) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors) [![Known Vulnerabilities](https://snyk.io/test/github/react-native-community/react-native-webview/badge.svg?style=flat-square)](https://snyk.io/test/github/react-native-community/react-native-webview)
|
||||
|
||||
**React Native WebView** is a modern, well-supported, and cross-platform WebView for React Native. It is intended to be a replacement for the built-in WebView (which will be [removed from core](https://github.com/react-native-community/discussions-and-proposals/pull/3)).
|
||||
|
||||
|
@ -84,8 +84,9 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/all-contri
|
|||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore -->
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/6181446?v=4" width="100px;" alt="Thibault Malbranche"/><br /><sub><b>Thibault Malbranche</b></sub>](https://twitter.com/titozzz)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=titozzz "Code") [🤔](#ideas-titozzz "Ideas, Planning, & Feedback") [👀](#review-titozzz "Reviewed Pull Requests") [📖](https://github.com/react-native-community/react-native-webview/commits?author=titozzz "Documentation") [🚧](#maintenance-titozzz "Maintenance") [⚠️](https://github.com/react-native-community/react-native-webview/commits?author=titozzz "Tests") [🚇](#infra-titozzz "Infrastructure (Hosting, Build-Tools, etc)") [💬](#question-titozzz "Answering Questions") | [<img src="https://avatars3.githubusercontent.com/u/1479215?v=4" width="100px;" alt="Jamon Holmgren"/><br /><sub><b>Jamon Holmgren</b></sub>](https://jamonholmgren.com)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=jamonholmgren "Code") [🤔](#ideas-jamonholmgren "Ideas, Planning, & Feedback") [👀](#review-jamonholmgren "Reviewed Pull Requests") [📖](https://github.com/react-native-community/react-native-webview/commits?author=jamonholmgren "Documentation") [🚧](#maintenance-jamonholmgren "Maintenance") [⚠️](https://github.com/react-native-community/react-native-webview/commits?author=jamonholmgren "Tests") [💡](#example-jamonholmgren "Examples") [💬](#question-jamonholmgren "Answering Questions") | [<img src="https://avatars1.githubusercontent.com/u/2570562?v=4" width="100px;" alt="Andrei Pfeiffer"/><br /><sub><b>Andrei Pfeiffer</b></sub>](https://github.com/andreipfeiffer)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=andreipfeiffer "Code") [👀](#review-andreipfeiffer "Reviewed Pull Requests") [🤔](#ideas-andreipfeiffer "Ideas, Planning, & Feedback") | [<img src="https://avatars0.githubusercontent.com/u/5347038?v=4" width="100px;" alt="Michael Diarmid"/><br /><sub><b>Michael Diarmid</b></sub>](https://twitter.com/mikediarmid)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=Salakar "Code") [👀](#review-Salakar "Reviewed Pull Requests") [🤔](#ideas-Salakar "Ideas, Planning, & Feedback") [🔧](#tool-Salakar "Tools") | [<img src="https://avatars3.githubusercontent.com/u/932981?v=4" width="100px;" alt="Scott Mathson"/><br /><sub><b>Scott Mathson</b></sub>](http://smathson.github.io)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=smathson "Code") [📖](https://github.com/react-native-community/react-native-webview/commits?author=smathson "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/8221990?v=4" width="100px;" alt="Margaret"/><br /><sub><b>Margaret</b></sub>](https://github.com/YangXiaomei)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=YangXiaomei "Code") [📖](https://github.com/react-native-community/react-native-webview/commits?author=YangXiaomei "Documentation") |
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/6181446?v=4" width="100px;" alt="Thibault Malbranche"/><br /><sub><b>Thibault Malbranche</b></sub>](https://twitter.com/titozzz)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=titozzz "Code") [🤔](#ideas-titozzz "Ideas, Planning, & Feedback") [👀](#review-titozzz "Reviewed Pull Requests") [📖](https://github.com/react-native-community/react-native-webview/commits?author=titozzz "Documentation") [🚧](#maintenance-titozzz "Maintenance") [⚠️](https://github.com/react-native-community/react-native-webview/commits?author=titozzz "Tests") [🚇](#infra-titozzz "Infrastructure (Hosting, Build-Tools, etc)") [💬](#question-titozzz "Answering Questions") | [<img src="https://avatars3.githubusercontent.com/u/1479215?v=4" width="100px;" alt="Jamon Holmgren"/><br /><sub><b>Jamon Holmgren</b></sub>](https://jamonholmgren.com)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=jamonholmgren "Code") [🤔](#ideas-jamonholmgren "Ideas, Planning, & Feedback") [👀](#review-jamonholmgren "Reviewed Pull Requests") [📖](https://github.com/react-native-community/react-native-webview/commits?author=jamonholmgren "Documentation") [🚧](#maintenance-jamonholmgren "Maintenance") [⚠️](https://github.com/react-native-community/react-native-webview/commits?author=jamonholmgren "Tests") [💡](#example-jamonholmgren "Examples") [💬](#question-jamonholmgren "Answering Questions") | [<img src="https://avatars1.githubusercontent.com/u/2570562?v=4" width="100px;" alt="Andrei Pfeiffer"/><br /><sub><b>Andrei Pfeiffer</b></sub>](https://github.com/andreipfeiffer)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=andreipfeiffer "Code") [👀](#review-andreipfeiffer "Reviewed Pull Requests") [🤔](#ideas-andreipfeiffer "Ideas, Planning, & Feedback") | [<img src="https://avatars0.githubusercontent.com/u/5347038?v=4" width="100px;" alt="Michael Diarmid"/><br /><sub><b>Michael Diarmid</b></sub>](https://twitter.com/mikediarmid)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=Salakar "Code") [👀](#review-Salakar "Reviewed Pull Requests") [🤔](#ideas-Salakar "Ideas, Planning, & Feedback") [🔧](#tool-Salakar "Tools") | [<img src="https://avatars3.githubusercontent.com/u/932981?v=4" width="100px;" alt="Scott Mathson"/><br /><sub><b>Scott Mathson</b></sub>](http://smathson.github.io)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=smathson "Code") [📖](https://github.com/react-native-community/react-native-webview/commits?author=smathson "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/8221990?v=4" width="100px;" alt="Margaret"/><br /><sub><b>Margaret</b></sub>](https://github.com/YangXiaomei)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=YangXiaomei "Code") [📖](https://github.com/react-native-community/react-native-webview/commits?author=YangXiaomei "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/1173161?v=4" width="100px;" alt="Jordan Sexton"/><br /><sub><b>Jordan Sexton</b></sub>](https://stylisted.com)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=jordansexton "Code") [📖](https://github.com/react-native-community/react-native-webview/commits?author=jordansexton "Documentation") |
|
||||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
||||
| [<img src="https://avatars1.githubusercontent.com/u/22333355?v=4" width="100px;" alt="Malcolm Scruggs"/><br /><sub><b>Malcolm Scruggs</b></sub>](https://github.com/MalcolmScruggs)<br />[💻](https://github.com/react-native-community/react-native-webview/commits?author=MalcolmScruggs "Code") [🔧](#tool-MalcolmScruggs "Tools") [⚠️](https://github.com/react-native-community/react-native-webview/commits?author=MalcolmScruggs "Tests") |
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
buildscript {
|
||||
//Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
||||
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['ReactNativeWebview_kotlinVersion']
|
||||
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['ReactNativeWebView_kotlinVersion']
|
||||
|
||||
repositories {
|
||||
google()
|
||||
|
@ -15,11 +15,11 @@ buildscript {
|
|||
}
|
||||
|
||||
def getExtOrDefault(name) {
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeWebview_' + name]
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeWebView_' + name]
|
||||
}
|
||||
|
||||
def getExtOrIntegerDefault(name) {
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativeWebview_' + name]).toInteger()
|
||||
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['ReactNativeWebView_' + name]).toInteger()
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
ReactNativeWebview_kotlinVersion=1.3.11
|
||||
ReactNativeWebview_compileSdkVersion=28
|
||||
ReactNativeWebview_buildToolsVersion=28.0.3
|
||||
ReactNativeWebview_targetSdkVersion=28
|
||||
ReactNativeWebview_supportLibVersion=28.0.0
|
||||
ReactNativeWebView_kotlinVersion=1.3.11
|
||||
ReactNativeWebView_compileSdkVersion=28
|
||||
ReactNativeWebView_buildToolsVersion=28.0.3
|
||||
ReactNativeWebView_targetSdkVersion=28
|
||||
ReactNativeWebView_supportLibVersion=28.0.0
|
|
@ -109,7 +109,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
|
||||
protected static final String HTML_ENCODING = "UTF-8";
|
||||
protected static final String HTML_MIME_TYPE = "text/html";
|
||||
protected static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE";
|
||||
protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
|
||||
|
||||
protected static final String HTTP_METHOD_POST = "POST";
|
||||
|
||||
|
@ -138,8 +138,9 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
|
||||
if (!mLastLoadFailed) {
|
||||
RNCWebView reactWebView = (RNCWebView) webView;
|
||||
|
||||
reactWebView.callInjectedJavaScript();
|
||||
reactWebView.linkBridge();
|
||||
|
||||
emitFinishEvent(webView, url);
|
||||
}
|
||||
}
|
||||
|
@ -239,6 +240,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
mContext = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called whenever JavaScript running within the web view calls:
|
||||
* - window[JAVASCRIPT_INTERFACE].postMessage
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void postMessage(String message) {
|
||||
mContext.onMessage(message);
|
||||
|
@ -312,11 +317,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
}
|
||||
|
||||
messagingEnabled = enabled;
|
||||
|
||||
if (enabled) {
|
||||
addJavascriptInterface(createRNCWebViewBridge(this), BRIDGE_NAME);
|
||||
linkBridge();
|
||||
addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
|
||||
} else {
|
||||
removeJavascriptInterface(BRIDGE_NAME);
|
||||
removeJavascriptInterface(JAVASCRIPT_INTERFACE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,30 +347,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
|
|||
}
|
||||
}
|
||||
|
||||
public void linkBridge() {
|
||||
if (messagingEnabled) {
|
||||
if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// See isNative in lodash
|
||||
String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
|
||||
evaluateJavascript(testPostMessageNative, new ValueCallback<String>() {
|
||||
@Override
|
||||
public void onReceiveValue(String value) {
|
||||
if (value.equals("true")) {
|
||||
FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
evaluateJavascriptWithFallback("(" +
|
||||
"window.originalPostMessage = window.postMessage," +
|
||||
"window.postMessage = function(data) {" +
|
||||
BRIDGE_NAME + ".postMessage(String(data));" +
|
||||
"}" +
|
||||
")");
|
||||
}
|
||||
}
|
||||
|
||||
public void onMessage(String message) {
|
||||
dispatchEvent(this, new TopMessageEvent(this.getId(), message));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
module.exports = function (api) {
|
||||
api && api.cache(false);
|
||||
return {
|
||||
env: {
|
||||
test: {
|
||||
presets: [
|
||||
"module:metro-react-native-babel-preset"
|
||||
],
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -49,6 +49,7 @@ $ yarn add ../react-native-webview && react-native link react-native-webview
|
|||
- After pulling this repo and installing all dependencies, you can run flow on iOS and Android-specific files using the commands:
|
||||
- `yarn test:ios:flow` for iOS
|
||||
- `yarn test:android:flow` for Android
|
||||
- You can run Jest tests using the command: `yarn test:js`
|
||||
- If you want to add another React Native platform to this repository, you will need to create another `.flowconfig` for it. If your platform is `example`, copy the main flowconfig and rename it to `.flowconfig.example`. Then edit the config to ignore other platforms, and add `.*/*[.]example.js` to the ignore lists of the other platforms. Then add an entry to `package.json` like this:
|
||||
- `"test:example:flow": "flow check --flowconfig-name .flowconfig.example"`
|
||||
- Currently you need to install React Native 0.57 to be able to test these types - `flow check` will not pass against 0.56.
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
This document lays out the current public properties and methods for the React Native WebView.
|
||||
|
||||
> **Security Warning:** Currently, `onMessage` and `postMessage` do not allow specifying an origin. This can lead to cross-site scripting attacks if an unexpected document is loaded within a `WebView` instance. Please refer to the MDN documentation for [`Window.postMessage()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) for more details on the security implications of this.
|
||||
|
||||
## Props Index
|
||||
|
||||
- [`source`](Reference.md#source)
|
||||
|
@ -196,9 +194,9 @@ Function that is invoked when the `WebView` is loading.
|
|||
|
||||
### `onMessage`
|
||||
|
||||
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`.
|
||||
Function that is invoked when the webview calls `window.ReactNativeWebView.postMessage`. Setting this property will inject this global into your webview.
|
||||
|
||||
`window.postMessage` accepts one argument, `data`, which will be available on the event object, `event.nativeEvent.data`. `data` must be a string.
|
||||
`window.ReactNativeWebView.postMessage` accepts one argument, `data`, which will be available on the event object, `event.nativeEvent.data`. `data` must be a string.
|
||||
|
||||
| Type | Required |
|
||||
| -------- | -------- |
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
NSString *const RNCJSNavigationScheme = @"react-js-navigation";
|
||||
|
||||
static NSString *const kPostMessageHost = @"postMessage";
|
||||
static NSString *const MessageHandlerName = @"ReactNativeWebView";
|
||||
|
||||
@interface RNCUIWebView () <UIWebViewDelegate, RCTAutoInsetsProtocol>
|
||||
|
||||
|
@ -86,7 +86,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
@"data": message,
|
||||
};
|
||||
NSString *source = [NSString
|
||||
stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));",
|
||||
stringWithFormat:@"window.dispatchEvent(new MessageEvent('message', %@));",
|
||||
RCTJSONStringify(eventInitDict, NULL)
|
||||
];
|
||||
[_webView stringByEvaluatingJavaScriptFromString:source];
|
||||
|
@ -236,7 +236,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
}
|
||||
}
|
||||
|
||||
if (isJSNavigation && [request.URL.host isEqualToString:kPostMessageHost]) {
|
||||
if (isJSNavigation && [request.URL.host isEqualToString:MessageHandlerName]) {
|
||||
NSString *data = request.URL.query;
|
||||
data = [data stringByReplacingOccurrencesOfString:@"+" withString:@" "];
|
||||
data = [data stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
@ -246,7 +246,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
@"data": data,
|
||||
}];
|
||||
|
||||
NSString *source = @"document.dispatchEvent(new MessageEvent('message:received'));";
|
||||
NSString *source = [NSString stringWithFormat:@"window.%@.messageReceived();", MessageHandlerName];
|
||||
|
||||
[_webView stringByEvaluatingJavaScriptFromString:source];
|
||||
|
||||
|
@ -289,40 +289,28 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||
- (void)webViewDidFinishLoad:(UIWebView *)webView
|
||||
{
|
||||
if (_messagingEnabled) {
|
||||
#if RCT_DEV
|
||||
// See isNative in lodash
|
||||
NSString *testPostMessageNative = @"String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
|
||||
BOOL postMessageIsNative = [
|
||||
[webView stringByEvaluatingJavaScriptFromString:testPostMessageNative]
|
||||
isEqualToString:@"true"
|
||||
];
|
||||
if (!postMessageIsNative) {
|
||||
RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
|
||||
}
|
||||
#endif
|
||||
NSString *source = [NSString stringWithFormat:
|
||||
@"(function() {"
|
||||
"window.originalPostMessage = window.postMessage;"
|
||||
" var messageQueue = [];"
|
||||
" var messagePending = false;"
|
||||
|
||||
"var messageQueue = [];"
|
||||
"var messagePending = false;"
|
||||
" function processQueue () {"
|
||||
" if (!messageQueue.length || messagePending) return;"
|
||||
" messagePending = true;"
|
||||
" document.location = '%@://%@?' + encodeURIComponent(messageQueue.shift());"
|
||||
" }"
|
||||
|
||||
"function processQueue() {"
|
||||
"if (!messageQueue.length || messagePending) return;"
|
||||
"messagePending = true;"
|
||||
"window.location = '%@://%@?' + encodeURIComponent(messageQueue.shift());"
|
||||
"}"
|
||||
|
||||
"window.postMessage = function(data) {"
|
||||
"messageQueue.push(String(data));"
|
||||
"processQueue();"
|
||||
"};"
|
||||
|
||||
"document.addEventListener('message:received', function(e) {"
|
||||
"messagePending = false;"
|
||||
"processQueue();"
|
||||
"});"
|
||||
"})();", RNCJSNavigationScheme, kPostMessageHost
|
||||
" window.%@ = {"
|
||||
" postMessage: function (data) {"
|
||||
" messageQueue.push(String(data));"
|
||||
" processQueue();"
|
||||
" },"
|
||||
" messageReceived: function () {"
|
||||
" messagePending = false;"
|
||||
" processQueue();"
|
||||
" }"
|
||||
" };"
|
||||
"})();", RNCJSNavigationScheme, MessageHandlerName, MessageHandlerName
|
||||
];
|
||||
[webView stringByEvaluatingJavaScriptFromString:source];
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#import "objc/runtime.h"
|
||||
|
||||
static NSTimer *keyboardTimer;
|
||||
static NSString *const MessageHanderName = @"ReactNative";
|
||||
static NSString *const MessageHandlerName = @"ReactNativeWebView";
|
||||
|
||||
// runtime trick to remove WKWebView keyboard default toolbar
|
||||
// see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279
|
||||
|
@ -88,6 +88,17 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* See https://stackoverflow.com/questions/25713069/why-is-wkwebview-not-opening-links-with-target-blank/25853806#25853806 for details.
|
||||
*/
|
||||
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
|
||||
{
|
||||
if (!navigationAction.targetFrame.isMainFrame) {
|
||||
[webView loadRequest:navigationAction.request];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
if (self.window != nil && _webView == nil) {
|
||||
|
@ -105,7 +116,22 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
wkWebViewConfig.processPool = [[RNCWKProcessPoolManager sharedManager] sharedProcessPool];
|
||||
}
|
||||
wkWebViewConfig.userContentController = [WKUserContentController new];
|
||||
[wkWebViewConfig.userContentController addScriptMessageHandler: self name: MessageHanderName];
|
||||
|
||||
if (_messagingEnabled) {
|
||||
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:MessageHandlerName];
|
||||
|
||||
NSString *source = [NSString stringWithFormat:
|
||||
@"window.%@ = {"
|
||||
" postMessage: function (data) {"
|
||||
" window.webkit.messageHandlers.%@.postMessage(String(data));"
|
||||
" }"
|
||||
"};", MessageHandlerName, MessageHandlerName
|
||||
];
|
||||
|
||||
WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
|
||||
[wkWebViewConfig.userContentController addUserScript:script];
|
||||
}
|
||||
|
||||
wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
|
||||
#if WEBKIT_IOS_10_APIS_AVAILABLE
|
||||
wkWebViewConfig.mediaTypesRequiringUserActionForPlayback = _mediaPlaybackRequiresUserAction
|
||||
|
@ -152,7 +178,7 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
- (void)removeFromSuperview
|
||||
{
|
||||
if (_webView) {
|
||||
[_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHanderName];
|
||||
[_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
|
||||
[_webView removeObserver:self forKeyPath:@"estimatedProgress"];
|
||||
[_webView removeFromSuperview];
|
||||
_webView = nil;
|
||||
|
@ -215,7 +241,7 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
|
||||
/**
|
||||
* This method is called whenever JavaScript running within the web view calls:
|
||||
* - window.webkit.messageHandlers.[MessageHanderName].postMessage
|
||||
* - window.webkit.messageHandlers[MessageHandlerName].postMessage
|
||||
*/
|
||||
- (void)userContentController:(WKUserContentController *)userContentController
|
||||
didReceiveScriptMessage:(WKScriptMessage *)message
|
||||
|
@ -284,7 +310,6 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
|
||||
-(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
|
||||
{
|
||||
|
||||
if (_webView == nil) {
|
||||
_savedHideKeyboardAccessoryView = hideKeyboardAccessoryView;
|
||||
return;
|
||||
|
@ -295,6 +320,7 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
}
|
||||
|
||||
UIView* subview;
|
||||
|
||||
for (UIView* view in _webView.scrollView.subviews) {
|
||||
if([[view.class description] hasPrefix:@"WK"])
|
||||
subview = view;
|
||||
|
@ -334,10 +360,10 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
{
|
||||
NSDictionary *eventInitDict = @{@"data": message};
|
||||
NSString *source = [NSString
|
||||
stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));",
|
||||
stringWithFormat:@"window.dispatchEvent(new MessageEvent('message', %@));",
|
||||
RCTJSONStringify(eventInitDict, NULL)
|
||||
];
|
||||
[self evaluateJS: source thenCall: nil];
|
||||
[self injectJavaScript: source];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
|
@ -551,7 +577,6 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
}];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the navigation is complete.
|
||||
* @see https://fburl.com/rtys6jlb
|
||||
|
@ -559,35 +584,11 @@ static NSString *const MessageHanderName = @"ReactNative";
|
|||
- (void) webView:(WKWebView *)webView
|
||||
didFinishNavigation:(WKNavigation *)navigation
|
||||
{
|
||||
if (_messagingEnabled) {
|
||||
#if RCT_DEV
|
||||
|
||||
// Implementation inspired by Lodash.isNative.
|
||||
NSString *isPostMessageNative = @"String(String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage'))";
|
||||
[self evaluateJS: isPostMessageNative thenCall: ^(NSString *result) {
|
||||
if (! [result isEqualToString:@"true"]) {
|
||||
RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
|
||||
}
|
||||
}];
|
||||
#endif
|
||||
|
||||
NSString *source = [NSString stringWithFormat:
|
||||
@"(function() {"
|
||||
"window.originalPostMessage = window.postMessage;"
|
||||
|
||||
"window.postMessage = function(data) {"
|
||||
"window.webkit.messageHandlers.%@.postMessage(String(data));"
|
||||
"};"
|
||||
"})();",
|
||||
MessageHanderName
|
||||
];
|
||||
[self evaluateJS: source thenCall: nil];
|
||||
}
|
||||
|
||||
if (_injectedJavaScript) {
|
||||
[self evaluateJS: _injectedJavaScript thenCall: ^(NSString *jsEvaluationValue) {
|
||||
NSMutableDictionary *event = [self baseEvent];
|
||||
event[@"jsEvaluationValue"] = jsEvaluationValue;
|
||||
|
||||
if (self.onLoadingFinish) {
|
||||
self.onLoadingFinish(event);
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
// !$*UTF8*$!
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:RNCWebView.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,185 @@
|
|||
// For a detailed explanation regarding each configuration property, visit:
|
||||
// https://jestjs.io/docs/en/configuration.html
|
||||
|
||||
module.exports = {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// Respect "browser" field in package.json when resolving modules
|
||||
// browser: false,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/private/var/folders/8f/kgcy219d1dvfvbcqky441_d00000gp/T/jest_dy",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
// collectCoverage: false,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: null,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
// coverageDirectory: "coverage",
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: null,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: null,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files usin a array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: null,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: null,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "json",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
preset: "react-native",
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: null,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: null,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: null,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
// roots: [
|
||||
// "<rootDir>"
|
||||
// ],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: "node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
// testMatch: [
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: null,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jasmine2",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: null,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: null,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
|
@ -157,9 +157,9 @@ class WebView extends React.Component<WebViewSharedProps, State> {
|
|||
}
|
||||
thirdPartyCookiesEnabled={this.props.thirdPartyCookiesEnabled}
|
||||
domStorageEnabled={this.props.domStorageEnabled}
|
||||
messagingEnabled={typeof this.props.onMessage === 'function'}
|
||||
cacheEnabled={this.props.cacheEnabled}
|
||||
onMessage={this.onMessage}
|
||||
messagingEnabled={typeof this.props.onMessage === 'function'}
|
||||
overScrollMode={this.props.overScrollMode}
|
||||
contentInset={this.props.contentInset}
|
||||
automaticallyAdjustContentInsets={
|
||||
|
|
|
@ -232,8 +232,6 @@ class WebView extends React.Component<WebViewSharedProps, State> {
|
|||
source = { uri: this.props.url };
|
||||
}
|
||||
|
||||
const messagingEnabled = typeof this.props.onMessage === 'function';
|
||||
|
||||
let NativeWebView = nativeConfig.component;
|
||||
|
||||
if (this.props.useWebKit) {
|
||||
|
@ -268,8 +266,8 @@ class WebView extends React.Component<WebViewSharedProps, State> {
|
|||
onLoadingFinish={this._onLoadingFinish}
|
||||
onLoadingError={this._onLoadingError}
|
||||
onLoadingProgress={this._onLoadingProgress}
|
||||
messagingEnabled={messagingEnabled}
|
||||
onMessage={this._onMessage}
|
||||
messagingEnabled={typeof this.props.onMessage === 'function'}
|
||||
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
|
||||
scalesPageToFit={scalesPageToFit}
|
||||
allowsInlineMediaPlayback={this.props.allowsInlineMediaPlayback}
|
||||
|
|
|
@ -19,12 +19,12 @@ import type {
|
|||
const defaultOriginWhitelist = ['http://*', 'https://*'];
|
||||
|
||||
const extractOrigin = (url: string): string => {
|
||||
const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
|
||||
const result = /^[A-Za-z][A-Za-z0-9\+\-\.]+:(\/\/)?[^/]*/.exec(url);
|
||||
return result === null ? '' : result[0];
|
||||
};
|
||||
|
||||
const originWhitelistToRegex = (originWhitelist: string): string =>
|
||||
escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
|
||||
`^${escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*')}`;
|
||||
|
||||
const passesWhitelist = (compiledWhitelist: Array<string>, url: string) => {
|
||||
const origin = extractOrigin(url);
|
||||
|
|
|
@ -424,13 +424,11 @@ export type WebViewSharedProps = $ReadOnly<{|
|
|||
onNavigationStateChange?: (event: WebViewNavigation) => mixed,
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
* Function that is invoked when the webview calls `window.ReactNativeWebView.postMessage`.
|
||||
* Setting this property will inject this global into your webview.
|
||||
*
|
||||
* `window.postMessage` accepts one argument, `data`, which will be
|
||||
* available on the event object, `event.nativeEvent.data`. `data`
|
||||
* must be a string.
|
||||
* `window.ReactNativeWebView.postMessage` accepts one argument, `data`, which will be
|
||||
* available on the event object, `event.nativeEvent.data`. `data` must be a string.
|
||||
*/
|
||||
onMessage?: (event: WebViewMessageEvent) => mixed,
|
||||
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
import { Linking } from 'react-native';
|
||||
|
||||
import {
|
||||
defaultOriginWhitelist,
|
||||
createOnShouldStartLoadWithRequest,
|
||||
} from '../WebViewShared';
|
||||
|
||||
describe('WebViewShared', () => {
|
||||
test('exports defaultOriginWhitelist', () => {
|
||||
expect(defaultOriginWhitelist).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('createOnShouldStartLoadWithRequest', () => {
|
||||
const alwaysTrueOnShouldStartLoadWithRequest = (nativeEvent) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const alwaysFalseOnShouldStartLoadWithRequest = (nativeEvent) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const loadRequest = jest.fn();
|
||||
|
||||
test('loadRequest is called without onShouldStartLoadWithRequest override', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
defaultOriginWhitelist,
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'https://www.example.com/', lockIdentifier: 1 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenCalledWith(true, 'https://www.example.com/', 1);
|
||||
});
|
||||
|
||||
test('Linking.openURL is called without onShouldStartLoadWithRequest override', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
defaultOriginWhitelist,
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'invalid://example.com/', lockIdentifier: 2 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledWith('invalid://example.com/');
|
||||
expect(loadRequest).toHaveBeenCalledWith(false, 'invalid://example.com/', 2);
|
||||
});
|
||||
|
||||
test('loadRequest with true onShouldStartLoadWithRequest override is called', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
defaultOriginWhitelist,
|
||||
alwaysTrueOnShouldStartLoadWithRequest,
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'https://www.example.com/', lockIdentifier: 1 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(true, 'https://www.example.com/', 1);
|
||||
});
|
||||
|
||||
test('Linking.openURL with true onShouldStartLoadWithRequest override is called for links not passing the whitelist', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
defaultOriginWhitelist,
|
||||
alwaysTrueOnShouldStartLoadWithRequest,
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'invalid://example.com/', lockIdentifier: 1 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('invalid://example.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(true, 'invalid://example.com/', 1);
|
||||
});
|
||||
|
||||
test('loadRequest with false onShouldStartLoadWithRequest override is called', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
defaultOriginWhitelist,
|
||||
alwaysFalseOnShouldStartLoadWithRequest,
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'https://www.example.com/', lockIdentifier: 1 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, 'https://www.example.com/', 1);
|
||||
});
|
||||
|
||||
test('loadRequest with limited whitelist', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
['https://*'],
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'https://www.example.com/', lockIdentifier: 1 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(true, 'https://www.example.com/', 1);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'http://insecure.com/', lockIdentifier: 2 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('http://insecure.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, 'http://insecure.com/', 2);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'git+https://insecure.com/', lockIdentifier: 3 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('git+https://insecure.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, 'git+https://insecure.com/', 3);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'fakehttps://insecure.com/', lockIdentifier: 4 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('fakehttps://insecure.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, 'fakehttps://insecure.com/', 4);
|
||||
});
|
||||
|
||||
test('loadRequest allows for valid URIs', () => {
|
||||
const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
|
||||
loadRequest,
|
||||
['plus+https://*', 'DOT.https://*', 'dash-https://*', '0invalid://*', '+invalid://*'],
|
||||
);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'plus+https://www.example.com/', lockIdentifier: 1 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(true, 'plus+https://www.example.com/', 1);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'DOT.https://www.example.com/', lockIdentifier: 2 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(true, 'DOT.https://www.example.com/', 2);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'dash-https://www.example.com/', lockIdentifier: 3 } });
|
||||
expect(Linking.openURL).toHaveBeenCalledTimes(0);
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(true, 'dash-https://www.example.com/', 3);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: '0invalid://www.example.com/', lockIdentifier: 4 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('0invalid://www.example.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, '0invalid://www.example.com/', 4);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: '+invalid://www.example.com/', lockIdentifier: 5 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('+invalid://www.example.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, '+invalid://www.example.com/', 5);
|
||||
|
||||
onShouldStartLoadWithRequest({ nativeEvent: { url: 'FAKE+plus+https://www.example.com/', lockIdentifier: 6 } });
|
||||
expect(Linking.openURL).toHaveBeenLastCalledWith('FAKE+plus+https://www.example.com/');
|
||||
expect(loadRequest).toHaveBeenLastCalledWith(false, 'FAKE+plus+https://www.example.com/', 6);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`WebViewShared exports defaultOriginWhitelist 1`] = `
|
||||
Array [
|
||||
"http://*",
|
||||
"https://*",
|
||||
]
|
||||
`;
|
10
package.json
10
package.json
|
@ -8,14 +8,16 @@
|
|||
"Thibault Malbranche <malbranche.thibault@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"version": "4.0.0",
|
||||
"version": "5.0.2",
|
||||
"homepage": "https://github.com/react-native-community/react-native-webview#readme",
|
||||
"scripts": {
|
||||
"test:js": "jest",
|
||||
"test:ios:flow": "flow check",
|
||||
"test:android:flow": "flow check --flowconfig-name .flowconfig.android",
|
||||
"ci:publish": "yarn semantic-release",
|
||||
"ci:test": "yarn ci:test:flow",
|
||||
"ci:test": "yarn ci:test:flow && yarn ci:test:js",
|
||||
"ci:test:flow": "yarn test:ios:flow && yarn test:android:flow",
|
||||
"ci:test:js": "yarn test:js",
|
||||
"semantic-release": "semantic-release"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -27,10 +29,14 @@
|
|||
"fbjs": "^0.8.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
"@semantic-release/git": "7.0.5",
|
||||
"@types/react": "^16.4.18",
|
||||
"@types/react-native": "^0.57.6",
|
||||
"babel-jest": "^24.0.0",
|
||||
"flow-bin": "^0.80.0",
|
||||
"jest": "^24.0.0",
|
||||
"metro-react-native-babel-preset": "^0.51.1",
|
||||
"react-native": "^0.57",
|
||||
"semantic-release": "15.10.3"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue