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

"use strict";

add_task(async function () {
  info("Test 1 JSON row selection started");

  // Create a tall JSON so that there is a scrollbar.
  const numRows = 1e3;
  const json = JSON.stringify(
    Array(numRows)
      .fill()
      .map((_, i) => i)
  );
  const tab = await addJsonViewTab("data:application/json," + json);

  is(
    await getElementCount(".treeRow"),
    numRows,
    "Got the expected number of rows."
  );
  await assertRowSelected(null);

  // Focus the tree and select first row.
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const tree = content.document.querySelector(".treeTable");
    tree.focus();
    is(tree, content.document.activeElement, "Tree should be focused");
    content.document.querySelector(".treeRow:nth-child(1)").click();
  });
  await assertRowSelected(1);

  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    Assert.less(
      scroller.clientHeight,
      scroller.scrollHeight,
      "There is a scrollbar."
    );
    is(scroller.scrollTop, 0, "Initially scrolled to the top.");
  });

  // Select last row.
  await BrowserTestUtils.synthesizeKey("VK_END", {}, tab.linkedBrowser);
  await assertRowSelected(numRows);

  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    is(
      scroller.scrollTop + scroller.clientHeight,
      scroller.scrollHeight,
      "Scrolled to the bottom."
    );
    // Click to select 2nd row.
    content.document.querySelector(".treeRow:nth-child(2)").click();
  });
  await assertRowSelected(2);

  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    Assert.greater(scroller.scrollTop, 0, "Not scrolled to the top.");
    // Synthesize up arrow key to select first row.
    content.document.querySelector(".treeTable").focus();
  });
  await BrowserTestUtils.synthesizeKey("VK_UP", {}, tab.linkedBrowser);
  await assertRowSelected(1);
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    is(scroller.scrollTop, 0, "Scrolled to the top.");
  });
});

add_task(async function () {
  info("Test 2 JSON row selection started");

  const numRows = 4;
  const tab = await addJsonViewTab("data:application/json,[0,1,2,3]");

  is(
    await getElementCount(".treeRow"),
    numRows,
    "Got the expected number of rows."
  );
  await assertRowSelected(null);

  // Click to select first row.
  await clickJsonNode(".treeRow:first-child");
  await assertRowSelected(1);

  // Synthesize multiple down arrow keydowns to select following rows.
  await SpecialPowers.spawn(tab.linkedBrowser, [], function () {
    content.document.querySelector(".treeTable").focus();
  });
  for (let i = 2; i < numRows; ++i) {
    await BrowserTestUtils.synthesizeKey(
      "VK_DOWN",
      { type: "keydown" },
      tab.linkedBrowser
    );
    await assertRowSelected(i);
  }

  // Now synthesize the keyup, this shouldn't change selected row.
  await BrowserTestUtils.synthesizeKey(
    "VK_DOWN",
    { type: "keyup" },
    tab.linkedBrowser
  );
  await wait(500);
  await assertRowSelected(numRows - 1);

  // Finally, synthesize keydown with a modifier, this also shouldn't change selected row.
  await BrowserTestUtils.synthesizeKey(
    "VK_DOWN",
    { type: "keydown", shiftKey: true },
    tab.linkedBrowser
  );
  await wait(500);
  await assertRowSelected(numRows - 1);
});

add_task(async function () {
  info("Test 3 JSON row selection started");

  // Create a JSON with a row taller than the panel.
  const json = JSON.stringify([0, "a ".repeat(1e4), 1]);
  const tab = await addJsonViewTab("data:application/json," + encodeURI(json));

  is(await getElementCount(".treeRow"), 3, "Got the expected number of rows.");
  await assertRowSelected(null);
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    const row = content.document.querySelector(".treeRow:nth-child(2)");
    Assert.less(
      scroller.clientHeight,
      row.clientHeight,
      "The row is taller than the scroller."
    );
    is(scroller.scrollTop, 0, "Initially scrolled to the top.");

    // Select the tall row.
    content.document.querySelector(".treeTable").focus();
    row.click();
  });
  await assertRowSelected(2);
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    is(
      scroller.scrollTop,
      0,
      "When the row is visible, do not scroll on click."
    );
  });

  // Select the last row.
  await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, tab.linkedBrowser);
  await assertRowSelected(3);
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () {
    const scroller = content.document.querySelector(
      ".jsonPanelBox .panelContent"
    );
    is(
      scroller.scrollTop + scroller.offsetHeight,
      scroller.scrollHeight,
      "Scrolled to the bottom."
    );

    // Select the tall row.
    const row = content.document.querySelector(".treeRow:nth-child(2)");
    row.click();
  });

  await assertRowSelected(2);
  const scroll = await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [],
    function () {
      const scroller = content.document.querySelector(
        ".jsonPanelBox .panelContent"
      );
      const row = content.document.querySelector(".treeRow:nth-child(2)");
      is(
        scroller.scrollTop + scroller.offsetHeight,
        scroller.scrollHeight,
        "Scrolled to the bottom. When the row is visible, do not scroll on click."
      );

      // Scroll up a bit, so that both the top and bottom of the row are not visible.
      const scrollPos = (scroller.scrollTop = Math.ceil(
        (scroller.scrollTop + row.offsetTop) / 2
      ));
      Assert.greater(
        scroller.scrollTop,
        row.offsetTop,
        "The top of the row is not visible."
      );
      Assert.less(
        scroller.scrollTop + scroller.offsetHeight,
        row.offsetTop + row.offsetHeight,
        "The bottom of the row is not visible."
      );

      // Select the tall row.
      row.click();
      return scrollPos;
    }
  );

  await assertRowSelected(2);
  await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [scroll],
    function (scrollPos) {
      const scroller = content.document.querySelector(
        ".jsonPanelBox .panelContent"
      );
      is(scroller.scrollTop, scrollPos, "Scroll did not change");
    }
  );
});

async function assertRowSelected(rowNum) {
  const idx = await SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [],
    function () {
      return [].indexOf.call(
        content.document.querySelectorAll(".treeRow"),
        content.document.querySelector(".treeRow.selected")
      );
    }
  );
  is(
    idx + 1,
    +rowNum,
    `${rowNum ? "The row #" + rowNum : "No row"} is selected.`
  );
}
