/* 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 FILE_FOLDER = `browser/devtools/client/webconsole/test/browser`;
const TEST_URI = `https://example.com/${FILE_FOLDER}/test-console-evaluation-context-selector.html`;
const IFRAME_PATH = `${FILE_FOLDER}/test-console-evaluation-context-selector-child.html`;

// Test that when a target is destroyed, it does not appear in the list anymore (and
// the context is set to the top one if the destroyed target was selected).

add_task(async function () {
  await pushPref("devtools.popups.debug", true);
  await pushPref("devtools.webconsole.input.context", true);

  const hud = await openNewTabWithIframesAndConsole(TEST_URI, [
    `https://example.net/${IFRAME_PATH}?id=iframe-1`,
  ]);

  const evaluationContextSelectorButton = hud.ui.outputNode.querySelector(
    ".webconsole-evaluation-selector-button"
  );

  if (!isFissionEnabled() && !isEveryFrameTargetEnabled()) {
    is(
      evaluationContextSelectorButton,
      null,
      "context selector is only displayed when Fission or EFT is enabled"
    );
    return;
  }

  is(
    evaluationContextSelectorButton.innerText,
    "Top",
    "The button has the expected 'Top' text"
  );

  setInputValue(hud, "document.location.host");
  await waitForEagerEvaluationResult(hud, `"example.com"`);

  info("Check the context selector menu");
  const expectedTopItem = {
    label: "Top",
    tooltip: TEST_URI,
  };
  const expectedSeparatorItem = { separator: true };
  const expectedFirstIframeItem = {
    label: "iframe-1|example.net",
    tooltip: `https://example.net/${IFRAME_PATH}?id=iframe-1`,
  };

  await checkContextSelectorMenu(hud, [
    {
      ...expectedTopItem,
      checked: true,
    },
    expectedSeparatorItem,
    {
      ...expectedFirstIframeItem,
      checked: false,
    },
  ]);

  info("Add another iframe");
  ContentTask.spawn(gBrowser.selectedBrowser, [IFRAME_PATH], function (path) {
    const iframe = content.document.createElement("iframe");
    iframe.src = `https://test1.example.org/${path}?id=iframe-2`;
    content.document.body.append(iframe);
  });

  // Wait until the new iframe is rendered in the context selector.
  await waitFor(() => {
    const items = getContextSelectorItems(hud);
    return (
      items.length === 4 &&
      items.some(el =>
        el
          .querySelector(".label")
          ?.textContent.includes("iframe-2|test1.example.org")
      )
    );
  });

  const expectedSecondIframeItem = {
    label: `iframe-2|test1.example.org`,
    tooltip: `https://test1.example.org/${IFRAME_PATH}?id=iframe-2`,
  };

  await checkContextSelectorMenu(hud, [
    {
      ...expectedTopItem,
      checked: true,
    },
    expectedSeparatorItem,
    {
      ...expectedFirstIframeItem,
      checked: false,
    },
    {
      ...expectedSecondIframeItem,
      checked: false,
    },
  ]);

  info("Select the first iframe");
  selectTargetInContextSelector(hud, expectedFirstIframeItem.label);

  await waitFor(() =>
    evaluationContextSelectorButton.innerText.includes("example.net")
  );
  await waitForEagerEvaluationResult(hud, `"example.net"`);
  ok(true, "The context was set to the selected iframe document");

  info("Remove the first iframe from the content document");
  ContentTask.spawn(gBrowser.selectedBrowser, [], function () {
    content.document.querySelector("iframe").remove();
  });

  await waitFor(() =>
    evaluationContextSelectorButton.innerText.includes("Top")
  );
  ok(
    true,
    "The context was set to Top frame after the selected iframe was removed"
  );
  await waitForEagerEvaluationResult(hud, `"example.com"`);
  ok(true, "Instant evaluation is done against the top frame context");

  await checkContextSelectorMenu(hud, [
    {
      ...expectedTopItem,
      checked: true,
    },
    expectedSeparatorItem,
    {
      ...expectedSecondIframeItem,
      checked: false,
    },
  ]);

  info("Select the remaining iframe");
  selectTargetInContextSelector(hud, expectedSecondIframeItem.label);

  await waitFor(() =>
    evaluationContextSelectorButton.innerText.includes("test1.example.org")
  );
  await waitForEagerEvaluationResult(hud, `"test1.example.org"`);
  ok(true, "The context was set to the selected iframe document");

  info("Remove the second iframe from the content document");
  ContentTask.spawn(gBrowser.selectedBrowser, [], function () {
    content.document.querySelector("iframe").remove();
  });

  await waitFor(
    () =>
      !hud.ui.outputNode.querySelector(".webconsole-evaluation-selector-button")
  );
  ok(
    true,
    "The evaluation context selector is hidden after last iframe was removed"
  );

  await waitForEagerEvaluationResult(hud, `"example.com"`);
  ok(true, "Instant evaluation is done against the top frame context");

  info("Open a popup");
  const originalTab = gBrowser.selectedTab;
  let onSwitchedHost = hud.toolbox.once("host-changed");
  await ContentTask.spawn(
    gBrowser.selectedBrowser,
    [IFRAME_PATH],
    function (path) {
      content.open(`https://test2.example.org/${path}?id=popup`);
    }
  );
  await onSwitchedHost;

  // Wait until the popup is rendered in the context selector
  // and that it is automatically switched to (aria-checked==true).
  await waitFor(() => {
    try {
      const items = getContextSelectorItems(hud);
      return (
        items.length === 3 &&
        items.some(
          el =>
            el
              .querySelector(".label")
              ?.textContent.includes("popup|test2.example.org") &&
            el.getAttribute("aria-checked") === "true"
        )
      );
    } catch (e) {
      // The context list may be wiped while updating and getContextSelectorItems will throw
    }
    return false;
  });

  const expectedPopupItem = {
    label: `popup|test2.example.org`,
    tooltip: `https://test2.example.org/${IFRAME_PATH}?id=popup`,
  };

  await checkContextSelectorMenu(hud, [
    {
      ...expectedTopItem,
      checked: false,
    },
    expectedSeparatorItem,
    {
      ...expectedPopupItem,
      checked: true,
    },
  ]);

  await waitForEagerEvaluationResult(hud, `"test2.example.org"`);
  ok(true, "The context was set to the popup document");

  info("Open a second popup and reload the original tab");
  onSwitchedHost = hud.toolbox.once("host-changed");
  await ContentTask.spawn(
    originalTab.linkedBrowser,
    [IFRAME_PATH],
    function (path) {
      content.open(`https://test2.example.org/${path}?id=popup2`);
    }
  );
  await onSwitchedHost;

  // Reloading the tab while having two popups opened used to
  // generate exception in the context selector component
  await BrowserTestUtils.reloadTab(originalTab);

  ok(
    !hud.ui.document.querySelector(".app-error-panel"),
    "The web console did not crash"
  );
});
