// This file gets imported into the same scope as head.js.
/* import-globals-from head.js */

async function runTestOnPrivacyPrefPane(testFunc) {
  info("runTestOnPrivacyPrefPane entered");
  let tab = await BrowserTestUtils.openNewForegroundTab(
    gBrowser,
    "about:preferences",
    true,
    true
  );
  let browser = tab.linkedBrowser;
  info("loaded about:preferences");
  await browser.contentWindow.gotoPref("panePrivacy");
  info("viewing privacy pane, executing testFunc");
  await testFunc(browser.contentWindow);
  BrowserTestUtils.removeTab(tab);
}

function controlChanged(element) {
  element.doCommand();
}

// We can only test the panes that don't trigger a preference update
async function test_pane_visibility(win) {
  // Elements which should only be visible in history mode "custom".
  let customElementIds = [
    "privateBrowsingAutoStart",
    "rememberHistory",
    "rememberForms",
    "alwaysClear",
    "clearDataSettings",
  ];

  let historymode = win.document.getElementById("historyMode");
  ok(historymode, "history mode menulist should exist");

  await selectHistoryMode(win, "custom");

  for (let id of customElementIds) {
    let element = win.document.getElementById(id);
    ok(element, `the ${id} element should exist`);
    is_element_visible(
      element,
      `the ${id} element should be visible in 'custom' mode`
    );
  }

  await selectHistoryMode(win, "remember");

  for (let id of customElementIds) {
    let element = win.document.getElementById(id);
    ok(element, `the ${id} element should exist`);
    is_element_hidden(
      element,
      `the ${id} element should be hidden in 'remember' mode`
    );
  }
}

async function test_dependent_elements(win) {
  let historymode = win.document.getElementById("historyMode");
  ok(historymode, "history mode menulist should exist");
  let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
  ok(pbautostart, "the private browsing auto-start checkbox should exist");
  let controls = [
    win.document.getElementById("rememberHistory"),
    win.document.getElementById("rememberForms"),
    win.document.getElementById("deleteOnClose"),
    win.document.getElementById("alwaysClear"),
  ];
  controls.forEach(function (control) {
    ok(control, "the dependent controls should exist");
  });
  let independents = [
    win.document.getElementById("contentBlockingBlockCookiesCheckbox"),
  ];
  independents.forEach(function (control) {
    ok(control, "the independent controls should exist");
  });
  let cookieexceptions = win.document.getElementById("cookieExceptions");
  ok(cookieexceptions, "the cookie exceptions button should exist");
  let deleteOnCloseCheckbox = win.document.getElementById("deleteOnClose");
  ok(deleteOnCloseCheckbox, "the delete on close checkbox should exist");
  let alwaysclear = win.document.getElementById("alwaysClear");
  ok(alwaysclear, "the clear data on close checkbox should exist");
  let rememberhistory = win.document.getElementById("rememberHistory");
  ok(rememberhistory, "the remember history checkbox should exist");
  let rememberforms = win.document.getElementById("rememberForms");
  ok(rememberforms, "the remember forms checkbox should exist");
  let alwaysclearsettings = win.document.getElementById("clearDataSettings");
  ok(alwaysclearsettings, "the clear data settings button should exist");

  function expect_disabled(disabled) {
    controls.forEach(function (control) {
      is(
        control.disabled,
        disabled,
        control.getAttribute("id") +
          " should " +
          (disabled ? "" : "not ") +
          "be disabled"
      );
    });
    if (disabled) {
      ok(
        !alwaysclear.checked,
        "the clear data on close checkbox value should be as expected"
      );
      ok(
        !rememberhistory.checked,
        "the remember history checkbox value should be as expected"
      );
      ok(
        !rememberforms.checked,
        "the remember forms checkbox value should be as expected"
      );
    }
  }
  function check_independents(expected) {
    independents.forEach(function (control) {
      is(
        control.disabled,
        expected,
        control.getAttribute("id") +
          " should " +
          (expected ? "" : "not ") +
          "be disabled"
      );
    });

    ok(
      !cookieexceptions.disabled,
      "the cookie exceptions button should never be disabled"
    );
    ok(
      alwaysclearsettings.disabled,
      "the clear data settings button should always be disabled"
    );
  }

  // controls should only change in custom mode
  await selectHistoryMode(win, "remember");
  expect_disabled(false);
  check_independents(false);

  // setting the mode to custom shouldn't change anything
  await selectHistoryMode(win, "custom");
  expect_disabled(false);
  check_independents(false);

  await selectHistoryMode(win, "remember");
}

async function test_dependent_cookie_elements(win) {
  let deleteOnCloseCheckbox = win.document.getElementById("deleteOnClose");
  let deleteOnCloseNote = win.document.getElementById("deleteOnCloseInfo");
  let blockCookiesMenu = win.document.getElementById("blockCookiesMenu");

  let controls = [blockCookiesMenu, deleteOnCloseCheckbox];
  controls.forEach(function (control) {
    ok(control, "the dependent cookie controls should exist");
  });
  let blockCookiesCheckbox = win.document.getElementById(
    "contentBlockingBlockCookiesCheckbox"
  );
  ok(blockCookiesCheckbox, "the block cookies checkbox should exist");

  function expect_disabled(disabled, c = controls) {
    c.forEach(function (control) {
      is(
        control.disabled,
        disabled,
        control.getAttribute("id") +
          " should " +
          (disabled ? "" : "not ") +
          "be disabled"
      );
    });
  }

  blockCookiesCheckbox.checked = true;
  controlChanged(blockCookiesCheckbox);
  await new Promise(resolve => requestAnimationFrame(resolve));
  expect_disabled(false);

  blockCookiesCheckbox.checked = false;
  controlChanged(blockCookiesCheckbox);
  await new Promise(resolve => requestAnimationFrame(resolve));
  expect_disabled(true, [blockCookiesMenu]);
  expect_disabled(false, [deleteOnCloseCheckbox]);
  is_element_hidden(
    deleteOnCloseNote,
    "The notice for delete on close in permanent private browsing mode should be hidden."
  );

  blockCookiesMenu.value = "always";
  controlChanged(blockCookiesMenu);
  await new Promise(resolve => requestAnimationFrame(resolve));
  expect_disabled(true, [deleteOnCloseCheckbox]);
  expect_disabled(false, [blockCookiesMenu]);
  is_element_hidden(
    deleteOnCloseNote,
    "The notice for delete on close in permanent private browsing mode should be hidden."
  );

  if (win.contentBlockingCookiesAndSiteDataRejectTrackersEnabled) {
    blockCookiesMenu.value = "trackers";
  } else {
    blockCookiesMenu.value = "unvisited";
  }
  controlChanged(blockCookiesMenu);
  await new Promise(resolve => requestAnimationFrame(resolve));
  expect_disabled(false);

  // The History mode setting for "never remember history" should still
  // disable the "keep cookies until..." menu.

  // Suppress restart prompt, because we can't restart in a mochitest.
  win._shouldPromptForRestartPBM = false;
  await selectHistoryMode(win, "dontremember");
  expect_disabled(true, [deleteOnCloseCheckbox]);
  is_element_visible(
    deleteOnCloseNote,
    "The notice for delete on close in permanent private browsing mode should be visible."
  );
  expect_disabled(false, [blockCookiesMenu]);

  // Suppress restart prompt, because we can't restart in a mochitest.
  win._shouldPromptForRestartPBM = false;

  await selectHistoryMode(win, "remember");
  expect_disabled(false);
  is_element_hidden(
    deleteOnCloseNote,
    "The notice for delete on close in permanent private browsing mode should be hidden."
  );
}

async function test_dependent_clearonclose_elements(win) {
  let historymode = win.document.getElementById("historyMode");
  ok(historymode, "history mode menulist should exist");
  let pbautostart = win.document.getElementById("privateBrowsingAutoStart");
  ok(pbautostart, "the private browsing auto-start checkbox should exist");
  let alwaysclear = win.document.getElementById("alwaysClear");
  ok(alwaysclear, "the clear data on close checkbox should exist");
  let alwaysclearsettings = win.document.getElementById("clearDataSettings");
  ok(alwaysclearsettings, "the clear data settings button should exist");

  function expect_disabled(disabled) {
    is(
      alwaysclearsettings.disabled,
      disabled,
      "the clear data settings should " +
        (disabled ? "" : "not ") +
        "be disabled"
    );
  }

  await selectHistoryMode(win, "custom");
  await updateCheckBox(win, "privateBrowsingAutoStart", false);

  await updateCheckBox(win, "alwaysClear", false);
  expect_disabled(true);

  await updateCheckBox(win, "alwaysClear", true);
  expect_disabled(false);

  await updateCheckBox(win, "alwaysClear", false);
  expect_disabled(true);
}

async function test_dependent_prefs(win) {
  let historymode = win.document.getElementById("historyMode");
  ok(historymode, "history mode menulist should exist");
  let controls = [
    win.document.getElementById("rememberHistory"),
    win.document.getElementById("rememberForms"),
  ];
  controls.forEach(function (control) {
    ok(control, "the micro-management controls should exist");
  });

  function expect_checked(checked) {
    controls.forEach(function (control) {
      is(
        control.checked,
        checked,
        control.getAttribute("id") +
          " should " +
          (checked ? "" : "not ") +
          "be checked"
      );
    });
  }

  // controls should be checked in remember mode
  await selectHistoryMode(win, "remember");
  // Initial updates from prefs are not sync, so wait:
  await TestUtils.waitForCondition(() => controls[0].checked);
  expect_checked(true);

  // even if they're unchecked in custom mode
  await selectHistoryMode(win, "custom");

  for (let control of controls) {
    await updateCheckBox(win, control.id, false);
  }
  expect_checked(false);
  await selectHistoryMode(win, "remember");
  expect_checked(true);
}

function test_historymode_retention(mode, expect) {
  return async function test_historymode_retention_fn(win) {
    let historymode = win.document.getElementById("historyMode");
    ok(historymode, "history mode menulist should exist");

    if (
      (historymode.value == "remember" && mode == "dontremember") ||
      (historymode.value == "dontremember" && mode == "remember") ||
      (historymode.value == "custom" && mode == "dontremember")
    ) {
      return;
    }

    if (expect !== undefined) {
      is(
        historymode.value,
        expect,
        "history mode is expected to remain " + expect
      );
    }

    await selectHistoryMode(win, mode);
  };
}

function test_custom_retention(controlToChange, expect, valueIncrement) {
  return async function test_custom_retention_fn(win) {
    let historymode = win.document.getElementById("historyMode");
    ok(historymode, "history mode menulist should exist");

    if (expect !== undefined) {
      is(
        historymode.value,
        expect,
        "history mode is expected to remain " + expect
      );
    }

    await selectHistoryMode(win, "custom");

    controlToChange = win.document.getElementById(controlToChange);
    ok(controlToChange, "the control to change should exist");
    switch (controlToChange.localName) {
      case "checkbox":
        await updateCheckBox(win, controlToChange.id, !controlToChange.checked);
        break;
      case "menulist":
        await selectHistoryMode(win, valueIncrement);
        break;
    }
  };
}

const gPrefCache = new Map();

function cache_preferences(win) {
  let prefs = win.Preferences.getAll();
  for (let pref of prefs) {
    gPrefCache.set(pref.id, pref.value);
  }
}

function reset_preferences(win) {
  let prefs = win.Preferences.getAll();
  // Avoid assigning undefined, which means clearing a "user"/test pref value
  for (let pref of prefs) {
    if (gPrefCache.has(pref.id)) {
      pref.value = gPrefCache.get(pref.id);
    }
  }
}

function run_test_subset(subset) {
  info("subset: " + Array.from(subset, x => x.name).join(",") + "\n");
  let tests = [cache_preferences, ...subset, reset_preferences];
  for (let test of tests) {
    add_task(runTestOnPrivacyPrefPane.bind(undefined, test));
  }
}
