From 92d6849af3ce1454ddfee95aae246fbd73c0d55c Mon Sep 17 00:00:00 2001 From: hackademix <giorgio@maone.net> Date: Wed, 5 Jul 2023 17:05:40 +0200 Subject: [PATCH] Bug 41854: Allow overriding download spam protection. --- .../downloads/DownloadSpamProtection.sys.mjs | 61 +++++++++++++++++-- .../components/downloads/DownloadCore.sys.mjs | 9 ++- .../downloads/DownloadIntegration.sys.mjs | 5 +- .../exthandler/nsExternalHelperAppService.cpp | 4 +- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/browser/components/downloads/DownloadSpamProtection.sys.mjs b/browser/components/downloads/DownloadSpamProtection.sys.mjs index a05c508e628fd..9e890edc2830e 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 62adf072f46d6..ae12dc14ed7d0 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 8aed081df12c1..4180508e8df29 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 364cfae0f849a..fa2c70044ff8a 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 -- GitLab