"use strict";

const { PermissionTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/PermissionTestUtils.sys.mjs"
);

const ROOT = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content/",
  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
  "http://example.com/"
);
let pageWithAlert = ROOT + "openPromptOffTimeout.html";

registerCleanupFunction(function () {
  Services.perms.removeAll();
});

/*
 * This test opens a tab that alerts when it is hidden. We then switch away
 * from the tab, and check that by default the tab is not automatically
 * re-selected. We also check that a checkbox appears in the alert that allows
 * the user to enable this automatically re-selecting. We then check that
 * checking the checkbox does actually enable that behaviour.
 */
add_task(async function test_old_modal_ui() {
  // We're intentionally testing the old modal mechanism, so disable the new one.
  await SpecialPowers.pushPrefEnv({
    set: [["prompts.contentPromptSubDialog", false]],
  });

  let firstTab = gBrowser.selectedTab;
  // load page that opens prompt when page is hidden
  let openedTab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    pageWithAlert,
    true
  );
  let openedTabGotAttentionPromise = BrowserTestUtils.waitForAttribute(
    "attention",
    openedTab
  );
  // switch away from that tab again - this triggers the alert.
  await BrowserTestUtils.switchTab(gBrowser, firstTab);
  // ... but that's async on e10s...
  await openedTabGotAttentionPromise;
  // check for attention attribute
  is(
    openedTab.hasAttribute("attention"),
    true,
    "Tab with alert should have 'attention' attribute."
  );
  ok(!openedTab.selected, "Tab with alert should not be selected");

  // switch tab back, and check the checkbox is displayed:
  await BrowserTestUtils.switchTab(gBrowser, openedTab);
  // check the prompt is there, and the extra row is present
  let promptElements =
    openedTab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt");
  is(promptElements.length, 1, "There should be 1 prompt");
  let ourPromptElement = promptElements[0];
  let checkbox = ourPromptElement.querySelector(
    "checkbox[label*='example.com']"
  );
  ok(checkbox, "The checkbox should be there");
  ok(!checkbox.checked, "Checkbox shouldn't be checked");
  // tick box and accept dialog
  checkbox.checked = true;
  let ourPrompt =
    openedTab.linkedBrowser.tabModalPromptBox.getPrompt(ourPromptElement);
  ourPrompt.onButtonClick(0);
  // Wait for that click to actually be handled completely.
  await new Promise(function (resolve) {
    Services.tm.dispatchToMainThread(resolve);
  });
  // check permission is set
  is(
    Services.perms.ALLOW_ACTION,
    PermissionTestUtils.testPermission(pageWithAlert, "focus-tab-by-prompt"),
    "Tab switching should now be allowed"
  );

  // Check if the control center shows the correct permission.
  let shown = BrowserTestUtils.waitForEvent(
    window,
    "popupshown",
    true,
    event => event.target == gPermissionPanel._permissionPopup
  );
  gPermissionPanel._identityPermissionBox.click();
  await shown;
  let labelText = SitePermissions.getPermissionLabel("focus-tab-by-prompt");
  let permissionsList = document.getElementById(
    "permission-popup-permission-list"
  );
  let label = permissionsList.querySelector(
    ".permission-popup-permission-label"
  );
  is(label.textContent, labelText);
  gPermissionPanel._permissionPopup.hidePopup();

  // Check if the identity icon signals granted permission.
  ok(
    gPermissionPanel._identityPermissionBox.hasAttribute("hasPermissions"),
    "identity-box signals granted permissions"
  );

  let openedTabSelectedPromise = BrowserTestUtils.waitForAttribute(
    "selected",
    openedTab,
    "true"
  );
  // switch to other tab again
  await BrowserTestUtils.switchTab(gBrowser, firstTab);

  // This is sync in non-e10s, but in e10s we need to wait for this, so yield anyway.
  // Note that the switchTab promise doesn't actually guarantee anything about *which*
  // tab ends up as selected when its event fires, so using that here wouldn't work.
  await openedTabSelectedPromise;
  // should be switched back
  ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!");

  // In e10s, with the conformant promise scheduling, we have to wait for next tick
  // to ensure that the prompt is open before removing the opened tab, because the
  // promise callback of 'openedTabSelectedPromise' could be done at the middle of
  // RemotePrompt.openTabPrompt() while 'DOMModalDialogClosed' event is fired.
  await TestUtils.waitForTick();

  BrowserTestUtils.removeTab(openedTab);
});

add_task(async function test_new_modal_ui() {
  // We're intentionally testing the new modal mechanism, so make sure it's enabled.
  await SpecialPowers.pushPrefEnv({
    set: [["prompts.contentPromptSubDialog", true]],
  });

  // Make sure we clear the focus tab permission set in the previous test
  PermissionTestUtils.remove(pageWithAlert, "focus-tab-by-prompt");

  let firstTab = gBrowser.selectedTab;
  // load page that opens prompt when page is hidden
  let openedTab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    pageWithAlert,
    true
  );
  let openedTabGotAttentionPromise = BrowserTestUtils.waitForAttribute(
    "attention",
    openedTab
  );
  // switch away from that tab again - this triggers the alert.
  await BrowserTestUtils.switchTab(gBrowser, firstTab);
  // ... but that's async on e10s...
  await openedTabGotAttentionPromise;
  // check for attention attribute
  is(
    openedTab.hasAttribute("attention"),
    true,
    "Tab with alert should have 'attention' attribute."
  );
  ok(!openedTab.selected, "Tab with alert should not be selected");

  // switch tab back, and check the checkbox is displayed:
  await BrowserTestUtils.switchTab(gBrowser, openedTab);
  // check the prompt is there
  let promptElements = openedTab.linkedBrowser.parentNode.querySelectorAll(
    ".content-prompt-dialog"
  );

  let dialogBox = gBrowser.getTabDialogBox(openedTab.linkedBrowser);
  let contentPromptManager = dialogBox.getContentDialogManager();
  is(promptElements.length, 1, "There should be 1 prompt");
  is(
    contentPromptManager._dialogs.length,
    1,
    "Content prompt manager should have 1 dialog box."
  );

  // make sure the checkbox appears and that the permission for allowing tab switching
  // is set when the checkbox is tickted and the dialog is accepted
  let dialog = contentPromptManager._dialogs[0];

  await dialog._dialogReady;

  let dialogDoc = dialog._frame.contentWindow.document;
  let checkbox = dialogDoc.querySelector("checkbox[label*='example.com']");
  let button = dialogDoc.querySelector("#commonDialog").getButton("accept");

  ok(checkbox, "The checkbox should be there");
  ok(!checkbox.checked, "Checkbox shouldn't be checked");

  // tick box and accept dialog
  checkbox.checked = true;
  button.click();
  // Wait for that click to actually be handled completely.
  await new Promise(function (resolve) {
    Services.tm.dispatchToMainThread(resolve);
  });

  ok(!contentPromptManager._dialogs.length, "Dialog should be closed");

  // check permission is set
  is(
    Services.perms.ALLOW_ACTION,
    PermissionTestUtils.testPermission(pageWithAlert, "focus-tab-by-prompt"),
    "Tab switching should now be allowed"
  );

  // Check if the control center shows the correct permission.
  let shown = BrowserTestUtils.waitForEvent(
    window,
    "popupshown",
    true,
    event => event.target == gPermissionPanel._permissionPopup
  );
  gPermissionPanel._identityPermissionBox.click();
  await shown;
  let labelText = SitePermissions.getPermissionLabel("focus-tab-by-prompt");
  let permissionsList = document.getElementById(
    "permission-popup-permission-list"
  );
  let label = permissionsList.querySelector(
    ".permission-popup-permission-label"
  );
  is(label.textContent, labelText);
  gPermissionPanel.hidePopup();

  // Check if the identity icon signals granted permission.
  ok(
    gPermissionPanel._identityPermissionBox.hasAttribute("hasPermissions"),
    "identity-permission-box signals granted permissions"
  );

  let openedTabSelectedPromise = BrowserTestUtils.waitForAttribute(
    "selected",
    openedTab,
    "true"
  );

  // switch to other tab again
  await BrowserTestUtils.switchTab(gBrowser, firstTab);

  // This is sync in non-e10s, but in e10s we need to wait for this, so yield anyway.
  // Note that the switchTab promise doesn't actually guarantee anything about *which*
  // tab ends up as selected when its event fires, so using that here wouldn't work.
  await openedTabSelectedPromise;

  ok(contentPromptManager._dialogs.length === 1, "Dialog opened.");
  dialog = contentPromptManager._dialogs[0];
  await dialog._dialogReady;

  // should be switched back
  ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!");

  // In e10s, with the conformant promise scheduling, we have to wait for next tick
  // to ensure that the prompt is open before removing the opened tab, because the
  // promise callback of 'openedTabSelectedPromise' could be done at the middle of
  // RemotePrompt.openTabPrompt() while 'DOMModalDialogClosed' event is fired.
  // await TestUtils.waitForTick();

  await BrowserTestUtils.removeTab(openedTab);
});
