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

"use strict";

// We refer to addons that were sideloaded prior to disabling sideloading as legacy.  We
// determine that they are legacy because they are in a SCOPE that is not included
// in AddonSettings.SCOPES_SIDELOAD.
//
// test_startup.js tests the legacy sideloading functionality still works as it should
// for ESR and some 3rd party distributions, which is to allow sideloading in all scopes.
//
// This file tests that locking down the sideload functionality works as we expect it to, which
// is to allow sideloading only in SCOPE_APPLICATION and SCOPE_PROFILE.  We also allow the legacy
// sideloaded addons to be updated or removed.
//
// We first change the sideload scope so we can sideload some addons into locations outside
// the profile (ie. we create some legacy sideloads).  We then reset it to test new sideloads.
//
// We expect new sideloads to only work in profile.
// We expect new sideloads to fail elsewhere.
// We expect to be able to change/uninstall legacy sideloads.

// This test uses add-on versions that follow the toolkit version but we
// started to encourage the use of a simpler format in Bug 1793925. We disable
// the pref below to avoid install errors.
Services.prefs.setBoolPref(
  "extensions.webextensions.warnings-as-errors",
  false
);

// IDs for scopes that should sideload when sideloading
// is not disabled.
let legacyIDs = [
  getID(`legacy-global`),
  getID(`legacy-user`),
  getID(`legacy-app`),
  getID(`legacy-profile`),
];

add_task(async function test_sideloads_legacy() {
  let IDs = [];

  // Create a "legacy" addon for each scope.
  for (let [name, dir] of Object.entries(scopeDirectories)) {
    let id = getID(`legacy-${name}`);
    IDs.push(id);
    await createWebExtension(id, initialVersion(name), dir);
  }

  await promiseStartupManager();

  // SCOPE_APPLICATION will never sideload, so we expect 3 addons.
  let sideloaded = await AddonManagerPrivate.getNewSideloads();
  Assert.equal(sideloaded.length, 4, "four sideloaded addons");
  let sideloadedIds = sideloaded.map(a => a.id);
  for (let id of legacyIDs) {
    Assert.ok(sideloadedIds.includes(id), `${id} is sideloaded`);
  }

  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);

  await promiseShutdownManager();
});

// Test that a sideload install in SCOPE_PROFILE is allowed, all others are
// disallowed.
add_task(async function test_sideloads_disabled() {
  // First, reset our scope pref to disable sideloading.  head_sideload.js set this to ALL.
  Services.prefs.setIntPref(
    "extensions.sideloadScopes",
    AddonManager.SCOPE_PROFILE
  );

  // Create 4 new addons, only one of these, "profile" should
  // sideload.
  for (let [name, dir] of Object.entries(scopeDirectories)) {
    await createWebExtension(getID(name), initialVersion(name), dir);
  }

  await promiseStartupManager();

  // Test that the "profile" addon has been sideloaded.
  let sideloaded = await AddonManagerPrivate.getNewSideloads();
  Assert.equal(sideloaded.length, 1, "one sideloaded addon");

  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, [
    getID("profile"),
  ]);
  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);

  for (let [name] of Object.entries(scopeDirectories)) {
    let id = getID(name);
    let addon = await promiseAddonByID(id);
    if (name === "profile") {
      Assert.notEqual(addon, null);
      Assert.equal(addon.id, id);
      Assert.ok(addon.foreignInstall);
      Assert.equal(addon.scope, AddonManager.SCOPE_PROFILE);
      Assert.ok(addon.userDisabled);
      Assert.ok(!addon.seen);
    } else {
      Assert.equal(addon, null, `addon ${id} is not installed`);
    }
  }

  // Test that we still have the 3 legacy addons from the prior test, plus
  // the new "profile" addon from this test.
  let extensionAddons = await AddonManager.getAddonsByTypes(["extension"]);
  Assert.equal(
    extensionAddons.length,
    5,
    "five addons expected to be installed"
  );
  let IDs = extensionAddons.map(ext => ext.id);
  for (let id of [getID("profile"), ...legacyIDs]) {
    Assert.ok(IDs.includes(id), `${id} is installed`);
  }

  await promiseShutdownManager();
});

add_task(async function test_sideloads_changed() {
  // Upgrade the manifest version
  for (let [name, dir] of Object.entries(scopeDirectories)) {
    let id = getID(name);
    await createWebExtension(id, `${name}.1`, dir);

    id = getID(`legacy-${name}`);
    await createWebExtension(id, `${name}.1`, dir);
  }

  await promiseStartupManager();
  let addons = await AddonManager.getAddonsByTypes(["extension"]);
  Assert.equal(addons.length, 5, "addons installed");

  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, [
    getID("profile"),
    ...legacyIDs,
  ]);
  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);

  await promiseShutdownManager();
});

// Remove one just to test the startup changes
add_task(async function test_sideload_removal() {
  let id = getID(`legacy-profile`);
  let file = AddonTestUtils.getFileForAddon(profileDir, id);
  file.remove(false);
  Assert.ok(!file.exists());

  await promiseStartupManager();

  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, [id]);

  await promiseShutdownManager();
});

add_task(async function test_sideload_uninstall() {
  await promiseStartupManager();
  let addons = await AddonManager.getAddonsByTypes(["extension"]);
  Assert.equal(addons.length, 4, "addons installed");
  for (let addon of addons) {
    let file = AddonTestUtils.getFileForAddon(
      scopeToDir.get(addon.scope),
      addon.id
    );
    await addon.uninstall();
    // Addon file should still exist in non-profile directories.
    Assert.equal(
      addon.scope !== AddonManager.SCOPE_PROFILE,
      file.exists(),
      `file remains after uninstall for non-profile sideloads, scope ${addon.scope}`
    );
  }
  addons = await AddonManager.getAddonsByTypes(["extension"]);
  Assert.equal(addons.length, 0, "addons left");

  await promiseShutdownManager();
});
