/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

ChromeUtils.defineESModuleGetters(this, {
  sinon: "resource://testing-common/Sinon.sys.mjs",
});

const PROMO_DISMISSED_PREF = "browser.aboutpdf.promo.dismissed";

registerCleanupFunction(() => {
  Services.prefs.clearUserPref(PROMO_DISMISSED_PREF);
});

add_task(async function testPromoHiddenWhenPrefDismissed() {
  await SpecialPowers.pushPrefEnv({
    set: [[PROMO_DISMISSED_PREF, true]],
  });

  const tab = await openAboutPDF();
  await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    const promo = content.document.getElementById("promo");
    await ContentTaskUtils.waitForCondition(
      () => promo.hidden,
      "promo is hidden when dismissed pref is true"
    );
    ok(promo.hidden, "promo is hidden when dismissed pref is true");
  });
  BrowserTestUtils.removeTab(tab);

  await SpecialPowers.popPrefEnv();
});

add_task(async function testDismissButtonHidesPromoAndSetsPref() {
  Services.prefs.clearUserPref(PROMO_DISMISSED_PREF);

  const tab = await openAboutPDF();
  await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
    const promo = content.document.getElementById("promo");
    const dismiss = content.document.getElementById("dismiss-promo");

    // Force-show the promo so we can exercise the dismiss handler regardless
    // of platform / default-handler state.
    promo.hidden = false;

    dismiss.click();
    await ContentTaskUtils.waitForCondition(
      () => promo.hidden,
      "promo hidden after dismiss click"
    );
    ok(promo.hidden, "promo hidden after dismiss click");
  });

  await TestUtils.waitForCondition(
    () => Services.prefs.getBoolPref(PROMO_DISMISSED_PREF, false) === true,
    "dismissed pref persisted"
  );
  is(
    Services.prefs.getBoolPref(PROMO_DISMISSED_PREF, false),
    true,
    "dismissed pref persisted"
  );

  BrowserTestUtils.removeTab(tab);
  Services.prefs.clearUserPref(PROMO_DISMISSED_PREF);
});

// Open about:pdf with ShellService mocked so the promo is offered without
// touching the real OS default. setAsDefaultPDFHandler only reports that the
// attempt was made; the promo hides based on a fresh isDefaultHandlerFor check.
// `becomesDefault` models whether Firefox is the default after the attempt
// (the user confirming vs declining the macOS consent dialog): the set stub
// flips isDefaultHandlerFor accordingly, the way the real OS state would.
async function openPromoWithMockedShellService(sandbox, { becomesDefault }) {
  Services.prefs.clearUserPref(PROMO_DISMISSED_PREF);

  const { ShellService } = ChromeUtils.importESModule(
    // eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
    "moz-src:///browser/components/shell/ShellService.sys.mjs"
  );

  sandbox.stub(ShellService, "canSetAsDefaultPDFHandler").get(() => true);
  // Not the default while the promo is first shown.
  const isDefaultHandlerForStub = sandbox
    .stub(ShellService, "isDefaultHandlerFor")
    .returns(false);
  const setStub = sandbox
    .stub(ShellService, "setAsDefaultPDFHandler")
    .callsFake(async () => {
      // The OS consent completes; Firefox becomes the default iff confirmed.
      isDefaultHandlerForStub.returns(becomesDefault);
      return true;
    });

  let tab;
  try {
    tab = await openAboutPDF();
    await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
      const promo = content.document.getElementById("promo");
      await ContentTaskUtils.waitForCondition(
        () => !promo.hidden,
        "promo is shown when Firefox is not the default PDF handler"
      );
      // Make sure the button is in view for the synthesized click below.
      content.document.getElementById("set-default").scrollIntoView();
    });
  } catch (e) {
    if (tab) {
      BrowserTestUtils.removeTab(tab);
    }
    throw e;
  }

  isDefaultHandlerForStub.resetHistory();
  return { tab, setStub, isDefaultHandlerForStub };
}

// A trusted click is required: RPMSetDefaultPDFHandler enforces user
// activation, which a synthesized mouse event provides but a scripted .click()
// does not.
function clickSetDefault(tab) {
  return BrowserTestUtils.synthesizeMouseAtCenter(
    "#set-default",
    {},
    tab.linkedBrowser
  );
}

add_task(async function testSetDefaultHidesPromoWhenConfirmed() {
  const sandbox = sinon.createSandbox();
  let tab;
  let setStub;
  try {
    ({ tab, setStub } = await openPromoWithMockedShellService(sandbox, {
      becomesDefault: true,
    }));
    await clickSetDefault(tab);
    await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
      const promo = content.document.getElementById("promo");
      await ContentTaskUtils.waitForCondition(
        () => promo.hidden,
        "promo hides once the set is confirmed"
      );
      ok(promo.hidden, "promo hidden after a confirmed set");
    });
    ok(setStub.calledOnce, "Called ShellService.setAsDefaultPDFHandler once");
  } finally {
    if (tab) {
      BrowserTestUtils.removeTab(tab);
    }
    sandbox.restore();
    Services.prefs.clearUserPref(PROMO_DISMISSED_PREF);
  }
});

add_task(async function testSetDefaultKeepsPromoWhenDeclined() {
  const sandbox = sinon.createSandbox();
  let tab;
  let setStub;
  let isDefaultHandlerForStub;
  try {
    ({ tab, setStub, isDefaultHandlerForStub } =
      await openPromoWithMockedShellService(sandbox, {
        becomesDefault: false,
      }));
    await clickSetDefault(tab);
    await TestUtils.waitForCondition(
      () => setStub.calledOnce,
      "set was attempted"
    );
    await TestUtils.waitForCondition(
      () => isDefaultHandlerForStub.called,
      "promo visibility was rechecked after the declined set"
    );

    const promoHidden = await SpecialPowers.spawn(
      tab.linkedBrowser,
      [],
      async () => content.document.getElementById("promo").hidden
    );
    ok(!promoHidden, "promo stays visible after a declined set");
  } finally {
    if (tab) {
      BrowserTestUtils.removeTab(tab);
    }
    sandbox.restore();
    Services.prefs.clearUserPref(PROMO_DISMISSED_PREF);
  }
});
