/* 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";

ChromeUtils.defineESModuleGetters(this, {
  DownloadSpamProtection: "resource:///modules/DownloadSpamProtection.sys.mjs",
  PermissionTestUtils: "resource://testing-common/PermissionTestUtils.sys.mjs",
});

XPCOMUtils.defineLazyModuleGetters(this, {
  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
});

const TEST_URI = "https://example.com";

const TEST_PATH = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content",
  TEST_URI
);

add_setup(async function () {
  // Create temp directory
  let time = new Date().getTime();
  let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
  tempDir.append(time);
  Services.prefs.setIntPref("browser.download.folderList", 2);
  Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir);

  PermissionTestUtils.add(
    TEST_URI,
    "automatic-download",
    Services.perms.UNKNOWN_ACTION
  );
  await SpecialPowers.pushPrefEnv({
    set: [["browser.download.enable_spam_prevention", true]],
    clear: [
      ["browser.download.alwaysOpenPanel"],
      ["browser.download.always_ask_before_handling_new_types"],
    ],
  });

  registerCleanupFunction(async () => {
    Services.prefs.clearUserPref("browser.download.folderList");
    Services.prefs.clearUserPref("browser.download.dir");
    await IOUtils.remove(tempDir.path, { recursive: true });
  });
});

add_task(async function check_download_spam_ui() {
  await task_resetState();

  let browserWin = BrowserWindowTracker.getTopWindow();
  registerCleanupFunction(async () => {
    for (let win of [browserWin, browserWin2]) {
      win.DownloadsPanel.hidePanel();
      DownloadIntegration.downloadSpamProtection.removeDownloadSpamForWindow(
        TEST_URI,
        win
      );
    }
    let publicList = await Downloads.getList(Downloads.PUBLIC);
    await publicList.removeFinished();
    BrowserTestUtils.removeTab(newTab);
    await BrowserTestUtils.closeWindow(browserWin2);
  });
  let observedBlockedDownloads = 0;
  let gotAllBlockedDownloads = TestUtils.topicObserved(
    "blocked-automatic-download",
    () => {
      return ++observedBlockedDownloads >= 99;
    }
  );

  let newTab = await BrowserTestUtils.openNewForegroundTab(
    browserWin.gBrowser,
    TEST_PATH + "test_spammy_page.html"
  );

  await BrowserTestUtils.synthesizeMouseAtCenter(
    "body",
    {},
    newTab.linkedBrowser
  );

  info("Waiting on all blocked downloads");
  await gotAllBlockedDownloads;

  let { downloadSpamProtection } = DownloadIntegration;
  let spamList = downloadSpamProtection.getSpamListForWindow(browserWin);
  is(
    spamList._downloads[0].blockedDownloadsCount,
    99,
    "99 blocked downloads recorded"
  );
  ok(
    spamList._downloads[0].error.becauseBlockedByReputationCheck,
    "Download blocked because of reputation"
  );
  is(
    spamList._downloads[0].error.reputationCheckVerdict,
    "DownloadSpam",
    "Verdict is DownloadSpam"
  );

  browserWin.focus();
  await BrowserTestUtils.waitForPopupEvent(
    browserWin.DownloadsPanel.panel,
    "shown"
  );

  ok(browserWin.DownloadsPanel.isPanelShowing, "Download panel should open");
  await Downloads.getList(Downloads.PUBLIC);

  let listbox = browserWin.document.getElementById("downloadsListBox");
  ok(listbox, "Download list box present");

  await TestUtils.waitForCondition(() => {
    return listbox.childElementCount == 2 && !listbox.getAttribute("disabled");
  }, "2 downloads = 1 allowed download and 1 for 99 downloads blocked");

  let spamElement = listbox.itemChildren[0].classList.contains(
    "temporary-block"
  )
    ? listbox.itemChildren[0]
    : listbox.itemChildren[1];

  ok(spamElement.classList.contains("temporary-block"), "Download is blocked");

  info("Testing spam protection in a second window");

  browserWin.DownloadsPanel.hidePanel();
  DownloadIntegration.downloadSpamProtection.removeDownloadSpamForWindow(
    TEST_URI,
    browserWin
  );

  ok(
    !browserWin.DownloadsPanel.isPanelShowing,
    "Download panel should be closed in first window"
  );
  is(
    listbox.childElementCount,
    1,
    "First window's download list should have one item - the download that wasn't blocked"
  );

  let browserWin2 = await BrowserTestUtils.openNewBrowserWindow();
  let observedBlockedDownloads2 = 0;
  let gotAllBlockedDownloads2 = TestUtils.topicObserved(
    "blocked-automatic-download",
    () => {
      return ++observedBlockedDownloads2 >= 100;
    }
  );

  let newTab2 = await BrowserTestUtils.openNewForegroundTab(
    browserWin2.gBrowser,
    TEST_PATH + "test_spammy_page.html"
  );
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "body",
    {},
    newTab2.linkedBrowser
  );

  info("Waiting on all blocked downloads in second window");
  await gotAllBlockedDownloads2;

  let spamList2 = downloadSpamProtection.getSpamListForWindow(browserWin2);
  is(
    spamList2._downloads[0].blockedDownloadsCount,
    100,
    "100 blocked downloads recorded in second window"
  );
  ok(
    !spamList._downloads[0]?.blockedDownloadsCount,
    "No blocked downloads in first window"
  );

  browserWin2.focus();
  await BrowserTestUtils.waitForPopupEvent(
    browserWin2.DownloadsPanel.panel,
    "shown"
  );

  ok(
    browserWin2.DownloadsPanel.isPanelShowing,
    "Download panel should open in second window"
  );

  ok(
    !browserWin.DownloadsPanel.isPanelShowing,
    "Download panel should not open in first window"
  );

  let listbox2 = browserWin2.document.getElementById("downloadsListBox");
  ok(listbox2, "Download list box present");

  await TestUtils.waitForCondition(() => {
    return (
      listbox2.childElementCount == 2 && !listbox2.getAttribute("disabled")
    );
  }, "2 downloads = 1 allowed download from first window, and 1 for 100 downloads blocked in second window");

  is(
    listbox.childElementCount,
    1,
    "First window's download list should still have one item - the download that wasn't blocked"
  );

  let spamElement2 = listbox2.itemChildren[0].classList.contains(
    "temporary-block"
  )
    ? listbox2.itemChildren[0]
    : listbox2.itemChildren[1];

  ok(spamElement2.classList.contains("temporary-block"), "Download is blocked");
});
