Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.view.ViewGroup;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.JsResult;
import android.webkit.JsPromptResult;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
Expand Down Expand Up @@ -62,6 +63,7 @@ public class RNCWebChromeClient extends WebChromeClient implements LifecycleEven

// This boolean block JS prompts and alerts from displaying during loading
protected boolean blockJsDuringLoading = true;
protected boolean suppressJavaScriptDialogs = false;

/*
* - Permissions -
Expand Down Expand Up @@ -445,14 +447,32 @@ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathC

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
if (blockJsDuringLoading) {
if (shouldSuppressDialogs()) {
result.cancel();
return true;
} else {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
}

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
if (shouldSuppressDialogs()) {
result.cancel();
return true;
}
return super.onJsAlert(view, url, message, result);
}

@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
if (shouldSuppressDialogs()) {
result.cancel();
return true;
}
return super.onJsConfirm(view, url, message, result);
}

@Override
public void onHostResume() {
if (mVideoView != null && mVideoView.getSystemUiVisibility() != FULLSCREEN_SYSTEM_UI_VISIBILITY) {
Expand Down Expand Up @@ -486,4 +506,12 @@ public void setAllowsProtectedMedia(boolean enabled) {
public void setHasOnOpenWindowEvent(boolean hasEvent) {
mHasOnOpenWindowEvent = hasEvent;
}
}

public void setSuppressJavaScriptDialogs(boolean suppress) {
suppressJavaScriptDialogs = suppress;
}

private boolean shouldSuppressDialogs() {
return suppressJavaScriptDialogs || blockJsDuringLoading;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
protected boolean hasScrollEvent = false;
protected boolean nestedScrollEnabled = false;
protected ProgressChangedFilter progressChangedFilter;
protected boolean allowFileDownloads = true;
protected boolean suppressJavaScriptDialogs = false;

/** Samsung Manufacturer Name */
private static final String SAMSUNG_MANUFACTURER_NAME = "samsung";
Expand Down Expand Up @@ -121,6 +123,22 @@ public void setNestedScrollEnabled(boolean nestedScrollEnabled) {
this.nestedScrollEnabled = nestedScrollEnabled;
}

public void setAllowFileDownloads(boolean allow) {
this.allowFileDownloads = allow;
}

public boolean getAllowFileDownloads() {
return this.allowFileDownloads;
}

public void setSuppressJavaScriptDialogs(boolean suppress) {
this.suppressJavaScriptDialogs = suppress;
}

public boolean getSuppressJavaScriptDialogs() {
return this.suppressJavaScriptDialogs;
}

@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection inputConnection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
webView.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
}
val base64DownloaderRequestFilePermission = { base64: String ->
webView.reactApplicationContext.getNativeModule(RNCWebViewModule::class.java)?.let { module ->
module.setBase64DownloadRequest(base64)
module.grantFileDownloaderPermissions(getDownloadingMessageOrDefault(), getLackPermissionToDownloadMessageOrDefault())
if (webView.allowFileDownloads) {
webView.reactApplicationContext.getNativeModule(RNCWebViewModule::class.java)?.let { module ->
module.setBase64DownloadRequest(base64)
module.grantFileDownloaderPermissions(getDownloadingMessageOrDefault(), getLackPermissionToDownloadMessageOrDefault())
}
}
Unit
}
Expand All @@ -113,6 +115,9 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
requestFilePermission = base64DownloaderRequestFilePermission,
)
webView.setDownloadListener(DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength ->
if (!webView.allowFileDownloads) {
return@DownloadListener
}
if (url.startsWith("data:")) {
Base64FileDownloader.downloadBase64File(
context = context,
Expand Down Expand Up @@ -255,6 +260,7 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
}
webChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
webChromeClient.setHasOnOpenWindowEvent(mHasOnOpenWindowEvent);
webChromeClient.setSuppressJavaScriptDialogs(webView.suppressJavaScriptDialogs);
webView.webChromeClient = webChromeClient
} else {
var webChromeClient = webView.webChromeClient as RNCWebChromeClient?
Expand All @@ -266,6 +272,7 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
}
webChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
webChromeClient.setHasOnOpenWindowEvent(mHasOnOpenWindowEvent);
webChromeClient.setSuppressJavaScriptDialogs(webView.suppressJavaScriptDialogs);
webView.webChromeClient = webChromeClient
}
}
Expand Down Expand Up @@ -673,6 +680,11 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
mLackPermissionToDownloadMessage = value
}

fun setAllowFileDownloads(viewWrapper: RNCWebViewWrapper, value: Boolean) {
val view = viewWrapper.webView
view.allowFileDownloads = value
}

fun setHasOnOpenWindowEvent(viewWrapper: RNCWebViewWrapper, value: Boolean) {
val view = viewWrapper.webView
mHasOnOpenWindowEvent = value
Expand All @@ -698,6 +710,15 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
}
}

fun setSuppressJavaScriptDialogs(viewWrapper: RNCWebViewWrapper, suppress: Boolean) {
val view = viewWrapper.webView
view.suppressJavaScriptDialogs = suppress
val client = view.webChromeClient
if (client is RNCWebChromeClient) {
client.setSuppressJavaScriptDialogs(suppress)
}
}

fun setMenuCustomItems(viewWrapper: RNCWebViewWrapper, value: ReadableArray?) {
val view = viewWrapper.webView
when (value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
package com.reactnativecommunity.webview.extension.file

import android.content.Context
import android.webkit.JavascriptInterface
import com.reactnativecommunity.webview.RNCWebView
import com.reactnativecommunity.webview.extension.file.Base64FileDownloader.downloadBase64File
import java.io.IOException

internal fun RNCWebView.addBlobFileDownloaderJavascriptInterface(downloadingMessage: String, requestFilePermission: (String) -> Unit) {
this.addJavascriptInterface(
BlobFileDownloader(this.context, downloadingMessage, requestFilePermission),
BlobFileDownloader(this, downloadingMessage, requestFilePermission),
BlobFileDownloader.JS_INTERFACE_TAG,
)
}

internal class BlobFileDownloader(
private val context: Context,
private val webView: RNCWebView,
private val downloadingMessage: String,
private val requestFilePermission: (String) -> Unit,
) {

@JavascriptInterface
@Throws(IOException::class)
fun getBase64FromBlobData(base64: String) {
downloadBase64File(context, base64, downloadingMessage, requestFilePermission)
if (!webView.allowFileDownloads) {
return
}
downloadBase64File(webView.context, base64, downloadingMessage, requestFilePermission)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,24 @@ public void setLackPermissionToDownloadMessage(RNCWebViewWrapper view, @Nullable
mRNCWebViewManagerImpl.setLackPermissionToDownloadMessage(value);
}

@Override
@ReactProp(name = "allowFileDownloads", defaultBoolean = true)
public void setAllowFileDownloads(RNCWebViewWrapper view, boolean value) {
mRNCWebViewManagerImpl.setAllowFileDownloads(view, value);
}

@Override
@ReactProp(name = "hasOnOpenWindowEvent")
public void setHasOnOpenWindowEvent(RNCWebViewWrapper view, boolean hasEvent) {
mRNCWebViewManagerImpl.setHasOnOpenWindowEvent(view, hasEvent);
}

@Override
@ReactProp(name = "suppressJavaScriptDialogs")
public void setSuppressJavaScriptDialogs(RNCWebViewWrapper view, boolean suppress) {
mRNCWebViewManagerImpl.setSuppressJavaScriptDialogs(view, suppress);
}

@Override
@ReactProp(name = "mediaPlaybackRequiresUserAction")
public void setMediaPlaybackRequiresUserAction(RNCWebViewWrapper view, boolean value) {
Expand Down Expand Up @@ -556,4 +568,4 @@ public void onDropViewInstance(@NonNull RNCWebViewWrapper view) {
mRNCWebViewManagerImpl.onDropViewInstance(view);
super.onDropViewInstance(view);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,21 @@ public void setLackPermissionToDownloadMessage(RNCWebViewWrapper view, @Nullable
mRNCWebViewManagerImpl.setLackPermissionToDownloadMessage(value);
}

@ReactProp(name = "allowFileDownloads", defaultBoolean = true)
public void setAllowFileDownloads(RNCWebViewWrapper view, boolean value) {
mRNCWebViewManagerImpl.setAllowFileDownloads(view, value);
}

@ReactProp(name = "hasOnOpenWindowEvent")
public void setHasOnOpenWindowEvent(RNCWebViewWrapper view, boolean hasEvent) {
mRNCWebViewManagerImpl.setHasOnOpenWindowEvent(view, hasEvent);
}

@ReactProp(name = "suppressJavaScriptDialogs")
public void setSuppressJavaScriptDialogs(RNCWebViewWrapper view, boolean suppress) {
mRNCWebViewManagerImpl.setSuppressJavaScriptDialogs(view, suppress);
}

@ReactProp(name = "mediaPlaybackRequiresUserAction")
public void setMediaPlaybackRequiresUserAction(RNCWebViewWrapper view, boolean value) {
mRNCWebViewManagerImpl.setMediaPlaybackRequiresUserAction(view, value);
Expand Down Expand Up @@ -330,4 +340,4 @@ public void onDropViewInstance(@NonNull RNCWebViewWrapper view) {
mRNCWebViewManagerImpl.onDropViewInstance(view);
super.onDropViewInstance(view);
}
}
}
1 change: 1 addition & 0 deletions apple/RNCWebView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
REMAP_WEBVIEW_STRING_PROP(injectedJavaScriptObject)
REMAP_WEBVIEW_PROP(javaScriptEnabled)
REMAP_WEBVIEW_PROP(javaScriptCanOpenWindowsAutomatically)
REMAP_WEBVIEW_PROP(suppressJavaScriptDialogs)
REMAP_WEBVIEW_PROP(allowFileAccessFromFileURLs)
REMAP_WEBVIEW_PROP(allowUniversalAccessFromFileURLs)
REMAP_WEBVIEW_PROP(allowsInlineMediaPlayback)
Expand Down
1 change: 1 addition & 0 deletions apple/RNCWebViewImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
@property (nonatomic, assign) BOOL cacheEnabled;
@property (nonatomic, assign) BOOL javaScriptEnabled;
@property (nonatomic, assign) BOOL javaScriptCanOpenWindowsAutomatically;
@property (nonatomic, assign) BOOL suppressJavaScriptDialogs;
@property (nonatomic, assign) BOOL allowFileAccessFromFileURLs;
@property (nonatomic, assign) BOOL allowUniversalAccessFromFileURLs;
@property (nonatomic, assign) BOOL allowsLinkPreview;
Expand Down
12 changes: 12 additions & 0 deletions apple/RNCWebViewImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,10 @@ - (void) webView:(WKWebView *)webView
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
if (_suppressJavaScriptDialogs) {
completionHandler();
return;
}
#if !TARGET_OS_OSX
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
Expand All @@ -1287,6 +1291,10 @@ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSStrin
* confirm
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
if (_suppressJavaScriptDialogs) {
completionHandler(NO);
return;
}
#if !TARGET_OS_OSX
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(__unused UIAlertAction *action) {
Expand All @@ -1312,6 +1320,10 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr
* prompt
*/
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
if (_suppressJavaScriptDialogs) {
completionHandler(nil);
return;
}
if (!_disablePromptDuringLoading) {
#if !TARGET_OS_OSX
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
Expand Down
1 change: 1 addition & 0 deletions apple/RNCWebViewManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ - (RNCView *)view
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptObject, NSString)
RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(javaScriptCanOpenWindowsAutomatically, BOOL)
RCT_EXPORT_VIEW_PROPERTY(suppressJavaScriptDialogs, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowUniversalAccessFromFileURLs, BOOL)
RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
Expand Down
20 changes: 20 additions & 0 deletions docs/Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This document lays out the current public properties and methods for the React N
- [`domStorageEnabled`](Reference.md#domstorageenabled)
- [`javaScriptEnabled`](Reference.md#javascriptenabled)
- [`javaScriptCanOpenWindowsAutomatically`](Reference.md#javascriptcanopenwindowsautomatically)
- [`suppressJavaScriptDialogs`](Reference.md#suppressjavascriptdialogs)
- [`androidLayerType`](Reference.md#androidLayerType)
- [`mixedContentMode`](Reference.md#mixedcontentmode)
- [`thirdPartyCookiesEnabled`](Reference.md#thirdpartycookiesenabled)
Expand Down Expand Up @@ -91,6 +92,7 @@ This document lays out the current public properties and methods for the React N
- [`minimumFontSize`](Reference.md#minimumFontSize)
- [`downloadingMessage`](Reference.md#downloadingMessage)
- [`lackPermissionToDownloadMessage`](Reference.md#lackPermissionToDownloadMessage)
- [`allowFileDownloads`](Reference.md#allowfiledownloads)
- [`allowsProtectedMedia`](Reference.md#allowsProtectedMedia)
- [`webviewDebuggingEnabled`](Reference.md#webviewDebuggingEnabled)
- [`paymentRequestEnabled`](Reference.md#paymentRequestEnabled)
Expand Down Expand Up @@ -926,6 +928,16 @@ A Boolean value indicating whether JavaScript can open windows without user inte

---

### `suppressJavaScriptDialogs`[⬆](#props-index)

Boolean value to suppress JavaScript dialogs (alert/confirm/prompt). The default value is `false`.

| Type | Required | Platform |
| ---- | -------- | -------- |
| bool | No | Android, iOS |

---

### `androidLayerType`[⬆](#props-index)

Specifies the layer type.
Expand Down Expand Up @@ -1693,6 +1705,14 @@ This is the message that is shown in the Toast when the webview is unable to dow
| ------ | -------- | -------- |
| string | No | Android |

### `allowFileDownloads`[⬆](#props-index)

Boolean value to control whether file downloads are allowed. The default value is `true`.

| Type | Required | Platform |
| ---- | -------- | -------- |
| bool | No | Android |

### `allowsProtectedMedia`[⬆](#props-index)

Whether or not the Webview can play media protected by DRM. Default is `false`.
Expand Down
Loading
Loading