diff --git a/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs b/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs index c63c460a2dd4..7bae13531096 100644 --- a/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs +++ b/browser/components/contentanalysis/content/ContentAnalysis.sys.mjs @@ -352,8 +352,8 @@ export const ContentAnalysis = { windowAndResourceNameOrOperationType.resourceNameOrOperationType ?.operationType === Ci.nsIContentAnalysisRequest.eDownload ) { - // Don't show warn/block dialogs for downloads; they're shown inside - // the downloads panel. + // Don't show warn/block/error dialogs for downloads; they're shown + // inside the downloads panel. return; } const responseResult = @@ -744,10 +744,7 @@ export const ContentAnalysis = { _getErrorDialogMessage(aResourceNameOrOperationType) { if (aResourceNameOrOperationType.name) { return this.l10n.formatValueSync( - aResourceNameOrOperationType.operationType == - Ci.nsIContentAnalysisRequest.eUpload - ? "contentanalysis-error-message-upload-file" - : "contentanalysis-error-message-download-file", + "contentanalysis-error-message-upload-file", { filename: aResourceNameOrOperationType.name } ); } diff --git a/browser/components/downloads/DownloadsCommon.sys.mjs b/browser/components/downloads/DownloadsCommon.sys.mjs index 4679d650f848..2a56c1131588 100644 --- a/browser/components/downloads/DownloadsCommon.sys.mjs +++ b/browser/components/downloads/DownloadsCommon.sys.mjs @@ -75,6 +75,10 @@ const kDownloadsFluentStrings = new Localization( ); const kDownloadsStringsRequiringFormatting = { + contentAnalysisNoAgentError: true, + contentAnalysisInvalidAgentSignatureError: true, + contentAnalysisUnspecifiedError: true, + contentAnalysisTimeoutError: true, sizeWithUnits: true, statusSeparator: true, statusSeparatorBeforeNumber: true, diff --git a/browser/components/downloads/DownloadsViewUI.sys.mjs b/browser/components/downloads/DownloadsViewUI.sys.mjs index 09a8d41feca8..408c25f5d62e 100644 --- a/browser/components/downloads/DownloadsViewUI.sys.mjs +++ b/browser/components/downloads/DownloadsViewUI.sys.mjs @@ -34,6 +34,13 @@ XPCOMUtils.defineLazyServiceGetter( Ci.nsIApplicationReputationService ); +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "contentAnalysisAgentName", + "browser.contentanalysis.agent_name", + "A DLP agent" +); + import { Integration } from "resource://gre/modules/Integration.sys.mjs"; Integration.downloads.defineESModuleGetter( @@ -877,6 +884,31 @@ DownloadsViewUI.DownloadElementShell.prototype = { } }, + getContentAnalysisErrorTitle(strings, cancelError) { + switch (cancelError) { + case Ci.nsIContentAnalysisResponse.eNoAgent: + return strings.contentAnalysisNoAgentError( + lazy.contentAnalysisAgentName + ); + case Ci.nsIContentAnalysisResponse.eInvalidAgentSignature: + return strings.contentAnalysisInvalidAgentSignatureError( + lazy.contentAnalysisAgentName + ); + case Ci.nsIContentAnalysisResponse.eTimeout: + return strings.contentAnalysisTimeoutError( + lazy.contentAnalysisAgentName + ); + case Ci.nsIContentAnalysisResponse.eErrorOther: + return strings.contentAnalysisUnspecifiedError( + lazy.contentAnalysisAgentName + ); + default: + // This also handles the case when cancelError is undefined + // because the request wasn't cancelled at all. + return strings.blockedByContentAnalysis; + } + }, + /** * Returns [title, [details1, details2]] for blocked downloads. * The title or details could be raw strings or l10n objects. @@ -926,7 +958,10 @@ DownloadsViewUI.DownloadElementShell.prototype = { break; } return [ - s.blockedByContentAnalysis, + this.getContentAnalysisErrorTitle( + s, + this.download.error.contentAnalysisCancelError + ), [s.unblockContentAnalysis1, s.unblockContentAnalysis2], ]; case lazy.Downloads.Error.BLOCK_VERDICT_DOWNLOAD_SPAM: { diff --git a/browser/locales/en-US/chrome/browser/downloads/downloads.properties b/browser/locales/en-US/chrome/browser/downloads/downloads.properties index 5c7d4390eb17..7ebbfe04a823 100644 --- a/browser/locales/en-US/chrome/browser/downloads/downloads.properties +++ b/browser/locales/en-US/chrome/browser/downloads/downloads.properties @@ -35,6 +35,13 @@ blockedPotentiallyUnwanted=This file may harm your computer. blockedPotentiallyInsecure=File not downloaded: Potential security risk. blockedUncommon2=This file is not commonly downloaded. blockedByContentAnalysis=This file was blocked by your organization. +# LOCALIZATION NOTE (contentAnalysisNoAgentError, contentAnalysisInvalidAgentSignatureError, +# contentAnalysisUnspecifiedError, contentAnalysisTimeoutError): +# %1$S is replaced with the name of the DLP agent. +contentAnalysisNoAgentError=File was blocked because %1$S is not reachable. +contentAnalysisInvalidAgentSignatureError=File was blocked because %1$S failed signature verification. +contentAnalysisUnspecifiedError=File was blocked because of an error in communicating with %1$S. +contentAnalysisTimeoutError=%1$S timed out, so this file was blocked. warnedByContentAnalysis=This download may be unsafe and requires confirmation. # LOCALIZATION NOTE (fileMovedOrMissing): diff --git a/toolkit/components/downloads/DownloadCore.sys.mjs b/toolkit/components/downloads/DownloadCore.sys.mjs index 0fa3a3810232..c20530076f60 100644 --- a/toolkit/components/downloads/DownloadCore.sys.mjs +++ b/toolkit/components/downloads/DownloadCore.sys.mjs @@ -1936,6 +1936,7 @@ export var DownloadError = function (aProperties) { } else if (aProperties.becauseBlockedByContentAnalysis) { this.becauseBlocked = true; this.becauseBlockedByContentAnalysis = true; + this.contentAnalysisCancelError = aProperties.contentAnalysisCancelError; this.contentAnalysisWarnRequestToken = aProperties.contentAnalysisWarnRequestToken; this.reputationCheckVerdict = aProperties.reputationCheckVerdict; @@ -2001,6 +2002,12 @@ DownloadError.prototype = { */ becauseBlockedByContentAnalysis: false, + /** + * The cancelError returned by the content analysis tool, which corresponds + * to the nsIContentAnalysisResponse.CancelError enum. May be undefined. + */ + contentAnalysisCancelError: undefined, + /** * If becauseBlockedByReputationCheck is true, indicates the detailed reason * why the download was blocked, according to the "BLOCK_VERDICT_" constants. @@ -2063,7 +2070,8 @@ DownloadError.fromSerializable = function (aSerializable) { property != "becauseBlockedByParentalControls" && property != "becauseBlockedByReputationCheck" && property != "becauseBlockedByContentAnalysis" && - property != "reputationCheckVerdict" + property != "reputationCheckVerdict" && + property != "contentAnalysisCancelError" ); return e; @@ -2760,6 +2768,8 @@ DownloadCopySaver.prototype = { throw new DownloadError({ becauseBlockedByContentAnalysis: true, reputationCheckVerdict: downloadErrorVerdict, + contentAnalysisCancelError: + permissionResult.contentAnalysisCancelError, contentAnalysisWarnRequestToken: permissionResult.contentAnalysisWarnRequestToken, }); diff --git a/toolkit/components/downloads/DownloadHistory.sys.mjs b/toolkit/components/downloads/DownloadHistory.sys.mjs index 6b381d65efde..95f38fe44081 100644 --- a/toolkit/components/downloads/DownloadHistory.sys.mjs +++ b/toolkit/components/downloads/DownloadHistory.sys.mjs @@ -151,6 +151,10 @@ export let DownloadHistory = { if (download.error && download.error.reputationCheckVerdict) { metaData.reputationCheckVerdict = download.error.reputationCheckVerdict; } + if (download?.error?.hasOwnProperty("contentAnalysisCancelError")) { + metaData.contentAnalysisCancelError = + download.error.contentAnalysisCancelError; + } // This should be executed before any async parts, to ensure the cache is // updated before any notifications are activated. @@ -444,6 +448,7 @@ class HistoryDownload { } else if (metaData.state == METADATA_STATE_BLOCKED_CONTENT_ANALYSIS) { this.error = { becauseBlockedByContentAnalysis: true, + contentAnalysisCancelError: metaData.contentAnalysisCancelError, reputationCheckVerdict: metaData.reputationCheckVerdict || "", }; } else if (metaData.state == METADATA_STATE_DIRTY) { diff --git a/toolkit/components/downloads/DownloadIntegration.sys.mjs b/toolkit/components/downloads/DownloadIntegration.sys.mjs index 5e9f40ba67e2..47bfad83758f 100644 --- a/toolkit/components/downloads/DownloadIntegration.sys.mjs +++ b/toolkit/components/downloads/DownloadIntegration.sys.mjs @@ -552,11 +552,16 @@ export var DownloadIntegration = { /* autoAcknowledge*/ true ) .then(response => { + let cancelError; + if (response?.action === Ci.nsIContentAnalysisResponse.eCanceled) { + cancelError = response.cancelError; + } return { verdict: response.shouldAllowContent ? Ci.nsIApplicationReputationService.VERDICT_SAFE : Ci.nsIApplicationReputationService.VERDICT_DANGEROUS, shouldBlock: !response.shouldAllowContent, + contentAnalysisCancelError: cancelError, contentAnalysisWarnRequestToken: undefined, }; });