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

"use strict";

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

const { TabStateFlusher } = ChromeUtils.importESModule(
  "resource:///modules/sessionstore/TabStateFlusher.sys.mjs"
);

const TEST_ROOT = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content",
  "https://example.com"
);

async function addTab(url) {
  let tab = await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    url,
    animate: false,
  });
  return tab;
}

async function addBeforeUnloadTab() {
  let tab = await addTab(TEST_ROOT + "file_beforeunload_stop.html");
  await BrowserTestUtils.synthesizeMouseAtCenter(
    "body",
    {},
    tab.linkedBrowser.browsingContext
  );
  Assert.ok(
    tab.linkedBrowser.hasBeforeUnload,
    "Added tab has a beforeUnload prompt"
  );
  return tab;
}

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [["test.wait300msAfterTabSwitch", true]],
  });
});

/**
 * When closing a group with a tab prompt and accepting the prompt,
 * the group should close.
 */
add_task(async function test_closeGroupAndAcceptPrompt() {
  await SimpleTest.requestCompleteLog();
  let tab1 = await addTab("about:mozilla");
  let tab2 = await addBeforeUnloadTab();
  let group = gBrowser.addTabGroup([tab1, tab2]);

  let promptHandled = PromptTestUtils.handleNextPrompt(
    window,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 0, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );

  let groupRemoved = BrowserTestUtils.waitForEvent(group, "TabGroupRemoved");

  gBrowser.removeTabGroup(group);
  await Promise.allSettled([promptHandled, groupRemoved]);

  Assert.ok(!gBrowser.getAllTabGroups().length, "Tab group was removed");
});

/**
 * When closing a group with a tab prompt and rejecting the prompt,
 * all tabs in the group should remain open.
 */
add_task(async function test_closeGroupAndRejectPrompt() {
  let tab1 = await addTab("about:mozilla");
  let tab2 = await addBeforeUnloadTab();
  let groupCreated = BrowserTestUtils.waitForEvent(window, "TabGroupCreate");
  let group = gBrowser.addTabGroup([tab1, tab2]);
  await groupCreated;
  let promptHandled = PromptTestUtils.handleNextPrompt(
    tab2.linkedBrowser,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 1, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );

  gBrowser.removeTabGroup(group);
  await promptHandled;

  Assert.equal(gBrowser.getAllTabGroups().length, 1, "Tab group is still open");
  Assert.equal(group.tabs.length, 2, "Both tabs in group remain open");
  let groupRemoved = BrowserTestUtils.waitForEvent(group, "TabGroupRemoved");
  gBrowser.removeTabGroup(group, { skipPermitUnload: true });
  await groupRemoved;
});

/**
 * When closing a group with multiple tab prompts and rejecting ANY of them,
 * all tabs in the group should remain open.
 */
add_task(async function test_closeGroupAndRejectAnyPrompt() {
  // maybe disable this test and try running a11y tests
  let tab1 = await addBeforeUnloadTab();
  let tab2 = await addBeforeUnloadTab();
  let group = gBrowser.addTabGroup([tab1, tab2]);

  let firstPromptHandled = PromptTestUtils.handleNextPrompt(
    tab1.linkedBrowser,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 0, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );

  let secondPromptHandled = PromptTestUtils.handleNextPrompt(
    tab2.linkedBrowser,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 1, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );

  let groupRemoved = gBrowser.removeTabGroup(group);
  await Promise.race([firstPromptHandled, secondPromptHandled]);
  await groupRemoved;

  Assert.equal(gBrowser.getAllTabGroups().length, 1, "Tab group is still open");
  Assert.equal(group.tabs.length, 2, "Both tabs in group remain open");
  groupRemoved = BrowserTestUtils.waitForEvent(group, "TabGroupRemoved");
  gBrowser.removeTabGroup(group, { skipPermitUnload: true });
  await groupRemoved;
});

/**
 * When a bunch of tabs are closed, e.g. when using "close tabs to the right",
 * any whole tabgroups with beforeunload handlers should be kept open if
 * cancelled by the user.
 */
add_task(async function test_closeMultipleTabsAndRejectFromGroup() {
  let group1Tab1 = await addTab("about:mozilla");
  let group1Tab2 = await addBeforeUnloadTab();
  let group2Tab1 = await addBeforeUnloadTab();
  let group2Tab2 = await addTab("about:mozilla");
  let group1 = gBrowser.addTabGroup([group1Tab1, group1Tab2], {
    id: "leave-open",
    label: "leave open",
  });
  gBrowser.addTabGroup([group2Tab1, group2Tab2], {
    id: "close",
    label: "close",
  });

  let firstPromptHandled = PromptTestUtils.handleNextPrompt(
    group1Tab2.linkedBrowser,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 1, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );

  let secondPromptHandled = PromptTestUtils.handleNextPrompt(
    group2Tab1.linkedBrowser,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 0, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );

  gBrowser.removeTabsToTheEndFrom(gBrowser.tabs[0]);
  await Promise.allSettled([firstPromptHandled, secondPromptHandled]);

  // initial tab and 1 tab group should remain open
  await BrowserTestUtils.waitForCondition(
    () => gBrowser.tabs.length == 3,
    "3 tabs remain open"
  );
  Assert.equal(
    gBrowser.getAllTabGroups()[0].id,
    group1.id,
    "Group 1 is still open"
  );

  await gBrowser.removeTabGroup(group1, { skipPermitUnload: true });
});

/**
 * When a tab group is saved & closed, it should not end up saved if the user
 * cancels the group close.
 */
add_task(async function test_cancelClosingASavingGroup() {
  let tab1 = await addBeforeUnloadTab();
  let tab2 = await addTab("about:mozilla");
  await TabStateFlusher.flush(tab1.linkedBrowser);
  let group = gBrowser.addTabGroup([tab1, tab2]);
  group.save();
  Assert.equal(
    SessionStore.getCurrentState().savedGroups.length,
    1,
    "Group is saved"
  );

  let promptHandled = PromptTestUtils.handleNextPrompt(
    tab1.linkedBrowser,
    {
      modalType: Services.prompt.MODAL_TYPE_CONTENT,
    },
    {
      buttonNumClick: 1, // 0 = leave page (accept), 1= stay on page (cancel)
    }
  );
  let groupRemoved = gBrowser.removeTabGroup(group);
  await Promise.allSettled([promptHandled, groupRemoved]);
  Assert.equal(gBrowser.getAllTabGroups().length, 1, "Group is still open");
  Assert.ok(
    !SessionStore.getCurrentState().savedGroups.length,
    "Group is no longer saved after close was canceled"
  );
  await gBrowser.removeTabGroup(group, { skipPermitUnload: true });
});
