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

const { SessionStoreTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/SessionStoreTestUtils.sys.mjs"
);
const { TabStateFlusher } = ChromeUtils.importESModule(
  "resource:///modules/sessionstore/TabStateFlusher.sys.mjs"
);
const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;

SessionStoreTestUtils.init(this, window);

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

let panelMenuWidgetAdded = false;
function prepareHistoryPanel() {
  if (panelMenuWidgetAdded) {
    return;
  }
  CustomizableUI.addWidgetToArea(
    "history-panelmenu",
    CustomizableUI.AREA_FIXED_OVERFLOW_PANEL
  );
  registerCleanupFunction(() => CustomizableUI.reset());
}

async function openRecentlyClosedTabsMenu(doc = window.document) {
  prepareHistoryPanel();
  await openHistoryPanel(doc);

  let recentlyClosedTabs = doc.getElementById("appMenuRecentlyClosedTabs");
  Assert.ok(
    !recentlyClosedTabs.getAttribute("disabled"),
    "Recently closed tabs button enabled"
  );
  let closeTabsPanel = doc.getElementById("appMenu-library-recentlyClosedTabs");
  let panelView = closeTabsPanel && PanelView.forNode(closeTabsPanel);
  if (!panelView?.active) {
    recentlyClosedTabs.click();
    closeTabsPanel = doc.getElementById("appMenu-library-recentlyClosedTabs");
    await BrowserTestUtils.waitForEvent(closeTabsPanel, "ViewShown");
    ok(
      PanelView.forNode(closeTabsPanel)?.active,
      "Opened 'Recently closed tabs' panel"
    );
  }

  return closeTabsPanel;
}

function makeTabState(url) {
  return {
    entries: [{ url, triggeringPrincipal_base64 }],
  };
}

function makeClosedTabState(url, { groupId, closedInTabGroup = false } = {}) {
  return {
    title: url,
    closedInTabGroupId: closedInTabGroup ? groupId : null,
    state: {
      entries: [
        {
          url,
          triggeringPrincipal_base64,
        },
      ],
      groupId,
    },
  };
}

function resetClosedTabsAndWindows() {
  // Clear the lists of closed windows and tabs.
  Services.obs.notifyObservers(null, "browser:purge-session-history");
  is(SessionStore.getClosedWindowCount(), 0, "Expect 0 closed windows");
  for (const win of BrowserWindowTracker.orderedWindows) {
    is(
      SessionStore.getClosedTabCountForWindow(win),
      0,
      "Expect 0 closed tabs for this window"
    );
  }
}

registerCleanupFunction(async () => {
  await resetClosedTabsAndWindows();
});

add_task(async function testRecentlyClosedDisabled() {
  info("Check history recently closed tabs/windows section");

  prepareHistoryPanel();
  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);

  await openHistoryPanel();

  let recentlyClosedTabs = document.getElementById("appMenuRecentlyClosedTabs");
  let recentlyClosedWindows = document.getElementById(
    "appMenuRecentlyClosedWindows"
  );

  // Wait for the disabled attribute to change, as we receive
  // the "viewshown" event before this changes
  await BrowserTestUtils.waitForCondition(
    () => recentlyClosedTabs.hasAttribute("disabled"),
    "Waiting for button to become disabled"
  );
  Assert.ok(
    recentlyClosedTabs.hasAttribute("disabled"),
    "Recently closed tabs button disabled"
  );
  Assert.ok(
    recentlyClosedWindows.hasAttribute("disabled"),
    "Recently closed windows button disabled"
  );

  await hideHistoryPanel();

  gBrowser.selectedTab.focus();
  await SessionStoreTestUtils.openAndCloseTab(
    window,
    TEST_PATH + "dummy_history_item.html"
  );

  await openHistoryPanel();

  await BrowserTestUtils.waitForCondition(
    () => !recentlyClosedTabs.hasAttribute("disabled"),
    "Waiting for button to be enabled"
  );
  Assert.ok(
    !recentlyClosedTabs.hasAttribute("disabled"),
    "Recently closed tabs is available"
  );
  Assert.ok(
    recentlyClosedWindows.hasAttribute("disabled"),
    "Recently closed windows button disabled"
  );

  await hideHistoryPanel();

  let newWin = await BrowserTestUtils.openNewBrowserWindow();
  let loadedPromise = BrowserTestUtils.browserLoaded(
    newWin.gBrowser.selectedBrowser
  );
  BrowserTestUtils.startLoadingURIString(
    newWin.gBrowser.selectedBrowser,
    "about:mozilla"
  );
  await loadedPromise;
  await BrowserTestUtils.closeWindow(newWin);

  await openHistoryPanel();

  await BrowserTestUtils.waitForCondition(
    () => !recentlyClosedWindows.hasAttribute("disabled"),
    "Waiting for button to be enabled"
  );
  Assert.ok(
    !recentlyClosedTabs.hasAttribute("disabled"),
    "Recently closed tabs is available"
  );
  Assert.ok(
    !recentlyClosedWindows.hasAttribute("disabled"),
    "Recently closed windows is available"
  );

  await hideHistoryPanel();
});

add_task(async function testRecentlyClosedTabsDisabledPersists() {
  info("Check history recently closed tabs/windows section");

  prepareHistoryPanel();

  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);

  await openHistoryPanel();

  let recentlyClosedTabs = document.getElementById("appMenuRecentlyClosedTabs");
  Assert.ok(
    recentlyClosedTabs.hasAttribute("disabled"),
    "Recently closed tabs button disabled"
  );

  await hideHistoryPanel();

  let newWin = await BrowserTestUtils.openNewBrowserWindow();

  await openHistoryPanel(newWin.document);
  recentlyClosedTabs = newWin.document.getElementById(
    "appMenuRecentlyClosedTabs"
  );
  Assert.ok(
    recentlyClosedTabs.hasAttribute("disabled"),
    "Recently closed tabs is disabled"
  );

  // We close the window without hiding the panel first, which used to interfere
  // with populating the view subsequently.
  await BrowserTestUtils.closeWindow(newWin);

  newWin = await BrowserTestUtils.openNewBrowserWindow();
  await openHistoryPanel(newWin.document);
  recentlyClosedTabs = newWin.document.getElementById(
    "appMenuRecentlyClosedTabs"
  );
  Assert.ok(
    recentlyClosedTabs.hasAttribute("disabled"),
    "Recently closed tabs is disabled"
  );
  await hideHistoryPanel(newWin.document);
  await BrowserTestUtils.closeWindow(newWin);
});

add_task(async function testRecentlyClosedRestoreAllTabs() {
  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);
  await resetClosedTabsAndWindows();
  const initialTabCount = gBrowser.visibleTabs.length;

  const closedTabUrls = [
    "about:robots",
    "https://example.com/",
    "https://example.org/",
  ];

  const closedTabGroupInOpenWindowUrls = ["about:logo", "about:logo"];
  const closedTabGroupInOpenWindowId = "1234567890-1";

  const closedTabGroupInClosedWindowUrls = ["about:robots", "about:robots"];
  const closedTabGroupInClosedWindowId = "1234567890-2";

  await SessionStoreTestUtils.promiseBrowserState({
    windows: [
      {
        tabs: [makeTabState("about:mozilla")],
        _closedTabs: closedTabUrls.map(makeClosedTabState),
        closedGroups: [
          {
            collapsed: false,
            color: "blue",
            id: closedTabGroupInOpenWindowId,
            name: "closed-in-open-window",
            tabs: closedTabGroupInOpenWindowUrls.map(url =>
              makeClosedTabState(url, { groupId: closedTabGroupInOpenWindowId })
            ),
          },
        ],
      },
    ],
    _closedWindows: [
      {
        tabs: [makeTabState("about:mozilla")],
        _closedTabs: [],
        closedGroups: [
          {
            collapsed: false,
            color: "red",
            id: closedTabGroupInClosedWindowId,
            name: "closed-in-closed-window",
            tabs: closedTabGroupInClosedWindowUrls.map(url =>
              makeClosedTabState(url, {
                groupId: closedTabGroupInClosedWindowId,
              })
            ),
          },
        ],
      },
    ],
  });

  is(gBrowser.visibleTabs.length, 1, "We start with one tab open");
  // Open the "Recently closed tabs" panel.
  let closeTabsPanel = await openRecentlyClosedTabsMenu();

  // Click the first toolbar button in the panel.
  let toolbarButton = closeTabsPanel.querySelector(
    ".panel-subview-body toolbarbutton"
  );
  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window);

  info(
    "We should reopen the first of closedTabUrls: " +
      JSON.stringify(closedTabUrls)
  );
  let reopenedTab = await newTabPromise;
  is(
    reopenedTab.linkedBrowser.currentURI.spec,
    closedTabUrls[0],
    "Opened the first URL"
  );
  info(`restored tab, total open tabs: ${gBrowser.tabs.length}`);

  info("waiting for closeTab");
  await SessionStoreTestUtils.closeTab(reopenedTab);

  await openRecentlyClosedTabsMenu();
  let restoreAllItem = closeTabsPanel.querySelector(".restoreallitem");
  ok(
    restoreAllItem && !restoreAllItem.hidden,
    "Restore all menu item is not hidden"
  );

  // Click the restore-all toolbar button in the panel.
  EventUtils.sendMouseEvent({ type: "click" }, restoreAllItem, window);

  info("waiting for restored tabs");
  await BrowserTestUtils.waitForCondition(
    () => SessionStore.getClosedTabCount() === 0,
    "Waiting for all the closed tabs to be opened"
  );

  is(
    gBrowser.tabs.length,
    initialTabCount +
      closedTabUrls.length +
      closedTabGroupInOpenWindowUrls.length +
      closedTabGroupInClosedWindowUrls.length,
    "The expected number of closed tabs were restored"
  );
  is(
    gBrowser.tabGroups[0].id,
    closedTabGroupInOpenWindowId,
    "Closed tab group in open window was restored"
  );
  closedTabGroupInOpenWindowUrls.forEach((expectedUrl, index) => {
    is(
      gBrowser.tabGroups[0].tabs[index].linkedBrowser.currentURI.spec,
      expectedUrl,
      `Closed tab group in open window tab #${index} has correct URL`
    );
  });
  is(
    gBrowser.tabGroups[1].id,
    closedTabGroupInClosedWindowId,
    "Closed tab group in closed window was restored"
  );
  closedTabGroupInClosedWindowUrls.forEach((expectedUrl, index) => {
    is(
      gBrowser.tabGroups[1].tabs[index].linkedBrowser.currentURI.spec,
      expectedUrl,
      `Closed tab group in closed window tab #${index} has correct URL`
    );
  });

  // clean up extra tabs
  while (gBrowser.tabs.length > 1) {
    BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
  }
});

add_task(async function testRecentlyClosedWindows() {
  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);
  await resetClosedTabsAndWindows();

  // Open and close a new window.
  let newWin = await BrowserTestUtils.openNewBrowserWindow();
  let loadedPromise = BrowserTestUtils.browserLoaded(
    newWin.gBrowser.selectedBrowser
  );
  BrowserTestUtils.startLoadingURIString(
    newWin.gBrowser.selectedBrowser,
    "https://example.com"
  );
  await loadedPromise;
  let closedObjectsChangePromise = TestUtils.topicObserved(
    "sessionstore-closed-objects-changed"
  );
  await BrowserTestUtils.closeWindow(newWin);
  await closedObjectsChangePromise;

  prepareHistoryPanel();
  await openHistoryPanel();

  // Open the "Recently closed windows" panel.
  document.getElementById("appMenuRecentlyClosedWindows").click();

  let winPanel = document.getElementById(
    "appMenu-library-recentlyClosedWindows"
  );
  await BrowserTestUtils.waitForEvent(winPanel, "ViewShown");
  ok(true, "Opened 'Recently closed windows' panel");

  // Click the first toolbar button in the panel.
  let panelBody = winPanel.querySelector(".panel-subview-body");
  let toolbarButton = panelBody.querySelector("toolbarbutton");
  let newWindowPromise = BrowserTestUtils.waitForNewWindow({
    url: "https://example.com/",
  });
  closedObjectsChangePromise = TestUtils.topicObserved(
    "sessionstore-closed-objects-changed"
  );
  EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window);

  newWin = await newWindowPromise;
  await closedObjectsChangePromise;
  is(gBrowser.tabs.length, 1, "Did not open new tabs");

  await BrowserTestUtils.closeWindow(newWin);
});

add_task(async function testRecentlyClosedTabsFromClosedWindows() {
  await resetClosedTabsAndWindows();
  const closedTabUrls = [
    "about:robots",
    "https://example.com/",
    "https://example.org/",
  ];
  const closedWindowState = {
    tabs: [
      {
        entries: [{ url: "about:mozilla", triggeringPrincipal_base64 }],
      },
    ],
    closedGroups: [],
    _closedTabs: closedTabUrls.map(url => {
      return {
        title: url,
        state: {
          entries: [
            {
              url,
              triggeringPrincipal_base64,
            },
          ],
        },
      };
    }),
  };
  await SessionStoreTestUtils.promiseBrowserState({
    windows: [
      {
        tabs: [
          {
            entries: [{ url: "about:mozilla", triggeringPrincipal_base64 }],
          },
        ],
      },
    ],
    _closedWindows: [closedWindowState],
  });
  Assert.equal(
    SessionStore.getClosedTabCountFromClosedWindows(),
    closedTabUrls.length,
    "Sanity check number of closed tabs from closed windows"
  );

  prepareHistoryPanel();
  let closeTabsPanel = await openRecentlyClosedTabsMenu();
  // make sure we can actually restore one of these closed tabs
  const closedTabItems = closeTabsPanel.querySelectorAll(
    "toolbarbutton[targetURI]"
  );
  Assert.equal(
    closedTabItems.length,
    closedTabUrls.length,
    "We have expected number of closed tab items"
  );

  const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  const closedObjectsChangePromise = TestUtils.topicObserved(
    "sessionstore-closed-objects-changed"
  );
  EventUtils.sendMouseEvent({ type: "click" }, closedTabItems[0], window);
  await newTabPromise;
  await closedObjectsChangePromise;

  // flip the pref so none of the closed tabs from closed window are included
  await SpecialPowers.pushPrefEnv({
    set: [["browser.sessionstore.closedTabsFromClosedWindows", false]],
  });
  await openHistoryPanel();

  // verify the recently-closed-tabs menu item is disabled
  let recentlyClosedTabsItem = document.getElementById(
    "appMenuRecentlyClosedTabs"
  );
  Assert.ok(
    recentlyClosedTabsItem.hasAttribute("disabled"),
    "Recently closed tabs button is now disabled"
  );
  await hideHistoryPanel();

  SpecialPowers.popPrefEnv();
  while (gBrowser.tabs.length > 1) {
    await SessionStoreTestUtils.closeTab(
      gBrowser.tabs[gBrowser.tabs.length - 1]
    );
  }
});

add_task(async function testRecentlyClosedTabGroupsSingleTab() {
  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);
  await resetClosedTabsAndWindows();
  prepareHistoryPanel();

  is(gBrowser.visibleTabs.length, 1, "We start with one tab already open");

  let aboutMozillaTab = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
  let aboutLogoTab = BrowserTestUtils.addTab(gBrowser, "about:logo");
  let mozillaTabGroup = gBrowser.addTabGroup([aboutMozillaTab, aboutLogoTab], {
    color: "red",
    label: "mozilla stuff",
  });
  const mozillaTabGroupId = mozillaTabGroup.id;
  const mozillaTabGroupName = mozillaTabGroup.label;
  let aboutRobotsTab = BrowserTestUtils.addTab(gBrowser, "about:robots");

  info("load all of the tabs");
  await Promise.all(
    [aboutMozillaTab, aboutLogoTab, aboutRobotsTab].map(async t => {
      await BrowserTestUtils.browserLoaded(t.linkedBrowser);
      await TabStateFlusher.flush(t.linkedBrowser);
    })
  );

  info("close the tab group and wait for it to be removed");
  let removePromise = BrowserTestUtils.waitForEvent(
    mozillaTabGroup,
    "TabGroupRemoved"
  );
  gBrowser.removeTabGroup(mozillaTabGroup);
  await removePromise;

  is(
    gBrowser.visibleTabs.length,
    2,
    "The tab from before the test plus about:robots should still be open"
  );
  info("Open the 'Recently closed tabs' panel.");
  let closeTabsPanel = await openRecentlyClosedTabsMenu();

  // Click the tab group button in the panel.
  let tabGroupToolbarButton = closeTabsPanel.querySelector(
    `.panel-subview-body toolbarbutton[label="${mozillaTabGroupName}"]`
  );
  ok(tabGroupToolbarButton, "should find the tab group toolbar button");

  let tabGroupPanelview = document.getElementById(
    `closed-tabs-tab-group-${mozillaTabGroupId}`
  );
  ok(tabGroupPanelview, "should find the tab group panelview");

  EventUtils.sendMouseEvent({ type: "click" }, tabGroupToolbarButton, window);
  await BrowserTestUtils.waitForEvent(tabGroupPanelview, "ViewShown");

  info("restore the first tab from the closed tab group");
  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  let tabToolbarButton = tabGroupPanelview.querySelector(
    ".panel-subview-body toolbarbutton"
  );
  ok(tabToolbarButton, "should find at least one tab to restore");
  let tabTitle = tabToolbarButton.label;
  EventUtils.sendMouseEvent({ type: "click" }, tabToolbarButton, window);

  let reopenedTab = await newTabPromise;
  is(reopenedTab.label, tabTitle, "Opened the first URL");
  info(`restored tab, total open tabs: ${gBrowser.tabs.length}`);

  // clean up extra tabs
  while (gBrowser.tabs.length > 1) {
    BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
  }
});

add_task(async function testRecentlyClosedTabGroupOpensFromAnyWindow() {
  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);
  await resetClosedTabsAndWindows();
  prepareHistoryPanel();

  is(gBrowser.visibleTabs.length, 1, "We start with one tab already open");

  let groupedTab = BrowserTestUtils.addTab(gBrowser, "https://example.com");
  await BrowserTestUtils.browserLoaded(groupedTab.linkedBrowser);
  await TabStateFlusher.flush(groupedTab.linkedBrowser);
  let tabGroup = gBrowser.addTabGroup([groupedTab], { label: "Some group" });
  const tabGroupId = tabGroup.id;
  const tabGroupLabel = tabGroup.label;

  info("close the tab group and wait for it to be removed");
  let removePromise = BrowserTestUtils.waitForEvent(
    tabGroup,
    "TabGroupRemoved"
  );
  gBrowser.removeTabGroup(tabGroup);
  await removePromise;

  let newWin = await BrowserTestUtils.openNewBrowserWindow();
  let closeTabsPanel = await openRecentlyClosedTabsMenu(newWin.document);

  // Click the tab group button in the panel.
  let tabGroupToolbarButton = closeTabsPanel.querySelector(
    `.panel-subview-body toolbarbutton[label="${tabGroupLabel}"]`
  );
  ok(tabGroupToolbarButton, "should find the tab group toolbar button");

  let tabGroupPanelview = newWin.document.getElementById(
    `closed-tabs-tab-group-${tabGroupId}`
  );
  ok(tabGroupPanelview, "should find the tab group panelview");

  EventUtils.sendMouseEvent({ type: "click" }, tabGroupToolbarButton, window);
  await BrowserTestUtils.waitForEvent(tabGroupPanelview, "ViewShown");

  let tabToolbarButton = tabGroupPanelview.querySelector(
    "toolbarbutton.reopentabgroupitem"
  );
  let tabGroupRestored = BrowserTestUtils.waitForEvent(
    newWin.gBrowser.tabContainer,
    "SSTabRestored"
  );
  EventUtils.sendMouseEvent({ type: "click" }, tabToolbarButton, window);
  await tabGroupRestored;

  is(
    newWin.gBrowser.tabGroups.length,
    1,
    "Tab group added to new window tab strip"
  );
  is(
    newWin.gBrowser.tabGroups[0].label,
    tabGroupLabel,
    "Tab group label is the same as it was before restore"
  );

  await BrowserTestUtils.closeWindow(newWin);
});

add_task(async function testRecentlyClosedTabsFromManyWindows() {
  // Ensures that bug1943850 has a proper resolution.
  info(
    "Tabs must be indexed by individual window, even when multiple windows are open"
  );

  await resetClosedTabsAndWindows();
  const ORIG_STATE = SessionStore.getBrowserState();

  await SessionStoreTestUtils.promiseBrowserState({
    windows: [
      {
        tabs: [makeTabState("about:mozilla")],
        _closedTabs: [
          makeClosedTabState("about:mozilla"),
          makeClosedTabState("about:mozilla"),
        ],
      },
      {
        tabs: [makeTabState("about:mozilla")],
        _closedTabs: [makeClosedTabState("about:robots")],
      },
    ],
  });

  Assert.equal(
    SessionStore.getClosedTabCount(),
    3,
    "Sanity check number of closed tabs from closed windows"
  );

  prepareHistoryPanel();
  let closeTabsPanel = await openRecentlyClosedTabsMenu();

  info("make sure we can actually restore one of these closed tabs");
  const closedTabItems = closeTabsPanel.querySelectorAll(
    "toolbarbutton[targetURI]"
  );
  Assert.equal(
    closedTabItems.length,
    3,
    "We have expected number of closed tab items"
  );

  const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  const closedObjectsChangePromise = TestUtils.topicObserved(
    "sessionstore-closed-objects-changed"
  );
  EventUtils.sendMouseEvent({ type: "click" }, closedTabItems[2], window);
  await newTabPromise;
  await closedObjectsChangePromise;

  Assert.equal(
    gBrowser.tabs[1].linkedBrowser.currentURI.spec,
    "about:robots",
    "Closed tab from the second window is opened"
  );

  await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE);
});

add_task(async function testTabsFromGroupClosedBeforeGroupDeleted() {
  // Ensures that bug1945111 has a proper resolution.
  info(
    "Tabs that were closed from within a tab group should appear in history menus as standalone tabs, even if the tab group is later deleted"
  );

  await resetClosedTabsAndWindows();
  const ORIG_STATE = SessionStore.getBrowserState();
  const GROUP_ID = "1234567890-1";

  await SessionStoreTestUtils.promiseBrowserState({
    windows: [
      {
        tabs: [makeTabState("about:blank")],
        _closedTabs: [
          makeClosedTabState("about:mozilla", { groupId: GROUP_ID }),
        ],
        closedGroups: [
          {
            id: GROUP_ID,
            color: "blue",
            name: "tab-group",
            tabs: [
              makeClosedTabState("about:mozilla", {
                groupId: GROUP_ID,
                closedInTabGroup: true,
              }),
              makeClosedTabState("about:mozilla", {
                groupId: GROUP_ID,
                closedInTabGroup: true,
              }),
              makeClosedTabState("about:mozilla", {
                groupId: GROUP_ID,
                closedInTabGroup: true,
              }),
            ],
          },
        ],
      },
    ],
  });

  Assert.equal(
    SessionStore.getClosedTabCount(),
    4,
    "Sanity check number of closed tabs from closed windows"
  );

  let closeTabsPanel = await openRecentlyClosedTabsMenu();

  const topLevelClosedTabItems = closeTabsPanel
    .querySelector(".panel-subview-body")
    .querySelectorAll(":scope > toolbarbutton[targetURI]");
  Assert.equal(
    topLevelClosedTabItems.length,
    1,
    "We have the expected number of top-level closed tab items"
  );

  const tabGroupClosedTabItems = closeTabsPanel.querySelectorAll(
    `panelview#closed-tabs-tab-group-${GROUP_ID} toolbarbutton[targetURI]`
  );
  Assert.equal(
    tabGroupClosedTabItems.length,
    3,
    "We have the expected number of closed tab items within the tab group"
  );

  await hideHistoryPanel();

  await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE);
});

add_task(async function testOpenTabFromClosedGroupInClosedWindow() {
  // Asserts fix for bug1944416
  info(
    "Open a tab from a closed tab group in a closed window as a standalone tab"
  );
  await Sanitizer.sanitize(["history"]);
  await resetClosedTabsAndWindows();
  const ORIG_STATE = SessionStore.getBrowserState();

  const closedTabUrl = "about:robots";
  const closedTabGroupUrls = ["about:logo", "https://example.com"];
  const closedTabGroupId = "1234567890-1";

  await SessionStoreTestUtils.promiseBrowserState({
    windows: [
      {
        tabs: [makeTabState("about:blank")],
        _closedTabs: [],
        closedGroups: [],
      },
    ],
    _closedWindows: [
      {
        tabs: [makeTabState("about:blank")],
        _closedTabs: [makeClosedTabState(closedTabUrl)],
        closedGroups: [
          {
            id: closedTabGroupId,
            color: "red",
            name: "tab-group",
            tabs: closedTabGroupUrls.map(url =>
              makeClosedTabState(url, {
                groupId: closedTabGroupId,
                closedInTabGroup: true,
              })
            ),
          },
        ],
      },
    ],
  });

  Assert.equal(
    SessionStore.getClosedTabCountFromClosedWindows(),
    closedTabGroupUrls.length + 1, // Add the lone ungrouped closed tab
    "Sanity check number of closed tabs from closed windows"
  );

  let closeTabsPanel = await openRecentlyClosedTabsMenu();
  const closedTabItems = closeTabsPanel.querySelectorAll(
    "toolbarbutton[targetURI]"
  );

  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  let closedObjectsChangePromise = TestUtils.topicObserved(
    "sessionstore-closed-objects-changed"
  );
  EventUtils.sendMouseEvent({ type: "click" }, closedTabItems[0], window);
  await newTabPromise;
  await closedObjectsChangePromise;

  Assert.equal(
    gBrowser.tabs.at(-1).linkedBrowser.currentURI.spec,
    closedTabUrl,
    "Ungrouped closed tab from closed window is opened correctly in the presence of closed tab groups"
  );

  closeTabsPanel = await openRecentlyClosedTabsMenu();
  const tabGroupClosedTabItems = closeTabsPanel.querySelectorAll(
    `panelview#closed-tabs-tab-group-${closedTabGroupId} toolbarbutton[targetURI]`
  );

  newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  closedObjectsChangePromise = TestUtils.topicObserved(
    "sessionstore-closed-objects-changed"
  );
  EventUtils.sendMouseEvent(
    { type: "click" },
    tabGroupClosedTabItems[0],
    window
  );
  await newTabPromise;
  await closedObjectsChangePromise;

  Assert.equal(
    gBrowser.tabs.at(-1).linkedBrowser.currentURI.spec,
    closedTabGroupUrls[0],
    "Grouped closed tab from closed window is opened correctly"
  );

  await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE);
});

add_task(async function testHistoryMenusWorkWithOldPreTabGroupsState() {
  info(
    "Ensure that a session file created before the release of tab groups does not prevent closed tabs in closed windows from opening"
  );
  // Fixes bug1947503

  // We need to make sure the history is cleared before starting the test
  await Sanitizer.sanitize(["history"]);
  await resetClosedTabsAndWindows();
  const ORIG_STATE = SessionStore.getBrowserState();

  const closedTabUrls = [
    "about:robots",
    "https://example.com/",
    "https://example.org/",
  ];

  await SessionStoreTestUtils.promiseBrowserState({
    windows: [
      {
        tabs: [makeTabState("about:mozilla")],
        _closedTabs: [],
        // No closedGroups element
      },
    ],
    _closedWindows: [
      {
        tabs: [makeTabState("about:mozilla")],
        _closedTabs: closedTabUrls.map(makeClosedTabState),
        // No closedGroups element
      },
    ],
  });

  is(gBrowser.visibleTabs.length, 1, "We start with one tab open");
  // Open the "Recently closed tabs" panel.
  let closeTabsPanel = await openRecentlyClosedTabsMenu();

  // Click the first toolbar button in the panel.
  let toolbarButton = closeTabsPanel.querySelector(
    ".panel-subview-body toolbarbutton"
  );
  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
  EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window);
  let reopenedTab = await newTabPromise;

  is(gBrowser.tabs.length, 2, "Closed tab was restored");

  await SessionStoreTestUtils.closeTab(reopenedTab);
  await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE);
});
