[tbb-commits] [tor-browser] 50/73: Bug 1739348 - Don't open downloads panel after download dialogs. r=NeilDeakin, a=RyanVM
gitolite role
git at cupani.torproject.org
Wed Sep 21 20:17:43 UTC 2022
This is an automated email from the git hooks/post-receive script.
richard pushed a commit to branch geckoview-102.3.0esr-12.0-1
in repository tor-browser.
commit 226da410d935623597a0b535222d4386031b1610
Author: Shane Hughes <shmediaproductions at gmail.com>
AuthorDate: Mon Jul 18 20:45:41 2022 +0000
Bug 1739348 - Don't open downloads panel after download dialogs. r=NeilDeakin, a=RyanVM
This is a medium sized patch to legacy download construction. It takes
advantage of the new property added in Bug 1762033 to prevent the
downloads panel from being automatically shown when a download is added
after an interaction with the unknown content type dialog or the file
picker dialog. I chose to not do the same for failed transfers since I
thought it might serve some use, but that might be wrong. I don't know
if there's a way to test the dialog that appears when you download an
executable without going through the same path I adjusted with the
patch. It seems like it's covered but I could be wrong. Also add a test
to cover these changes from the bottom up. Thanks and apologies for my
sloppy C++, though I'm sure I'll learn a lot more from the review 😅
Differential Revision: https://phabricator.services.mozilla.com/D145312
---
browser/components/downloads/DownloadsCommon.jsm | 14 +-
.../components/downloads/test/browser/browser.ini | 5 +
.../test/browser/browser_downloads_panel_opens.js | 236 ++++++++++++++++++++-
.../downloads/test/browser/browser_tempfilename.js | 38 +++-
toolkit/components/downloads/DownloadLegacy.jsm | 10 +-
.../pdfjs/test/browser_pdfjs_download_button.js | 43 +---
toolkit/mozapps/downloads/HelperAppDlg.jsm | 3 +-
uriloader/base/nsITransfer.idl | 9 +-
.../exthandler/nsExternalHelperAppService.cpp | 27 ++-
uriloader/exthandler/nsExternalHelperAppService.h | 8 +-
.../exthandler/nsIExternalHelperAppService.idl | 5 +-
.../browser_open_internal_choice_persistence.js | 4 +-
12 files changed, 344 insertions(+), 58 deletions(-)
diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm
index 422be505a3156..11e119a0155ea 100644
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -1003,14 +1003,18 @@ DownloadsDataCtor.prototype = {
browserWin == Services.focus.activeWindow &&
gAlwaysOpenPanel;
+ // For new downloads after the first one, don't show the panel
+ // automatically, but provide a visible notification in the topmost browser
+ // window, if the status indicator is already visible. Also ensure that if
+ // openDownloadsListOnStart = false is passed, we always skip opening the
+ // panel. That's because this will only be passed if the download is started
+ // without user interaction or if a dialog was previously opened in the
+ // process of the download (e.g. unknown content type dialog).
if (
- this.panelHasShownBefore &&
aType != "error" &&
- !shouldOpenDownloadsPanel
+ ((this.panelHasShownBefore && !shouldOpenDownloadsPanel) ||
+ !openDownloadsListOnStart)
) {
- // For new downloads after the first one, don't show the panel
- // automatically, but provide a visible notification in the topmost
- // browser window, if the status indicator is already visible.
DownloadsCommon.log("Showing new download notification.");
browserWin.DownloadsIndicatorView.showEventNotification(aType);
return;
diff --git a/browser/components/downloads/test/browser/browser.ini b/browser/components/downloads/test/browser/browser.ini
index f803f34daa988..6098bd3caa7c6 100644
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -48,6 +48,11 @@ support-files =
[browser_downloads_panel_dontshow.js]
[browser_downloads_panel_height.js]
[browser_downloads_panel_opens.js]
+skip-if =
+ os == "linux" && verify && !debug # For some reason linux opt verify builds time out.
+support-files =
+ foo.txt
+ foo.txt^headers^
[browser_downloads_autohide.js]
[browser_go_to_download_page.js]
[browser_pdfjs_preview.js]
diff --git a/browser/components/downloads/test/browser/browser_downloads_panel_opens.js b/browser/components/downloads/test/browser/browser_downloads_panel_opens.js
index e56b24fd0e3bf..fb1e6f76dabb9 100644
--- a/browser/components/downloads/test/browser/browser_downloads_panel_opens.js
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_opens.js
@@ -1,6 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
+let { MockFilePicker } = SpecialPowers;
+MockFilePicker.init(window);
+registerCleanupFunction(() => MockFilePicker.cleanup());
+
/**
* Check that the downloads panel opens when a download is spoofed.
*/
@@ -85,6 +89,175 @@ function clickCheckbox(checkbox) {
checkbox.parentElement.hidePopup();
}
+/**
+ * Test that the downloads panel correctly opens or doesn't open based on
+ * whether the download triggered a dialog already. If askWhereToSave is true,
+ * we should get a file picker dialog. If preferredAction is alwaysAsk, we
+ * should get an unknown content type dialog. If neither of those is true, we
+ * should get no dialog at all, and expect the downloads panel to open.
+ * @param {boolean} [expectPanelToOpen] true - fail if panel doesn't open
+ * false (default) - fail if it opens
+ * @param {number} [preferredAction] Default download action:
+ * 0 (default) - save download to disk
+ * 1 - open UCT dialog first
+ * @param {boolean} [askWhereToSave] true - open file picker dialog
+ * false (default) - use download dir
+ */
+async function testDownloadsPanelAfterDialog({
+ expectPanelToOpen = false,
+ preferredAction,
+ askWhereToSave = false,
+} = {}) {
+ const { saveToDisk, alwaysAsk } = Ci.nsIHandlerInfo;
+ if (![saveToDisk, alwaysAsk].includes(preferredAction)) {
+ preferredAction = saveToDisk;
+ }
+ const openUCT = preferredAction === alwaysAsk;
+ const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ );
+ const MimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+ const HandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
+ Ci.nsIHandlerService
+ );
+ let publicList = await Downloads.getList(Downloads.PUBLIC);
+
+ for (let download of await publicList.getAll()) {
+ await publicList.remove(download);
+ }
+
+ // We need to test the changes from bug 1739348, where the helper app service
+ // sets a flag based on whether a file picker dialog was opened, and this flag
+ // determines whether the downloads panel will be opened as the download
+ // starts. We need to actually hit "Save" for the download to start, but we
+ // can't interact with the real file picker dialog. So this temporarily
+ // replaces it with a barebones component that plugs into the helper app
+ // service and tells it to start saving the file to the default path.
+ if (askWhereToSave) {
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+ MockFilePicker.showCallback = function(fp) {
+ // Get the default location from the helper app service.
+ let testFile = MockFilePicker.displayDirectory.clone();
+ testFile.append(fp.defaultString);
+ info("File picker download path: " + testFile.path);
+ MockFilePicker.setFiles([testFile]);
+ MockFilePicker.filterIndex = 0; // kSaveAsType_Complete
+ MockFilePicker.showCallback = null;
+ // Confirm that saving should proceed. The helper app service uses this
+ // value to determine whether to invoke launcher.saveDestinationAvailable
+ return MockFilePicker.returnOK;
+ };
+ }
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.download.useDownloadDir", !askWhereToSave],
+ ["browser.download.always_ask_before_handling_new_types", openUCT],
+ ["browser.download.improvements_to_download_panel", true],
+ ["security.dialog_enable_delay", 0],
+ ],
+ });
+
+ // Configure the handler for the file according to parameters.
+ let mimeInfo = MimeSvc.getFromTypeAndExtension("text/plain", "txt");
+ let existed = HandlerSvc.exists(mimeInfo);
+ mimeInfo.alwaysAskBeforeHandling = openUCT;
+ mimeInfo.preferredAction = preferredAction;
+ HandlerSvc.store(mimeInfo);
+ registerCleanupFunction(async () => {
+ // Reset the handler to its original state.
+ if (existed) {
+ HandlerSvc.store(mimeInfo);
+ } else {
+ HandlerSvc.remove(mimeInfo);
+ }
+ await publicList.removeFinished();
+ BrowserTestUtils.removeTab(loadingTab);
+ });
+
+ let dialogWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
+ let downloadFinishedPromise = new Promise(resolve => {
+ publicList.addView({
+ onDownloadChanged(download) {
+ info("Download changed!");
+ if (download.succeeded || download.error) {
+ info("Download succeeded or failed.");
+ publicList.removeView(this);
+ resolve(download);
+ }
+ },
+ });
+ });
+ let panelOpenedPromise = expectPanelToOpen ? promisePanelOpened() : null;
+
+ // Open the tab that will trigger the download.
+ let loadingTab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: TEST_PATH + "foo.txt",
+ waitForLoad: false,
+ waitForStateStop: true,
+ });
+
+ // Wait for a UCT dialog if the handler was set up to open one.
+ if (openUCT) {
+ let dialogWindow = await dialogWindowPromise;
+ is(
+ dialogWindow.location.href,
+ "chrome://mozapps/content/downloads/unknownContentType.xhtml",
+ "Should have seen the unknown content dialogWindow."
+ );
+ let doc = dialogWindow.document;
+ let dialog = doc.getElementById("unknownContentType");
+ let radio = doc.getElementById("save");
+ let button = dialog.getButton("accept");
+
+ await TestUtils.waitForCondition(
+ () => !button.disabled,
+ "Waiting for the UCT dialog's Accept button to be enabled."
+ );
+ ok(!radio.hidden, "The Save option should be visible");
+ // Make sure we aren't opening the file.
+ radio.click();
+ ok(radio.selected, "The Save option should be selected");
+ button.disabled = false;
+ dialog.acceptDialog();
+ }
+
+ info("Waiting for download to finish.");
+ let download = await downloadFinishedPromise;
+ ok(!download.error, "There should be no error.");
+ is(
+ DownloadsPanel.isPanelShowing,
+ expectPanelToOpen,
+ `Panel should${expectPanelToOpen ? " " : " not "}be showing.`
+ );
+ if (DownloadsPanel.isPanelShowing) {
+ await panelOpenedPromise;
+ let hiddenPromise = BrowserTestUtils.waitForPopupEvent(
+ DownloadsPanel.panel,
+ "hidden"
+ );
+ DownloadsPanel.hidePanel();
+ await hiddenPromise;
+ }
+ if (download?.target.exists) {
+ try {
+ info("Removing test file: " + download.target.path);
+ if (Services.appinfo.OS === "WINNT") {
+ await IOUtils.setPermissions(download.target.path, 0o600);
+ }
+ await IOUtils.remove(download.target.path);
+ } catch (ex) {
+ /* ignore */
+ }
+ }
+ for (let dl of await publicList.getAll()) {
+ await publicList.remove(dl);
+ }
+ BrowserTestUtils.removeTab(loadingTab);
+}
+
/**
* Make sure the downloads panel opens automatically with a new download.
*/
@@ -93,6 +266,7 @@ add_task(async function test_downloads_panel_opens() {
set: [
["browser.download.improvements_to_download_panel", true],
["browser.download.always_ask_before_handling_new_types", false],
+ ["browser.download.alwaysOpenPanel", true],
],
});
await checkPanelOpens();
@@ -103,6 +277,7 @@ add_task(async function test_customizemode_doesnt_wreck_things() {
set: [
["browser.download.improvements_to_download_panel", true],
["browser.download.always_ask_before_handling_new_types", false],
+ ["browser.download.alwaysOpenPanel", true],
],
});
@@ -114,7 +289,7 @@ add_task(async function test_customizemode_doesnt_wreck_things() {
gCustomizeMode.enter();
await customizationReadyPromise;
- info("try to open the panel (will not work, in customize mode)");
+ info("Try to open the panel (will not work, in customize mode)");
let promise = promisePanelOpened();
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
await TestUtils.waitForCondition(
@@ -134,10 +309,16 @@ add_task(async function test_customizemode_doesnt_wreck_things() {
gCustomizeMode.exit();
await afterCustomizationPromise;
+ // Avoid a failure on Linux where the window isn't active for some reason,
+ // which prevents the window's downloads panel from opening.
+ if (Services.focus.activeWindow != window) {
+ info("Main window is not active, trying to focus.");
+ await SimpleTest.promiseFocus(window);
+ is(Services.focus.activeWindow, window, "Main window should be active.");
+ }
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
- is(
- DownloadsPanel.isPanelShowing,
- true,
+ await TestUtils.waitForCondition(
+ () => DownloadsPanel.isPanelShowing,
"Panel state should indicate a preparation to be opened"
);
await promise;
@@ -456,3 +637,50 @@ add_task(async function test_alwaysOpenPanel_menuitem() {
await checkPanelOpens();
});
+
+/**
+ * Verify that the downloads panel opens if the download did not open a file
+ * picker or UCT dialog
+ */
+add_task(async function test_downloads_panel_after_no_dialogs() {
+ await testDownloadsPanelAfterDialog({ expectPanelToOpen: true });
+ ok(true, "Downloads panel opened because no dialogs were opened.");
+});
+
+/**
+ * Verify that the downloads panel doesn't open if the download opened an
+ * unknown content type dialog (e.g. action = always ask)
+ */
+add_task(async function test_downloads_panel_after_UCT_dialog() {
+ await testDownloadsPanelAfterDialog({
+ expectPanelToOpen: false,
+ preferredAction: Ci.nsIHandlerInfo.alwaysAsk,
+ });
+ ok(true, "Downloads panel suppressed after UCT dialog.");
+});
+
+/**
+ * Verify that the downloads panel doesn't open if the download opened a file
+ * picker dialog (e.g. useDownloadDir = false)
+ */
+add_task(async function test_downloads_panel_after_file_picker_dialog() {
+ await testDownloadsPanelAfterDialog({
+ expectPanelToOpen: false,
+ preferredAction: Ci.nsIHandlerInfo.saveToDisk,
+ askWhereToSave: true,
+ });
+ ok(true, "Downloads panel suppressed after file picker dialog.");
+});
+
+/**
+ * Verify that the downloads panel doesn't open if the download opened both
+ * dialogs (e.g. default action = always ask AND useDownloadDir = false)
+ */
+add_task(async function test_downloads_panel_after_both_dialogs() {
+ await testDownloadsPanelAfterDialog({
+ expectPanelToOpen: false,
+ preferredAction: Ci.nsIHandlerInfo.alwaysAsk,
+ askWhereToSave: true,
+ });
+ ok(true, "Downloads panel suppressed after UCT and file picker dialogs.");
+});
diff --git a/browser/components/downloads/test/browser/browser_tempfilename.js b/browser/components/downloads/test/browser/browser_tempfilename.js
index b995f078895f1..dcef4016a6fbe 100644
--- a/browser/components/downloads/test/browser/browser_tempfilename.js
+++ b/browser/components/downloads/test/browser/browser_tempfilename.js
@@ -16,6 +16,27 @@ add_task(async function test_tempfilename() {
};
list.addView(view);
});
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.download.improvements_to_download_panel", true],
+ ["browser.download.always_ask_before_handling_new_types", false],
+ ],
+ });
+
+ const MimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+ const HandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
+ Ci.nsIHandlerService
+ );
+ let mimeInfo = MimeSvc.getFromTypeAndExtension(
+ HandlerSvc.getTypeFromExtension("txt"),
+ "txt"
+ );
+ let existed = HandlerSvc.exists(mimeInfo);
+ mimeInfo.alwaysAskBeforeHandling = false;
+ mimeInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk;
+ HandlerSvc.store(mimeInfo);
+
serveInterruptibleAsDownload();
mustInterruptResponses();
await BrowserTestUtils.withNewTab(
@@ -27,7 +48,21 @@ add_task(async function test_tempfilename() {
},
async () => {
let download = await downloadStarted;
- registerCleanupFunction(() => download.finalize());
+ registerCleanupFunction(async () => {
+ if (existed) {
+ HandlerSvc.store(mimeInfo);
+ } else {
+ HandlerSvc.remove(mimeInfo);
+ }
+ await download.finalize(true);
+ if (Services.appinfo.OS === "WINNT") {
+ // We need to make the file writable to delete it on Windows.
+ await IOUtils.setPermissions(download.target.path, 0o600);
+ }
+ await IOUtils.remove(download.target.path);
+ await download.finalize();
+ await list.removeFinished();
+ });
let { partFilePath } = download.target;
Assert.stringContains(
@@ -51,7 +86,6 @@ add_task(async function test_tempfilename() {
!(await IOUtils.exists(download.target.partFilePath)),
"Temp file should be gone."
);
- await IOUtils.remove(download.target.path);
}
);
});
diff --git a/toolkit/components/downloads/DownloadLegacy.jsm b/toolkit/components/downloads/DownloadLegacy.jsm
index 14b9b8b8780fd..7ff95b53190c6 100644
--- a/toolkit/components/downloads/DownloadLegacy.jsm
+++ b/toolkit/components/downloads/DownloadLegacy.jsm
@@ -274,7 +274,8 @@ DownloadLegacyTransfer.prototype = {
aCancelable,
aIsPrivate,
aDownloadClassification,
- aReferrerInfo
+ aReferrerInfo,
+ aOpenDownloadsListOnStart
) {
return this._nsITransferInitInternal(
aSource,
@@ -287,7 +288,8 @@ DownloadLegacyTransfer.prototype = {
aCancelable,
aIsPrivate,
aDownloadClassification,
- aReferrerInfo
+ aReferrerInfo,
+ aOpenDownloadsListOnStart
);
},
@@ -303,6 +305,7 @@ DownloadLegacyTransfer.prototype = {
aIsPrivate,
aDownloadClassification,
aReferrerInfo,
+ aOpenDownloadsListOnStart,
aBrowsingContext,
aHandleInternally,
aHttpChannel
@@ -327,6 +330,7 @@ DownloadLegacyTransfer.prototype = {
aIsPrivate,
aDownloadClassification,
aReferrerInfo,
+ aOpenDownloadsListOnStart,
userContextId,
browsingContextId,
aHandleInternally,
@@ -346,6 +350,7 @@ DownloadLegacyTransfer.prototype = {
isPrivate,
aDownloadClassification,
referrerInfo,
+ openDownloadsListOnStart = true,
userContextId = 0,
browsingContextId = 0,
handleInternally = false,
@@ -397,6 +402,7 @@ DownloadLegacyTransfer.prototype = {
contentType,
launcherPath,
handleInternally,
+ openDownloadsListOnStart,
};
// In case the Download was classified as insecure/dangerous
diff --git a/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js b/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js
index b0040a71941f6..fa6b6d6c978a9 100644
--- a/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js
+++ b/toolkit/components/pdfjs/test/browser_pdfjs_download_button.js
@@ -52,22 +52,6 @@ add_setup(async function() {
});
});
-async function closeDownloadsPanel() {
- if (DownloadsPanel.panel.state !== "closed") {
- let hiddenPromise = BrowserTestUtils.waitForEvent(
- DownloadsPanel.panel,
- "popuphidden"
- );
- DownloadsPanel.hidePanel();
- await hiddenPromise;
- }
- is(
- DownloadsPanel.panel.state,
- "closed",
- "Check that the download panel is closed"
- );
-}
-
/**
* Check clicking the download button saves the file and doesn't open a new viewer
*/
@@ -75,7 +59,10 @@ add_task(async function test_downloading_pdf_nonprivate_window() {
const pdfUrl = TESTROOT + "file_pdfjs_test.pdf";
await SpecialPowers.pushPrefEnv({
- set: [["browser.download.improvements_to_download_panel", true]],
+ set: [
+ ["browser.download.improvements_to_download_panel", true],
+ ["browser.download.always_ask_before_handling_new_types", false],
+ ],
});
await BrowserTestUtils.withNewTab(
@@ -87,7 +74,6 @@ add_task(async function test_downloading_pdf_nonprivate_window() {
info(`${tabCount} tabs are open at the start of the test`);
let downloadList = await Downloads.getList(Downloads.PUBLIC);
- const initialDownloadCount = (await downloadList.getAll()).length;
let filePickerShown = createPromiseForFilePicker();
@@ -100,23 +86,16 @@ add_task(async function test_downloading_pdf_nonprivate_window() {
info("Waiting for a filename to be picked from the file picker");
await filePickerShown;
- // check that resulted in a download being added to the list. The
- // download panel should not open.
await downloadFinishedPromise;
- is(
- DownloadsPanel.panel.state,
- "closed",
- "Check the download panel state is 'closed'"
- );
- downloadList = await Downloads.getList(Downloads.PUBLIC);
- const allDownloads = await downloadList.getAll();
- let currentDownloadCount = allDownloads.length;
- is(
- currentDownloadCount,
- initialDownloadCount + 1,
- "A download was added when we clicked download"
+ ok(true, "A download was added when we clicked download");
+
+ // See bug 1739348 - don't show panel for downloads that opened dialogs
+ ok(
+ !DownloadsPanel.isPanelShowing,
+ "The download panel did not open, since the file picker was shown already"
);
+ const allDownloads = await downloadList.getAll();
const dl = allDownloads.find(dl => dl.source.originalUrl === pdfUrl);
ok(!!dl, "The pdf download has the correct url in source.originalUrl");
diff --git a/toolkit/mozapps/downloads/HelperAppDlg.jsm b/toolkit/mozapps/downloads/HelperAppDlg.jsm
index 325de51c0c5fe..0a5a4a460bf38 100644
--- a/toolkit/mozapps/downloads/HelperAppDlg.jsm
+++ b/toolkit/mozapps/downloads/HelperAppDlg.jsm
@@ -409,7 +409,8 @@ nsUnknownContentTypeDialog.prototype = {
}
}
}
- aLauncher.saveDestinationAvailable(result);
+ // Don't pop up the downloads panel redundantly.
+ aLauncher.saveDestinationAvailable(result, true);
});
});
})().catch(Cu.reportError);
diff --git a/uriloader/base/nsITransfer.idl b/uriloader/base/nsITransfer.idl
index ba701c982d7a0..828e4c4a0f7bc 100644
--- a/uriloader/base/nsITransfer.idl
+++ b/uriloader/base/nsITransfer.idl
@@ -62,7 +62,12 @@ interface nsITransfer : nsIWebProgressListener2 {
*
* @param aDownloadClassification Indicates wheter the download is unwanted,
* should be considered dangerous or insecure.
+ *
* @param aReferrerInfo The Referrer this download is started with
+ *
+ * @param aOpenDownloadsListOnStart true (default) - Open downloads panel.
+ * false - Only show an icon indicator.
+ * This parameter is optional.
*/
void init(in nsIURI aSource,
in nsIURI aSourceOriginalURI,
@@ -74,7 +79,8 @@ interface nsITransfer : nsIWebProgressListener2 {
in nsICancelable aCancelable,
in boolean aIsPrivate,
in long aDownloadClassification,
- in nsIReferrerInfo aReferrerInfo);
+ in nsIReferrerInfo aReferrerInfo,
+ [optional] in boolean aOpenDownloadsListOnStart);
/**
* Same as init, but allows for passing the browsingContext
@@ -97,6 +103,7 @@ interface nsITransfer : nsIWebProgressListener2 {
in boolean aIsPrivate,
in long aDownloadClassification,
in nsIReferrerInfo aReferrerInfo,
+ [optional] in boolean aOpenDownloadsListOnStart,
in BrowsingContext aBrowsingContext,
in boolean aHandleInternally,
in nsIHttpChannel aHttpChannel);
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
index adb05b3c46b85..f5b0e2e5bc95c 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1280,6 +1280,7 @@ nsExternalAppHandler::nsExternalAppHandler(
mIsFileChannel(false),
mShouldCloseWindow(false),
mHandleInternally(false),
+ mDialogShowing(false),
mReason(aReason),
mTempFileIsExecutable(false),
mTimeDownloadStarted(0),
@@ -1855,6 +1856,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
// this will create a reference cycle (the dialog holds a reference to us as
// nsIHelperAppLauncher), which will be broken in Cancel or CreateTransfer.
nsCOMPtr<nsIInterfaceRequestor> dialogParent = GetDialogParent();
+ // Don't pop up the downloads panel since we're already going to pop up the
+ // UCT dialog for basically the same effect.
+ mDialogShowing = true;
rv = mDialog->Show(this, dialogParent, mReason);
// what do we do if the dialog failed? I guess we should call Cancel and
@@ -2351,14 +2355,15 @@ nsresult nsExternalAppHandler::CreateTransfer() {
rv = transfer->InitWithBrowsingContext(
mSourceUrl, target, u""_ns, mMimeInfo, mTimeDownloadStarted, mTempFile,
this, channel && NS_UsePrivateBrowsing(channel),
- mDownloadClassification, referrerInfo, mBrowsingContext,
- mHandleInternally, nullptr);
+ mDownloadClassification, referrerInfo, !mDialogShowing,
+ mBrowsingContext, mHandleInternally, nullptr);
} else {
rv = transfer->Init(mSourceUrl, nullptr, target, u""_ns, mMimeInfo,
mTimeDownloadStarted, mTempFile, this,
channel && NS_UsePrivateBrowsing(channel),
- mDownloadClassification, referrerInfo);
+ mDownloadClassification, referrerInfo, !mDialogShowing);
}
+ mDialogShowing = false;
NS_ENSURE_SUCCESS(rv, rv);
@@ -2443,13 +2448,13 @@ nsresult nsExternalAppHandler::CreateFailedTransfer() {
rv = transfer->InitWithBrowsingContext(
mSourceUrl, pseudoTarget, u""_ns, mMimeInfo, mTimeDownloadStarted,
mTempFile, this, channel && NS_UsePrivateBrowsing(channel),
- mDownloadClassification, referrerInfo, mBrowsingContext,
+ mDownloadClassification, referrerInfo, true, mBrowsingContext,
mHandleInternally, httpChannel);
} else {
rv = transfer->Init(mSourceUrl, nullptr, pseudoTarget, u""_ns, mMimeInfo,
mTimeDownloadStarted, mTempFile, this,
channel && NS_UsePrivateBrowsing(channel),
- mDownloadClassification, referrerInfo);
+ mDownloadClassification, referrerInfo, true);
}
NS_ENSURE_SUCCESS(rv, rv);
@@ -2459,11 +2464,16 @@ nsresult nsExternalAppHandler::CreateFailedTransfer() {
return NS_OK;
}
-nsresult nsExternalAppHandler::SaveDestinationAvailable(nsIFile* aFile) {
- if (aFile)
+nsresult nsExternalAppHandler::SaveDestinationAvailable(nsIFile* aFile,
+ bool aDialogWasShown) {
+ if (aFile) {
+ if (aDialogWasShown) {
+ mDialogShowing = true;
+ }
ContinueSave(aFile);
- else
+ } else {
Cancel(NS_BINDING_ABORTED);
+ }
return NS_OK;
}
@@ -2735,6 +2745,7 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
// Break our reference cycle with the helper app dialog (set up in
// OnStartRequest)
mDialog = nullptr;
+ mDialogShowing = false;
mRequest = nullptr;
diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h
index ce142a4f5b6b0..439be1645f4fc 100644
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -376,6 +376,12 @@ class nsExternalAppHandler final : public nsIStreamListener,
*/
bool mHandleInternally;
+ /**
+ * True if any dialog (e.g. unknown content type or file picker) is shown —
+ * can stop downloads panel from opening, to avoid redundant interruptions.
+ */
+ bool mDialogShowing;
+
/**
* One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the
* reason the dialog was shown (unknown content type, server requested it,
@@ -522,7 +528,7 @@ class nsExternalAppHandler final : public nsIStreamListener,
const nsString& path);
/**
- * Set in nsHelperDlgApp.js. This is always null after the user has chosen an
+ * Set in HelperAppDlg.jsm. This is always null after the user has chosen an
* action.
*/
nsCOMPtr<nsIWebProgressListener2> mDialogProgressListener;
diff --git a/uriloader/exthandler/nsIExternalHelperAppService.idl b/uriloader/exthandler/nsIExternalHelperAppService.idl
index 3554c69aaced1..307e6196a89df 100644
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
@@ -150,8 +150,11 @@ interface nsIHelperAppLauncher : nsICancelable
* Callback invoked by nsIHelperAppLauncherDialog::promptForSaveToFileAsync
* after the user has chosen a file through the File Picker (or dismissed it).
* @param aFile The file that was chosen by the user (or null if dialog was dismissed).
+ * @param aDialogWasShown Optional boolean - false by default. Pass true if a
+ * dialog was opened in the process of reaching this file result. If true, we
+ * suppress the opening of the downloads panel to avoid redundancy.
*/
- void saveDestinationAvailable(in nsIFile aFile);
+ void saveDestinationAvailable(in nsIFile aFile, [optional] in boolean aDialogWasShown);
/**
* The following methods are used by the progress dialog to get or set
diff --git a/uriloader/exthandler/tests/mochitest/browser_open_internal_choice_persistence.js b/uriloader/exthandler/tests/mochitest/browser_open_internal_choice_persistence.js
index 11f9e24092053..e6dffa8df4ab4 100644
--- a/uriloader/exthandler/tests/mochitest/browser_open_internal_choice_persistence.js
+++ b/uriloader/exthandler/tests/mochitest/browser_open_internal_choice_persistence.js
@@ -33,11 +33,13 @@ function waitForAcceptButtonToGetEnabled(doc) {
}
add_setup(async function() {
- // Remove the security delay for the dialog during the test.
await SpecialPowers.pushPrefEnv({
set: [
+ // Remove the security delay for the dialog during the test.
["security.dialog_enable_delay", 0],
["browser.helperApps.showOpenOptionForViewableInternally", true],
+ // Make sure we don't open a file picker dialog somehow.
+ ["browser.download.useDownloadDir", true],
],
});
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the tbb-commits
mailing list