// Enable signature checks for these tests
gUseRealCertChecks = true;
// Disable update security
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
// Allow attempting to show the compatibility UI which should not happen
Services.prefs.setBoolPref("extensions.showMismatchUI", true);

const DATA = "data/signing_checks/";
const ADDONS = {
  bootstrap: {
    unsigned: "unsigned_bootstrap_2.xpi",
    badid: "signed_bootstrap_badid_2.xpi",
    signed: "signed_bootstrap_2.xpi",
  },
  nonbootstrap: {
    unsigned: "unsigned_nonbootstrap_2.xpi",
    badid: "signed_nonbootstrap_badid_2.xpi",
    signed: "signed_nonbootstrap_2.xpi",
  }
};
const ID = "test@tests.mozilla.org";

const profileDir = gProfD.clone();
profileDir.append("extensions");

// Override the window watcher
var WindowWatcher = {
  sawAddon: false,

  openWindow(parent, url, name, features, args) {
    let ids = args.QueryInterface(AM_Ci.nsIVariant);
    this.sawAddon = ids.indexOf(ID) >= 0;
  },

  QueryInterface(iid) {
    if (iid.equals(AM_Ci.nsIWindowWatcher)
        || iid.equals(AM_Ci.nsISupports))
      return this;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
}

MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);

function resetPrefs() {
  Services.prefs.setIntPref("bootstraptest.active_version", -1);
  Services.prefs.setIntPref("bootstraptest.installed_version", -1);
  Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
  Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
  Services.prefs.setIntPref("bootstraptest.install_reason", -1);
  Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
  Services.prefs.setIntPref("bootstraptest.startup_oldversion", -1);
  Services.prefs.setIntPref("bootstraptest.shutdown_newversion", -1);
  Services.prefs.setIntPref("bootstraptest.install_oldversion", -1);
  Services.prefs.setIntPref("bootstraptest.uninstall_newversion", -1);
}

function getActiveVersion() {
  return Services.prefs.getIntPref("bootstraptest.active_version");
}

function run_test() {
  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");

  // Start and stop the manager to initialise everything in the profile before
  // actual testing
  startupManager();
  shutdownManager();
  resetPrefs();

  run_next_test();
}

// Removes the signedState field from add-ons in the json database to make it
// look like the database was written with an older version of the application
function stripDB() {
  let jData = loadJSON(gExtensionsJSON);
  jData.schemaVersion--;

  for (let addon of jData.addons)
    delete addon.signedState;

  saveJSON(jData, gExtensionsJSON);
}

async function test_breaking_migrate(addons, test, expectedSignedState) {
  // Startup as the old version
  gAppInfo.version = "4";
  startupManager(true);

  // Install the signed add-on
  await promiseInstallAllFiles([do_get_file(DATA + addons.signed)]);
  // Restart to let non-restartless add-ons install fully
  await promiseRestartManager();
  await promiseShutdownManager();
  resetPrefs();
  stripDB();

  // Now replace it with the version to test. Doing this so quickly shouldn't
  // trigger the file modification code to detect the change by itself.
  manuallyUninstall(profileDir, ID);
  manuallyInstall(do_get_file(DATA + addons[test]), profileDir, ID);

  // Update the application
  gAppInfo.version = "5";
  await promiseStartupManager(true);

  let addon = await promiseAddonByID(ID);
  do_check_neq(addon, null);
  do_check_true(addon.appDisabled);
  do_check_false(addon.isActive);
  do_check_eq(addon.signedState, expectedSignedState);

  // Add-on shouldn't be active
  if (addons == ADDONS.bootstrap)
    do_check_eq(getActiveVersion(), -1);
  else
    do_check_false(isExtensionInAddonsList(profileDir, ID));

  // Should have flagged the change during startup
  let changes = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
  do_check_eq(changes.length, 1);
  do_check_eq(changes[0], ID);

  // Shouldn't have checked for updates for the add-on
  do_check_false(WindowWatcher.sawAddon);
  WindowWatcher.sawAddon = false;

  addon.uninstall();
  // Restart to let non-restartless add-ons uninstall fully
  await promiseRestartManager();
  await shutdownManager();
  resetPrefs();
}

async function test_working_migrate(addons, test, expectedSignedState) {
  // Startup as the old version
  gAppInfo.version = "4";
  startupManager(true);

  // Install the signed add-on
  await promiseInstallAllFiles([do_get_file(DATA + addons.signed)]);
  // Restart to let non-restartless add-ons install fully
  await promiseRestartManager();
  await promiseShutdownManager();
  resetPrefs();
  stripDB();

  // Now replace it with the version to test. Doing this so quickly shouldn't
  // trigger the file modification code to detect the change by itself.
  manuallyUninstall(profileDir, ID);
  manuallyInstall(do_get_file(DATA + addons[test]), profileDir, ID);

  // Update the application
  gAppInfo.version = "5";
  await promiseStartupManager(true);

  let addon = await promiseAddonByID(ID);
  do_check_neq(addon, null);
  do_check_false(addon.appDisabled);
  do_check_true(addon.isActive);
  do_check_eq(addon.signedState, expectedSignedState);

  if (addons == ADDONS.bootstrap)
    do_check_eq(getActiveVersion(), 2);
  else
    do_check_true(isExtensionInAddonsList(profileDir, ID));

  // Shouldn't have checked for updates for the add-on
  do_check_false(WindowWatcher.sawAddon);
  WindowWatcher.sawAddon = false;

  addon.uninstall();
  // Restart to let non-restartless add-ons uninstall fully
  await promiseRestartManager();
  await shutdownManager();
  resetPrefs();
}

add_task(async function() {
  await test_breaking_migrate(ADDONS.bootstrap, "unsigned", AddonManager.SIGNEDSTATE_MISSING);
  await test_breaking_migrate(ADDONS.nonbootstrap, "unsigned", AddonManager.SIGNEDSTATE_MISSING);
});

add_task(async function() {
  await test_breaking_migrate(ADDONS.bootstrap, "badid", AddonManager.SIGNEDSTATE_BROKEN);
  await test_breaking_migrate(ADDONS.nonbootstrap, "badid", AddonManager.SIGNEDSTATE_BROKEN);
});

add_task(async function() {
  await test_working_migrate(ADDONS.bootstrap, "signed", AddonManager.SIGNEDSTATE_SIGNED);
  await test_working_migrate(ADDONS.nonbootstrap, "signed", AddonManager.SIGNEDSTATE_SIGNED);
});
