summaryrefslogtreecommitdiffstats
path: root/browser/components/downloads/test/browser/browser_downloads_panel_block.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/downloads/test/browser/browser_downloads_panel_block.js')
-rw-r--r--browser/components/downloads/test/browser/browser_downloads_panel_block.js185
1 files changed, 185 insertions, 0 deletions
diff --git a/browser/components/downloads/test/browser/browser_downloads_panel_block.js b/browser/components/downloads/test/browser/browser_downloads_panel_block.js
new file mode 100644
index 0000000000..d1791a5862
--- /dev/null
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_block.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+"use strict";
+
+add_task(async function mainTest() {
+ await task_resetState();
+
+ let verdicts = [
+ Downloads.Error.BLOCK_VERDICT_UNCOMMON,
+ Downloads.Error.BLOCK_VERDICT_MALWARE,
+ Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED,
+ Downloads.Error.BLOCK_VERDICT_INSECURE,
+ ];
+ await task_addDownloads(verdicts.map(v => makeDownload(v)));
+
+ // Check that the richlistitem for each download is correct.
+ for (let i = 0; i < verdicts.length; i++) {
+ await openPanel();
+
+ // Handle items backwards, using lastElementChild, to ensure there's no
+ // code wrongly resetting the selection to the first item during the process.
+ let item = DownloadsView.richListBox.lastElementChild;
+
+ info("Open the panel and click the item to show the subview.");
+ let viewPromise = promiseViewShown(DownloadsBlockedSubview.subview);
+ EventUtils.synthesizeMouseAtCenter(item, {});
+ await viewPromise;
+
+ // Items are listed in newest-to-oldest order, so e.g. the first item's
+ // verdict is the last element in the verdicts array.
+ Assert.ok(
+ DownloadsBlockedSubview.subview.getAttribute("verdict"),
+ verdicts[verdicts.count - i - 1]
+ );
+
+ info("Go back to the main view.");
+ viewPromise = promiseViewShown(DownloadsBlockedSubview.mainView);
+ DownloadsBlockedSubview.panelMultiView.goBack();
+ await viewPromise;
+
+ info("Show the subview again.");
+ viewPromise = promiseViewShown(DownloadsBlockedSubview.subview);
+ EventUtils.synthesizeMouseAtCenter(item, {});
+ await viewPromise;
+
+ info("Click the Open button.");
+ // The download should be unblocked and then opened,
+ // i.e., unblockAndOpenDownload() should be called on the item. The panel
+ // should also be closed as a result, so wait for that too.
+ let unblockPromise = promiseUnblockAndSaveCalled(item);
+ let hidePromise = promisePanelHidden();
+ // Simulate a mousemove to ensure it's not wrongly being handled by the
+ // panel as the user changing download selection.
+ EventUtils.synthesizeMouseAtCenter(
+ DownloadsBlockedSubview.elements.unblockButton,
+ { type: "mousemove" }
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ DownloadsBlockedSubview.elements.unblockButton,
+ {}
+ );
+ info("waiting for unblockOpen");
+ await unblockPromise;
+ info("waiting for hide panel");
+ await hidePromise;
+
+ window.focus();
+ await SimpleTest.promiseFocus(window);
+
+ info("Reopen the panel and show the subview again.");
+ await openPanel();
+ viewPromise = promiseViewShown(DownloadsBlockedSubview.subview);
+ EventUtils.synthesizeMouseAtCenter(item, {});
+ await viewPromise;
+
+ info("Click the Remove button.");
+ // The panel should close and the item should be removed from it.
+ hidePromise = promisePanelHidden();
+ EventUtils.synthesizeMouseAtCenter(
+ DownloadsBlockedSubview.elements.deleteButton,
+ {}
+ );
+ info("Waiting for hide panel");
+ await hidePromise;
+
+ info("Open the panel again and check the item is gone.");
+ await openPanel();
+ Assert.ok(!item.parentNode);
+
+ hidePromise = promisePanelHidden();
+ DownloadsPanel.hidePanel();
+ await hidePromise;
+ }
+
+ await task_resetState();
+});
+
+async function openPanel() {
+ // This function is insane but something intermittently causes the panel to be
+ // closed as soon as it's opening on Linux ASAN. Maybe it would also happen
+ // on other build machines if the test ran often enough. Not only is the
+ // panel closed, it's closed while it's opening, leaving DownloadsPanel._state
+ // such that when you try to open the panel again, it thinks it's already
+ // open, but it's not. The result is that the test times out.
+ //
+ // What this does is call DownloadsPanel.showPanel over and over again until
+ // the panel is really open. There are a few wrinkles:
+ //
+ // (1) When panel.state is "open", check four more times (for a total of five)
+ // before returning to make the panel stays open.
+ // (2) If the panel is not open, check the _state. It should be either
+ // kStateUninitialized or kStateHidden. If it's not, then the panel is in the
+ // process of opening -- or maybe it's stuck in that process -- so reset the
+ // _state to kStateHidden.
+ // (3) If the _state is not kStateUninitialized or kStateHidden, then it may
+ // actually be properly opening and not stuck at all. To avoid always closing
+ // the panel while it's properly opening, use an exponential backoff mechanism
+ // for retries.
+ //
+ // If all that fails, then the test will time out, but it would have timed out
+ // anyway.
+
+ await promiseFocus();
+ await new Promise(resolve => {
+ let verifyCount = 5;
+ let backoff = 0;
+ let iBackoff = 0;
+ let interval = setInterval(() => {
+ if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
+ if (verifyCount > 0) {
+ verifyCount--;
+ } else {
+ clearInterval(interval);
+ resolve();
+ }
+ } else if (iBackoff < backoff) {
+ // Keep backing off before trying again.
+ iBackoff++;
+ } else {
+ // Try (or retry) opening the panel.
+ verifyCount = 5;
+ backoff = Math.max(1, 2 * backoff);
+ iBackoff = 0;
+ if (DownloadsPanel._state != DownloadsPanel.kStateUninitialized) {
+ DownloadsPanel._state = DownloadsPanel.kStateHidden;
+ }
+ DownloadsPanel.showPanel();
+ }
+ }, 100);
+ });
+}
+
+function promisePanelHidden() {
+ return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popuphidden");
+}
+
+function makeDownload(verdict) {
+ return {
+ state: DownloadsCommon.DOWNLOAD_DIRTY,
+ hasBlockedData: true,
+ errorObj: {
+ result: Cr.NS_ERROR_FAILURE,
+ message: "Download blocked.",
+ becauseBlocked: true,
+ becauseBlockedByReputationCheck: true,
+ reputationCheckVerdict: verdict,
+ },
+ };
+}
+
+function promiseViewShown(view) {
+ return BrowserTestUtils.waitForEvent(view, "ViewShown");
+}
+
+function promiseUnblockAndSaveCalled(item) {
+ return new Promise(resolve => {
+ let realFn = item._shell.unblockAndSave;
+ item._shell.unblockAndSave = async () => {
+ item._shell.unblockAndSave = realFn;
+ resolve();
+ };
+ });
+}