mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
Bug 1725353 - Store download permissions for the websites with multiple automatic downloads. r=mtigley
Differential Revision: https://phabricator.services.mozilla.com/D119512
This commit is contained in:
parent
bc47016bf9
commit
79de8d7ca7
@ -1683,6 +1683,11 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||
aChannel->GetURI(getter_AddRefs(mSourceUrl));
|
||||
}
|
||||
|
||||
if (StaticPrefs::browser_download_improvements_to_download_panel() &&
|
||||
IsDownloadSpam(aChannel)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mDownloadClassification =
|
||||
nsContentSecurityUtils::ClassifyDownload(aChannel, MIMEType);
|
||||
|
||||
@ -1958,6 +1963,62 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool nsExternalAppHandler::IsDownloadSpam(nsIChannel* aChannel) {
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||
|
||||
if (loadInfo->GetHasValidUserGestureActivation()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permissionManager =
|
||||
mozilla::services::GetPermissionManager();
|
||||
nsCOMPtr<nsIPrincipal> principal = loadInfo->TriggeringPrincipal();
|
||||
bool exactHostMatch = false;
|
||||
constexpr auto type = "automatic-download"_ns;
|
||||
nsCOMPtr<nsIPermission> permission;
|
||||
|
||||
permissionManager->GetPermissionObject(principal, type, exactHostMatch,
|
||||
getter_AddRefs(permission));
|
||||
|
||||
if (permission) {
|
||||
uint32_t capability;
|
||||
permission->GetCapability(&capability);
|
||||
if (capability == nsIPermissionManager::DENY_ACTION) {
|
||||
mCanceled = true;
|
||||
aChannel->Cancel(NS_ERROR_ABORT);
|
||||
return true;
|
||||
}
|
||||
if (capability == nsIPermissionManager::ALLOW_ACTION) {
|
||||
return false;
|
||||
}
|
||||
// If no action is set (i.e: null), we set PROMPT_ACTION by default,
|
||||
// which will notify the Downloads UI to open the panel on the next request.
|
||||
if (capability == nsIPermissionManager::PROMPT_ACTION) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
nsAutoCString cStringURI;
|
||||
loadInfo->TriggeringPrincipal()->GetPrePath(cStringURI);
|
||||
observerService->NotifyObservers(
|
||||
nullptr, "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
|
||||
// kept in order to restart the blocked download.
|
||||
mCanceled = true;
|
||||
aChannel->Cancel(NS_ERROR_ABORT);
|
||||
// End cancel
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
permissionManager->AddFromPrincipal(
|
||||
principal, type, nsIPermissionManager::PROMPT_ACTION,
|
||||
nsIPermissionManager::EXPIRE_NEVER, 0 /* expire time */);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert error info into proper message text and send OnStatusChange
|
||||
// notification to the dialog progress listener or nsITransfer implementation.
|
||||
void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv,
|
||||
|
@ -14,11 +14,13 @@
|
||||
#include "nsIWebProgressListener2.h"
|
||||
#include "nsIHelperAppLauncherDialog.h"
|
||||
|
||||
#include "nsILoadInfo.h"
|
||||
#include "nsIMIMEInfo.h"
|
||||
#include "nsIMIMEService.h"
|
||||
#include "nsINamed.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIPermission.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
@ -267,6 +269,8 @@ class nsExternalAppHandler final : public nsIStreamListener,
|
||||
void SetShouldCloseWindow() { mShouldCloseWindow = true; }
|
||||
|
||||
protected:
|
||||
bool IsDownloadSpam(nsIChannel* aChannel);
|
||||
|
||||
~nsExternalAppHandler();
|
||||
|
||||
nsCOMPtr<nsIFile> mTempFile;
|
||||
|
@ -29,6 +29,9 @@ support-files =
|
||||
file_xml_attachment_test.xml
|
||||
file_xml_attachment_test.xml^headers^
|
||||
[browser_download_skips_dialog.js]
|
||||
[browser_download_spam_permissions.js]
|
||||
support-files =
|
||||
test_spammy_page.html
|
||||
[browser_download_urlescape.js]
|
||||
support-files =
|
||||
file_with@@funny_name.png
|
||||
|
@ -0,0 +1,103 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PermissionTestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/PermissionTestUtils.jsm"
|
||||
);
|
||||
|
||||
const TEST_URI = "https://example.com";
|
||||
|
||||
const TEST_PATH = getRootDirectory(gTestPath).replace(
|
||||
"chrome://mochitests/content",
|
||||
TEST_URI
|
||||
);
|
||||
|
||||
const AUTOMATIC_DOWNLOAD_TOPIC = "blocked-automatic-download";
|
||||
|
||||
add_task(async function setup() {
|
||||
// Create temp directory
|
||||
let time = new Date().getTime();
|
||||
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||
tempDir.append(time);
|
||||
Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir);
|
||||
|
||||
PermissionTestUtils.add(
|
||||
TEST_URI,
|
||||
"automatic-download",
|
||||
Services.perms.UNKNOWN
|
||||
);
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.download.improvements_to_download_panel", true]],
|
||||
});
|
||||
|
||||
registerCleanupFunction(async () => {
|
||||
Services.prefs.clearUserPref("browser.download.dir");
|
||||
await IOUtils.remove(tempDir.path, { recursive: true });
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function check_download_spam_permissions() {
|
||||
const INITIAL_TABS_COUNT = gBrowser.tabs.length;
|
||||
let publicList = await Downloads.getList(Downloads.PUBLIC);
|
||||
let downloadFinishedPromise = promiseDownloadFinished(
|
||||
publicList,
|
||||
true /* stop the download from openning */
|
||||
);
|
||||
let blockedDownloadsCount = 0;
|
||||
let blockedDownloadsURI = "";
|
||||
let automaticDownloadObserver = {
|
||||
observe: function automatic_download_observe(aSubject, aTopic, aData) {
|
||||
if (aTopic === AUTOMATIC_DOWNLOAD_TOPIC) {
|
||||
blockedDownloadsCount++;
|
||||
blockedDownloadsURI = aData;
|
||||
}
|
||||
},
|
||||
};
|
||||
Services.obs.addObserver(automaticDownloadObserver, AUTOMATIC_DOWNLOAD_TOPIC);
|
||||
|
||||
let newTab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
TEST_PATH + "test_spammy_page.html"
|
||||
);
|
||||
registerCleanupFunction(async () => {
|
||||
await publicList.removeFinished();
|
||||
BrowserTestUtils.removeTab(newTab);
|
||||
Services.obs.removeObserver(
|
||||
automaticDownloadObserver,
|
||||
AUTOMATIC_DOWNLOAD_TOPIC
|
||||
);
|
||||
});
|
||||
|
||||
let download = await downloadFinishedPromise;
|
||||
TestUtils.waitForCondition(
|
||||
() => gBrowser.tabs.length == INITIAL_TABS_COUNT + 1
|
||||
);
|
||||
is(
|
||||
PermissionTestUtils.testPermission(TEST_URI, "automatic-download"),
|
||||
Services.perms.PROMPT_ACTION,
|
||||
"The permission to prompt the user should be stored."
|
||||
);
|
||||
|
||||
ok(
|
||||
await IOUtils.exists(download.target.path),
|
||||
"One file should be downloaded"
|
||||
);
|
||||
|
||||
let aCopyFilePath = download.target.path.replace(".pdf", "(1).pdf");
|
||||
is(
|
||||
await IOUtils.exists(aCopyFilePath),
|
||||
false,
|
||||
"An other file should be blocked"
|
||||
);
|
||||
|
||||
TestUtils.waitForCondition(() => blockedDownloadsCount >= 99);
|
||||
is(blockedDownloadsCount, 99, "Browser should block 99 downloads");
|
||||
is(
|
||||
blockedDownloadsURI,
|
||||
TEST_URI,
|
||||
"The test URI should have blocked automatic downloads"
|
||||
);
|
||||
});
|
@ -199,10 +199,13 @@ async function waitForProtocolAppChooserDialog(browser, state) {
|
||||
);
|
||||
}
|
||||
|
||||
async function promiseDownloadFinished(list) {
|
||||
async function promiseDownloadFinished(list, stopFromOpening) {
|
||||
return new Promise(resolve => {
|
||||
list.addView({
|
||||
onDownloadChanged(download) {
|
||||
if (stopFromOpening) {
|
||||
download.launchWhenSucceeded = false;
|
||||
}
|
||||
info("Download changed!");
|
||||
if (download.succeeded || download.error) {
|
||||
info("Download succeeded or errored");
|
||||
|
26
uriloader/exthandler/tests/mochitest/test_spammy_page.html
Normal file
26
uriloader/exthandler/tests/mochitest/test_spammy_page.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>...</title>
|
||||
</head>
|
||||
<body>
|
||||
<p> Hello, it's the spammy page! </p>
|
||||
<script type="text/javascript">
|
||||
let count = 0;
|
||||
window.onload = window.onclick = function() {
|
||||
if (count < 100) {
|
||||
count++;
|
||||
let l = document.createElement('a');
|
||||
l.href = 'data:text/plain,some text';
|
||||
l.download = 'sometext.pdf';
|
||||
|
||||
document.body.appendChild(l);
|
||||
l.click();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user