Skip to content

Commit ef08b0e

Browse files
committed
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
1 parent eb08a62 commit ef08b0e

File tree

3 files changed

+51
-17
lines changed

3 files changed

+51
-17
lines changed

android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class RNCWebViewManagerImpl(private val newArch: Boolean = false) {
123123
return@DownloadListener
124124
}
125125
if (url.startsWith("blob:")) {
126-
// Handled in RNCWebView.injectBlobFileDownloaderScript()
126+
webView.evaluateJavascriptWithFallback(BlobFileDownloader.getDownloadBlobInterceptor(url))
127127
return@DownloadListener
128128
}
129129
webView.setIgnoreErrFailedForThisURL(url)

android/src/main/java/com/reactnativecommunity/webview/extension/file/BlobFileDownloader.kt

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,55 @@ internal class BlobFileDownloader(
2828
companion object {
2929
const val JS_INTERFACE_TAG: String = "BlobFileDownloader"
3030

31-
fun getBlobFileInterceptor(): String =
32-
"""
33-
(function() {
34-
const originalCreateObjectURL = URL.createObjectURL;
35-
URL.createObjectURL = function(blob) {
36-
const url = originalCreateObjectURL.call(URL, blob);
37-
const reader = new FileReader();
38-
reader.readAsDataURL(blob);
39-
reader.onloadend = function() {
40-
const base64 = reader.result;
41-
${JS_INTERFACE_TAG}.getBase64FromBlobData(base64);
42-
};
43-
return url;
44-
};
31+
/**
32+
* Invokes JS method downloadBlob from [getBlobFileInterceptor]
33+
*/
34+
fun getDownloadBlobInterceptor(url: String): String = "downloadBlob('$url');"
35+
36+
/**
37+
* This script handles Blob downloading in two ways:
38+
* 1) It intercepts clicks on elements with a Blob: href.
39+
* 2) It defines a method, downloadBlob, which is manually invoked from Android [com.reactnativecommunity.webview.RNCWebViewManagerImpl] in cases where the href is undefined.
40+
*/
41+
fun getBlobFileInterceptor(): String = """
42+
(function() {
43+
if (window.blobDownloadInjected) return;
44+
window.blobDownloadInjected = true;
45+
46+
function blobToBase64(blob, callback) {
47+
const reader = new FileReader();
48+
reader.onloadend = function() {
49+
const base64 = reader.result;
50+
callback(base64);
51+
};
52+
reader.readAsDataURL(blob);
53+
}
54+
55+
function downloadBlobUrl(url) {
56+
const xhr = new XMLHttpRequest();
57+
xhr.open('GET', url, true);
58+
xhr.responseType = 'blob';
59+
xhr.onload = function() {
60+
if (this.status === 200) {
61+
const blob = this.response;
62+
blobToBase64(blob, function(base64) {
63+
${JS_INTERFACE_TAG}.getBase64FromBlobData(base64);
64+
});
65+
}
66+
};
67+
xhr.send();
68+
}
69+
70+
document.addEventListener('click', function(e) {
71+
const target = e.target;
72+
if (target.tagName === 'A' && target.href && target.href.startsWith('blob:')) {
73+
e.preventDefault();
74+
downloadBlobUrl(target.href);
75+
}
76+
}, true);
77+
78+
window.downloadBlob = downloadBlobUrl;
4579
})();
46-
""".trimIndent()
80+
""".trimIndent()
4781
}
4882
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"Thibault Malbranche <[email protected]>"
1111
],
1212
"license": "MIT",
13-
"version": "14.2.0",
13+
"version": "14.2.1",
1414
"homepage": "https://github.com/MetaMask/react-native-webview-mm#readme",
1515
"scripts": {
1616
"android": "react-native run-android",

0 commit comments

Comments
 (0)