diff --git a/browser/components/downloads/DownloadSpamProtection.sys.mjs b/browser/components/downloads/DownloadSpamProtection.sys.mjs
index a05c508e628fd8202b260d3a7c1a853f31bfccc2..9e890edc2830ef4d9005bb3a89eb5438b3ff8105 100644
--- a/browser/components/downloads/DownloadSpamProtection.sys.mjs
+++ b/browser/components/downloads/DownloadSpamProtection.sys.mjs
@@ -89,8 +89,9 @@ class WindowSpamProtection {
    * Add a blocked download to the spamList or increment the count of an
    * existing blocked download, then notify listeners about this.
    * @param {String} url
+   * @param {DownloadSpamEnabler} enabler
    */
-  addDownloadSpam(url) {
+  addDownloadSpam(url, enabler) {
     this._blocking = true;
     // Start listening on registered downloads views, if any exist.
     this._maybeAddViews();
@@ -104,7 +105,7 @@ class WindowSpamProtection {
     }
     // Otherwise, create a new DownloadSpam object for the URL, add it to the
     // spamList, and open the downloads panel.
-    let downloadSpam = new DownloadSpam(url);
+    let downloadSpam = new DownloadSpam(url, enabler);
     this.spamList.add(downloadSpam);
     this._downloadSpamForUrl.set(url, downloadSpam);
     this._notifyDownloadSpamAdded(downloadSpam);
@@ -188,6 +189,39 @@ class WindowSpamProtection {
   }
 }
 
+/**
+ * Helper to grant a certain principal permission for automatic downloads
+ * and to clear its download spam messages from the UI
+ */
+class DownloadSpamEnabler {
+  /**
+   * Constructs a DownloadSpamEnabler object
+   * @param {nsIPrincipal} principal
+   * @param {DownloadSpamProtection} downloadSpamProtection
+   */
+  constructor(principal, downloadSpamProtection) {
+    this.principal = principal;
+    this.downloadSpamProtection = downloadSpamProtection;
+  }
+  /**
+   * Allows a DownloadSpam item
+   * @param {DownloadSpam} downloadSpam
+   */
+  allow(downloadSpam) {
+    const pm = Services.perms;
+    pm.addFromPrincipal(
+      this.principal,
+      "automatic-download",
+      pm.ALLOW_ACTION,
+      pm.EXPIRE_SESSION
+    );
+    downloadSpam.hasBlockedData = downloadSpam.hasPartialData = false;
+    const { url } = downloadSpam.source;
+    for (let window of lazy.BrowserWindowTracker.orderedWindows) {
+      this.downloadSpamProtection.removeDownloadSpamForWindow(url, window);
+    }
+  }
+}
 /**
  * Responsible for detecting events related to downloads spam and notifying the
  * relevant window's WindowSpamProtection object. This is a singleton object,
@@ -205,9 +239,11 @@ export class DownloadSpamProtection {
    * download was blocked. This is invoked when a download is blocked by
    * nsExternalAppHandler::IsDownloadSpam
    * @param {String} url
-   * @param {Window} window
+   * @param {nsILoadInfo} loadInfo
    */
-  update(url, window) {
+  update(url, loadInfo) {
+    loadInfo = loadInfo.QueryInterface(Ci.nsILoadInfo);
+    const window = loadInfo.browsingContext.topChromeWindow;
     if (window == null) {
       lazy.DownloadsCommon.log(
         "Download spam blocked in a non-chrome window. URL: ",
@@ -221,7 +257,10 @@ export class DownloadSpamProtection {
     let wsp =
       this._forWindowMap.get(window) ?? new WindowSpamProtection(window);
     this._forWindowMap.set(window, wsp);
-    wsp.addDownloadSpam(url);
+    wsp.addDownloadSpam(
+      url,
+      new DownloadSpamEnabler(loadInfo.triggeringPrincipal, this)
+    );
   }
 
   /**
@@ -280,8 +319,9 @@ export class DownloadSpamProtection {
  * @extends Download
  */
 class DownloadSpam extends Download {
-  constructor(url) {
+  constructor(url, downloadSpamEnabler) {
     super();
+    this._downloadSpamEnabler = downloadSpamEnabler;
     this.hasBlockedData = true;
     this.stopped = true;
     this.error = new DownloadError({
@@ -292,4 +332,13 @@ class DownloadSpam extends Download {
     this.source = { url };
     this.blockedDownloadsCount = 1;
   }
+
+  /**
+   * Allows the principal which triggered this download to perform automatic downloads
+   * and clears the UI from messages reporting this download spam
+   */
+  allow() {
+    this._downloadSpamEnabler.allow(this);
+    this._notifyChange();
+  }
 }
diff --git a/toolkit/components/downloads/DownloadCore.sys.mjs b/toolkit/components/downloads/DownloadCore.sys.mjs
index 62adf072f46d6c7c1dc6601d9b7ded635a58dbdc..ae12dc14ed7d02bcf70043508b2f12011802b4f1 100644
--- a/toolkit/components/downloads/DownloadCore.sys.mjs
+++ b/toolkit/components/downloads/DownloadCore.sys.mjs
@@ -709,6 +709,10 @@ Download.prototype = {
     }
 
     this._promiseUnblock = (async () => {
+      if (this.allow) {
+        this.allow();
+        return;
+      }
       try {
         await IOUtils.move(this.target.partFilePath, this.target.path);
         await this.target.refresh();
@@ -717,7 +721,6 @@ Download.prototype = {
         this._promiseUnblock = null;
         throw ex;
       }
-
       this.succeeded = true;
       this.hasBlockedData = false;
       this._notifyChange();
@@ -947,7 +950,9 @@ Download.prototype = {
             await this._promiseCanceled;
           }
           // Ask the saver object to remove any partial data.
-          await this.saver.removeData();
+          if (this.saver) {
+            await this.saver.removeData();
+          }
           // For completeness, clear the number of bytes transferred.
           if (this.currentBytes != 0 || this.hasPartialData) {
             this.currentBytes = 0;
diff --git a/toolkit/components/downloads/DownloadIntegration.sys.mjs b/toolkit/components/downloads/DownloadIntegration.sys.mjs
index 8aed081df12c159e82e6b8a68c40445c580ddcb2..4180508e8df2969a2d568a4cc536bba76714d964 100644
--- a/toolkit/components/downloads/DownloadIntegration.sys.mjs
+++ b/toolkit/components/downloads/DownloadIntegration.sys.mjs
@@ -1156,10 +1156,7 @@ var DownloadObserver = {
       case "blocked-automatic-download":
         if (AppConstants.MOZ_BUILD_APP == "browser") {
           DownloadIntegration._initializeDownloadSpamProtection();
-          DownloadIntegration.downloadSpamProtection.update(
-            aData,
-            aSubject.topChromeWindow
-          );
+          DownloadIntegration.downloadSpamProtection.update(aData, aSubject);
         }
         break;
     }
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
index 364cfae0f849ac7e788e4bd962d80b7516246686..fa2c70044ff8a6540283e1acceed4b4586cafc8f 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1904,13 +1904,11 @@ bool nsExternalAppHandler::IsDownloadSpam(nsIChannel* aChannel) {
     if (capability == nsIPermissionManager::PROMPT_ACTION) {
       nsCOMPtr<nsIObserverService> observerService =
           mozilla::services::GetObserverService();
-      RefPtr<BrowsingContext> browsingContext;
-      loadInfo->GetBrowsingContext(getter_AddRefs(browsingContext));
 
       nsAutoCString cStringURI;
       loadInfo->TriggeringPrincipal()->GetPrePath(cStringURI);
       observerService->NotifyObservers(
-          browsingContext, "blocked-automatic-download",
+          loadInfo, "blocked-automatic-download",
           NS_ConvertASCIItoUTF16(cStringURI.get()).get());
       // FIXME: In order to escape memory leaks, currently we cancel blocked
       // downloads. This is temporary solution, because download data should be