From ef08b0ebc1ce069baa75bec241fdf032fd9aa301 Mon Sep 17 00:00:00 2001 From: Tyschenko Date: Tue, 24 Jun 2025 14:45:16 +0100 Subject: [PATCH 1/2] fix: Download file requested when connecting to any Dapp. Previous JS injection was catching a Blob file creation and triggered downloading immediately. Turned out some Dapps may create Blob files without user actually trying to download these files. To prevent the issue, we try to download Blobs only on clicks on download elements in the website or when Android receives a download callback --- .../webview/RNCWebViewManagerImpl.kt | 2 +- .../extension/file/BlobFileDownloader.kt | 64 ++++++++++++++----- package.json | 2 +- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt index 87e97261c..347ae619d 100644 --- a/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt +++ b/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt @@ -123,7 +123,7 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) { return@DownloadListener } if (url.startsWith("blob:")) { - // Handled in RNCWebView.injectBlobFileDownloaderScript() + webView.evaluateJavascriptWithFallback(BlobFileDownloader.getDownloadBlobInterceptor(url)) return@DownloadListener } webView.setIgnoreErrFailedForThisURL(url) diff --git a/android/src/main/java/com/reactnativecommunity/webview/extension/file/BlobFileDownloader.kt b/android/src/main/java/com/reactnativecommunity/webview/extension/file/BlobFileDownloader.kt index abc18daf6..0ad6183e6 100644 --- a/android/src/main/java/com/reactnativecommunity/webview/extension/file/BlobFileDownloader.kt +++ b/android/src/main/java/com/reactnativecommunity/webview/extension/file/BlobFileDownloader.kt @@ -28,21 +28,55 @@ internal class BlobFileDownloader( companion object { const val JS_INTERFACE_TAG: String = "BlobFileDownloader" - fun getBlobFileInterceptor(): String = - """ - (function() { - const originalCreateObjectURL = URL.createObjectURL; - URL.createObjectURL = function(blob) { - const url = originalCreateObjectURL.call(URL, blob); - const reader = new FileReader(); - reader.readAsDataURL(blob); - reader.onloadend = function() { - const base64 = reader.result; - ${JS_INTERFACE_TAG}.getBase64FromBlobData(base64); - }; - return url; - }; + /** + * Invokes JS method downloadBlob from [getBlobFileInterceptor] + */ + fun getDownloadBlobInterceptor(url: String): String = "downloadBlob('$url');" + + /** + * This script handles Blob downloading in two ways: + * 1) It intercepts clicks on elements with a Blob: href. + * 2) It defines a method, downloadBlob, which is manually invoked from Android [com.reactnativecommunity.webview.RNCWebViewManagerImpl] in cases where the href is undefined. + */ + fun getBlobFileInterceptor(): String = """ + (function() { + if (window.blobDownloadInjected) return; + window.blobDownloadInjected = true; + + function blobToBase64(blob, callback) { + const reader = new FileReader(); + reader.onloadend = function() { + const base64 = reader.result; + callback(base64); + }; + reader.readAsDataURL(blob); + } + + function downloadBlobUrl(url) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + xhr.onload = function() { + if (this.status === 200) { + const blob = this.response; + blobToBase64(blob, function(base64) { + ${JS_INTERFACE_TAG}.getBase64FromBlobData(base64); + }); + } + }; + xhr.send(); + } + + document.addEventListener('click', function(e) { + const target = e.target; + if (target.tagName === 'A' && target.href && target.href.startsWith('blob:')) { + e.preventDefault(); + downloadBlobUrl(target.href); + } + }, true); + + window.downloadBlob = downloadBlobUrl; })(); - """.trimIndent() + """.trimIndent() } } diff --git a/package.json b/package.json index 396595eef..5ec6a0f37 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "Thibault Malbranche " ], "license": "MIT", - "version": "14.2.0", + "version": "14.2.1", "homepage": "https://github.com/MetaMask/react-native-webview-mm#readme", "scripts": { "android": "react-native run-android", From efb052b33dcf62dcb0de9eb7ab281982cc4dc6fe Mon Sep 17 00:00:00 2001 From: Tyschenko Date: Tue, 24 Jun 2025 16:16:11 +0100 Subject: [PATCH 2/2] Revert version bump back to 14.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ec6a0f37..396595eef 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "Thibault Malbranche " ], "license": "MIT", - "version": "14.2.1", + "version": "14.2.0", "homepage": "https://github.com/MetaMask/react-native-webview-mm#readme", "scripts": { "android": "react-native run-android",