diff --git a/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
index 1ad2a567e..2190ae714 100644
--- a/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
+++ b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
@@ -13,6 +13,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
+import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -187,6 +188,7 @@ protected WebView createViewInstance(ThemedReactContext reactContext) {
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
settings.setDomStorageEnabled(true);
+ settings.setSupportMultipleWindows(true);
settings.setAllowFileAccess(false);
settings.setAllowContentAccess(false);
@@ -252,6 +254,11 @@ public void setJavaScriptEnabled(WebView view, boolean enabled) {
view.getSettings().setJavaScriptEnabled(enabled);
}
+ @ReactProp(name = "setSupportMultipleWindows")
+ public void setSupportMultipleWindows(WebView view, boolean enabled){
+ view.getSettings().setSupportMultipleWindows(enabled);
+ }
+
@ReactProp(name = "showsHorizontalScrollIndicator")
public void setShowsHorizontalScrollIndicator(WebView view, boolean enabled) {
view.setHorizontalScrollBarEnabled(enabled);
@@ -875,7 +882,7 @@ public void onReceivedSslError(final WebView webView, final SslErrorHandler hand
// This is desired behavior. We later use these values to determine whether the request is a top-level navigation or a subresource request.
String topWindowUrl = webView.getUrl();
String failingUrl = error.getUrl();
-
+
// Cancel request after obtaining top-level URL.
// If request is cancelled before obtaining top-level URL, undesired behavior may occur.
// Undesired behavior: Return value of WebView.getUrl() may be the current URL instead of the failing URL.
@@ -1073,6 +1080,17 @@ public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
this.mWebView = webView;
}
+ @Override
+ public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
+
+ final WebView newWebView = new WebView(view.getContext());
+ final WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
+ transport.setWebView(newWebView);
+ resultMsg.sendToTarget();
+
+ return true;
+ }
+
@Override
public boolean onConsoleMessage(ConsoleMessage message) {
if (ReactBuildConfig.DEBUG) {
diff --git a/docs/Reference.md b/docs/Reference.md
index f3ac6c5e3..bcd1dc3e3 100644
--- a/docs/Reference.md
+++ b/docs/Reference.md
@@ -72,6 +72,7 @@ This document lays out the current public properties and methods for the React N
- [`ignoreSilentHardwareSwitch`](Reference.md#ignoreSilentHardwareSwitch)
- [`onFileDownload`](Reference.md#onFileDownload)
- [`autoManageStatusBarEnabled`](Reference.md#autoManageStatusBarEnabled)
+- [`setSupportMultipleWindows`](Reference.md#setSupportMultipleWindows)
## Methods Index
@@ -782,7 +783,7 @@ A Boolean value indicating whether JavaScript can open windows without user inte
### `androidHardwareAccelerationDisabled`[⬆](#props-index)
-**Deprecated.** Use the `androidLayerType` prop instead.
+**Deprecated.** Use the `androidLayerType` prop instead.
| Type | Required | Platform |
| ---- | -------- | -------- |
@@ -792,7 +793,7 @@ A Boolean value indicating whether JavaScript can open windows without user inte
### `androidLayerType`[⬆](#props-index)
-Specifies the layer type.
+Specifies the layer type.
Possible values for `androidLayerType` are:
@@ -1282,6 +1283,21 @@ Example:
```
+### `setSupportMultipleWindows`
+
+Sets whether the WebView supports multiple windows. See [Android documentation]('https://developer.android.com/reference/android/webkit/WebSettings#setSupportMultipleWindows(boolean)') for more information.
+Setting this to false can expose the application to this [vulnerability](https://alesandroortiz.com/articles/uxss-android-webview-cve-2020-6506/) allowing a malicious iframe to escape into the top layer DOM.
+
+| Type | Required | Default | Platform |
+| ------- | -------- | ------- | -------- |
+| boolean | No | true | Android |
+
+Example:
+
+```javascript
+
+```
+
## Methods
### `extraNativeComponentConfig()`[⬆](#methods-index)
diff --git a/example/App.tsx b/example/App.tsx
index d269439d6..b2d563465 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -18,6 +18,7 @@ import Uploads from './examples/Uploads';
import Injection from './examples/Injection';
import LocalPageLoad from './examples/LocalPageLoad';
import Messaging from './examples/Messaging';
+import NativeWebpage from './examples/NativeWebpage';
const TESTS = {
Messaging: {
@@ -84,6 +85,14 @@ const TESTS = {
return ;
},
},
+ NativeWebpage: {
+ title: 'NativeWebpage',
+ testId: 'NativeWebpage',
+ description: 'Test to open a new webview with a link',
+ render() {
+ return ;
+ },
+ },
};
type Props = {};
@@ -166,6 +175,11 @@ export default class App extends Component {
title="Messaging"
onPress={() => this._changeTest('Messaging')}
/>
+