/* 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/>. */

// Test that the debugger pauses and is automatically highlighted and selected,
// even when it hasn't been opened.

"use strict";

const IFRAME_TEST_COM_URI = `https://example.com/document-builder.sjs?html=${encodeURI(
  `<script>const a=2;\ndebugger;\nconsole.log(a);</script>`
)}`;

// Embed the example.com test page in an example.org iframe.
const IFRAME_TEST_URI = `https://example.org/document-builder.sjs?html=${encodeURI(
  `<script>function breakDebugger() {const b=3;\ndebugger;\nconsole.log(b);}</script><iframe src="${IFRAME_TEST_COM_URI}"></iframe><body>`
)}`;

add_task(async function () {
  info("Test a debugger statement from the top level document");

  // Make sure the toolbox opens with the webconsole initially selected.
  const toolbox = await initPane("doc-debugger-statements.html", "webconsole");

  info("Execute a debugger statement");
  const pausedRun = SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [],
    function () {
      content.wrappedJSObject.test();
    }
  );

  const dbg = await assertDebuggerIsHighlightedAndPaused(toolbox);
  const source = findSource(dbg, "doc-debugger-statements.html");
  await assertPausedAtSourceAndLine(dbg, source.id, 16);

  await resume(dbg);
  info("Wait for the paused code to complete after resume");
  await pausedRun;

  ok(
    !toolbox.isHighlighted("jsdebugger"),
    "Debugger is no longer highlighted after resume"
  );
});

add_task(async function () {
  info("Test a debugger statement from an iframe");

  // Make sure the toolbox opens with the webconsole initially selected.
  const toolbox = await openNewTabAndToolbox(IFRAME_TEST_URI, "webconsole");

  info(
    "Reload the test page, which will trigger the debugger statement in the iframe"
  );
  const pausedReload = reloadSelectedTab();

  const dbg = await assertDebuggerIsHighlightedAndPaused(toolbox);
  const source = findSource(dbg, IFRAME_TEST_COM_URI);
  await assertPausedAtSourceAndLine(dbg, source.id, 2);

  await resume(dbg);
  info("Wait for the paused code to complete after resume");
  await pausedReload;

  ok(
    !toolbox.isHighlighted("jsdebugger"),
    "Debugger is no longer highlighted after resume"
  );
});

add_task(async function () {
  info("Test pausing from two distinct targets");

  // Make sure the toolbox opens with the webconsole initially selected.
  const toolbox = await openNewTabAndToolbox(IFRAME_TEST_URI, "webconsole");

  info(
    "Reload the test page, which will trigger the debugger statement in the iframe"
  );
  const pausedReload = reloadSelectedTab();

  const dbg = await assertDebuggerIsHighlightedAndPaused(toolbox);
  const topLevelThread =
    toolbox.commands.targetCommand.targetFront.threadFront.actorID;
  const iframeThread = dbg.selectors.getCurrentThread();
  isnot(
    topLevelThread,
    iframeThread,
    "We get two distinct threads and could pause two times"
  );
  ok(
    !dbg.selectors.getIsPaused(topLevelThread),
    "The top level document thread is not paused"
  );
  ok(
    dbg.selectors.getIsPaused(iframeThread),
    "Only the iframe thread is paused"
  );

  const source = findSource(dbg, IFRAME_TEST_COM_URI);
  await assertPausedAtSourceAndLine(dbg, source.id, 2);

  info("Step over to the next line");
  await stepOver(dbg);
  await assertPausedAtSourceAndLine(dbg, source.id, 3);

  info("Now execute a debugger statement in the top level target");
  const onPaused = waitForPausedThread(dbg, topLevelThread);
  const pausedTopTarget = SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [],
    function () {
      content.wrappedJSObject.breakDebugger();
    }
  );

  info("Wait for the top level target to be paused");
  await onPaused;
  // also use waitForPause to wait for UI updates
  await waitForPaused(dbg);

  ok(
    dbg.selectors.getIsPaused(topLevelThread),
    "The top level document thread is paused"
  );
  ok(dbg.selectors.getIsPaused(iframeThread), "The iframe thread is paused");

  ok(
    toolbox.isHighlighted("jsdebugger"),
    "Debugger stays highlighted when pausing on another thread"
  );

  info(
    "The new paused state refers to the latest breakpoint being hit, on the top level target"
  );
  const source2 = findSource(dbg, IFRAME_TEST_URI);
  await assertPausedAtSourceAndLine(dbg, source2.id, 2);

  info("Resume the top level target");
  await resume(dbg);

  info("Wait for top level target paused code to complete after resume");
  await pausedTopTarget;

  info(
    "By default we stay on the last selected thread on resume and so the current thread is no longer paused"
  );
  assertNotPaused(dbg);
  ok(
    toolbox.isHighlighted("jsdebugger"),
    "Debugger stays highlighted when resuming only the top level target"
  );

  info(
    "Re-select the iframe thread, which is still paused on the original breakpoint"
  );
  dbg.actions.selectThread(iframeThread);
  await waitForPausedThread(dbg, iframeThread);
  await waitForSelectedSource(dbg, source);
  await assertPausedAtSourceAndLine(dbg, source.id, 3);

  info("Resume the iframe target");
  await resume(dbg);
  assertNotPaused(dbg);

  info("Wait for the paused code in the iframe to complete after resume");
  await pausedReload;

  await waitUntil(() => !toolbox.isHighlighted("jsdebugger"));
  ok(
    true,
    "Debugger is no longer highlighted after resuming all the paused targets"
  );

  info("Resume the last paused thread");
  await resume(dbg);
  assertNotPaused(dbg);

  info("Wait for the paused code in the iframe to complete after resume");
  await pausedReload;

  await waitUntil(() => !toolbox.isHighlighted("jsdebugger"));
  ok(
    true,
    "Debugger is no longer highlighted after resuming all the paused targets"
  );
});
