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

// Checks that changes that cause an add-on to become unblocked or blocked have
// the right effect

// The tests follow a mostly common pattern. First they start with the add-ons
// unblocked, then they make a change that causes the add-ons to become blocked
// then they make a similar change that keeps the add-ons blocked then they make
// a change that unblocks the add-ons. Some tests skip the initial part and
// start with add-ons detected as blocked.

// softblock1 is enabled/disabled by the blocklist changes so its softDisabled
// property should always match its userDisabled property

// softblock2 gets manually enabled then disabled after it becomes blocked so
// its softDisabled property should never become true after that

// softblock3 does the same as softblock2 however it remains disabled

// softblock4 is disabled while unblocked and so should never have softDisabled
// set to true and stay userDisabled. This add-on is not used in tests that
// start with add-ons blocked as it would be identical to softblock3

const URI_EXTENSION_BLOCKLIST_DIALOG =
  "chrome://mozapps/content/extensions/blocklist.xhtml";

// Allow insecure updates
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);

const IS_ANDROID_WITH_BLOCKLIST_V2 =
  AppConstants.platform == "android" && !AppConstants.NIGHTLY_BUILD;

// This is the initial value of Blocklist.allowDeprecatedBlocklistV2.
if (IS_ANDROID_WITH_BLOCKLIST_V2) {
  // test_blocklistchange_v2.js tests blocklist v2, so we should flip the pref
  // to enable the v3 blocklist on Android.
  Assert.ok(
    _TEST_NAME.includes("test_blocklistchange"),
    `Expected _TEST_NAME to be test_blocklistchange{,_v2}.js`
  );
  if (_TEST_NAME.includes("test_blocklistchange.js")) {
    Assert.equal(
      Services.prefs.getBoolPref("extensions.blocklist.useMLBF"),
      false,
      "Blocklist v3 disabled by default on Android"
    );
    Services.prefs.setBoolPref("extensions.blocklist.useMLBF", true);
  }
}

// TODO bug 1649906: strip blocklist v2-specific parts of this test.
// All specific logic is already covered by other test files, but the tests
// here trigger the logic via higher-level methods, so it may make sense to
// keep this file even after the removal of blocklist v2.
const useMLBF = Services.prefs.getBoolPref(
  "extensions.blocklist.useMLBF",
  true
);

var testserver = createHttpServer({ hosts: ["example.com"] });

function permissionPromptHandler(subject, topic, data) {
  ok(
    subject?.wrappedJSObject?.info?.resolve,
    "Got a permission prompt notification as expected"
  );
  subject.wrappedJSObject.info.resolve();
}

Services.obs.addObserver(
  permissionPromptHandler,
  "webextension-permission-prompt"
);

registerCleanupFunction(() => {
  Services.obs.removeObserver(
    permissionPromptHandler,
    "webextension-permission-prompt"
  );
});

const XPIS = {};

const ADDON_IDS = [
  "softblock1@tests.mozilla.org",
  "softblock2@tests.mozilla.org",
  "softblock3@tests.mozilla.org",
  "softblock4@tests.mozilla.org",
  "hardblock@tests.mozilla.org",
  "regexpblock@tests.mozilla.org",
];

const BLOCK_APP = [
  {
    guid: "xpcshell@tests.mozilla.org",
    maxVersion: "2.*",
    minVersion: "2",
  },
];
// JEXL filter expression that matches BLOCK_APP.
const BLOCK_APP_FILTER_EXPRESSION = `env.appinfo.ID == "xpcshell@tests.mozilla.org" && env.appinfo.version >= "2" && env.appinfo.version < "3"`;

function softBlockApp(id) {
  return {
    guid: `${id}@tests.mozilla.org`,
    versionRange: [
      {
        severity: "1",
        targetApplication: BLOCK_APP,
      },
    ],
  };
}

function softBlockAddonChange(id) {
  return {
    guid: `${id}@tests.mozilla.org`,
    versionRange: [
      {
        severity: "1",
        minVersion: "2",
        maxVersion: "3",
      },
    ],
  };
}

function softBlockUpdate2(id) {
  return {
    guid: `${id}@tests.mozilla.org`,
    versionRange: [{ severity: "1" }],
  };
}

function softBlockManual(id) {
  return {
    guid: `${id}@tests.mozilla.org`,
    versionRange: [
      {
        maxVersion: "2",
        minVersion: "1",
        severity: "1",
      },
    ],
  };
}

const BLOCKLIST_DATA = {
  empty_blocklist: [],
  app_update: [
    softBlockApp("softblock1"),
    softBlockApp("softblock2"),
    softBlockApp("softblock3"),
    softBlockApp("softblock4"),
    {
      guid: "hardblock@tests.mozilla.org",
      versionRange: [
        {
          targetApplication: BLOCK_APP,
        },
      ],
    },
    {
      guid: "/^RegExp/",
      versionRange: [
        {
          severity: "1",
          targetApplication: BLOCK_APP,
        },
      ],
    },
    {
      guid: "/^RegExp/i",
      versionRange: [
        {
          targetApplication: BLOCK_APP,
        },
      ],
    },
  ],
  addon_change: [
    softBlockAddonChange("softblock1"),
    softBlockAddonChange("softblock2"),
    softBlockAddonChange("softblock3"),
    softBlockAddonChange("softblock4"),
    {
      guid: "hardblock@tests.mozilla.org",
      versionRange: [
        {
          maxVersion: "3",
          minVersion: "2",
        },
      ],
    },
    {
      _comment:
        "Two RegExp matches, so test flags work - first shouldn't match.",
      guid: "/^RegExp/",
      versionRange: [
        {
          maxVersion: "3",
          minVersion: "2",
          severity: "1",
        },
      ],
    },
    {
      guid: "/^RegExp/i",
      versionRange: [
        {
          maxVersion: "3",
          minVersion: "2",
          severity: "2",
        },
      ],
    },
  ],
  blocklist_update2: [
    softBlockUpdate2("softblock1"),
    softBlockUpdate2("softblock2"),
    softBlockUpdate2("softblock3"),
    softBlockUpdate2("softblock4"),
    {
      guid: "hardblock@tests.mozilla.org",
      versionRange: [],
    },
    {
      guid: "/^RegExp/",
      versionRange: [{ severity: "1" }],
    },
    {
      guid: "/^RegExp/i",
      versionRange: [],
    },
  ],
  manual_update: [
    softBlockManual("softblock1"),
    softBlockManual("softblock2"),
    softBlockManual("softblock3"),
    softBlockManual("softblock4"),
    {
      guid: "hardblock@tests.mozilla.org",
      versionRange: [
        {
          maxVersion: "2",
          minVersion: "1",
        },
      ],
    },
    {
      guid: "/^RegExp/i",
      versionRange: [
        {
          maxVersion: "2",
          minVersion: "1",
        },
      ],
    },
  ],
};

// Blocklist v3 (useMLBF) only supports hard blocks by guid+version. Version
// ranges, regexps and soft blocks are not supported. So adjust expectations to
// ensure that the test passes even if useMLBF=true, by:
// - soft blocks are converted to hard blocks.
// - hard blocks are accepted as-is.
// - regexps blocks are converted to hard blocks.
// - Version ranges are expanded to cover all known versions.
if (useMLBF) {
  for (let [key, blocks] of Object.entries(BLOCKLIST_DATA)) {
    BLOCKLIST_DATA[key] = [];
    for (let block of blocks) {
      let { guid } = block;
      if (guid.includes("RegExp")) {
        guid = "regexpblock@tests.mozilla.org";
      } else if (!guid.startsWith("soft") && !guid.startsWith("hard")) {
        throw new Error(`Unexpected mock addon ID: ${guid}`);
      }

      const {
        minVersion = "1",
        maxVersion = "3",
        targetApplication,
      } = block.versionRange?.[0] || {};

      for (let v = minVersion; v <= maxVersion; ++v) {
        BLOCKLIST_DATA[key].push({
          // Assume that IF targetApplication is set, that it is BLOCK_APP.
          filter_expression: targetApplication && BLOCK_APP_FILTER_EXPRESSION,
          stash: {
            // XPI files use version `${v}.0`, update manifests use `${v}`.
            blocked: [`${guid}:${v}.0`, `${guid}:${v}`],
            unblocked: [],
          },
        });
      }
    }
  }
}

// XXXgijs: according to https://bugzilla.mozilla.org/show_bug.cgi?id=1257565#c111
// this code and the related code in Blocklist.jsm (specific to XML blocklist) is
// dead code and can be removed. See https://bugzilla.mozilla.org/show_bug.cgi?id=1549550 .
//
// Don't need the full interface, attempts to call other methods will just
// throw which is just fine
var WindowWatcher = {
  openWindow(parent, url, name, features, openArgs) {
    // Should be called to list the newly blocklisted items
    Assert.equal(url, URI_EXTENSION_BLOCKLIST_DIALOG);

    // Simulate auto-disabling any softblocks
    var list = openArgs.wrappedJSObject.list;
    list.forEach(function (aItem) {
      if (!aItem.blocked) {
        aItem.disable = true;
      }
    });

    // run the code after the blocklist is closed
    Services.obs.notifyObservers(null, "addon-blocklist-closed");
  },

  QueryInterface: ChromeUtils.generateQI(["nsIWindowWatcher"]),
};

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

var InstallConfirm = {
  confirm(aWindow, aUrl, aInstalls) {
    aInstalls.forEach(function (aInstall) {
      aInstall.install();
    });
  },

  QueryInterface: ChromeUtils.generateQI(["amIWebInstallPrompt"]),
};

var InstallConfirmFactory = {
  createInstance: function createInstance(iid) {
    return InstallConfirm.QueryInterface(iid);
  },
};

var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(
  Components.ID("{f0863905-4dde-42e2-991c-2dc8209bc9ca}"),
  "Fake Install Prompt",
  "@mozilla.org/addons/web-install-prompt;1",
  InstallConfirmFactory
);

function Pload_blocklist(aId) {
  return AddonTestUtils.loadBlocklistRawData({
    [useMLBF ? "extensionsMLBF" : "extensions"]: BLOCKLIST_DATA[aId],
  });
}

// Does a background update check for add-ons and returns a promise that
// resolves when any started installs complete
function Pbackground_update() {
  return new Promise((resolve, reject) => {
    let installCount = 0;
    let backgroundCheckCompleted = false;

    AddonManager.addInstallListener({
      onNewInstall(aInstall) {
        installCount++;
      },

      onInstallEnded(aInstall) {
        installCount--;
        // Wait until all started installs have completed
        if (installCount) {
          return;
        }

        AddonManager.removeInstallListener(this);

        // If the background check hasn't yet completed then let that call the
        // callback when it is done
        if (!backgroundCheckCompleted) {
          return;
        }

        resolve();
      },
    });

    Services.obs.addObserver(function observer() {
      Services.obs.removeObserver(
        observer,
        "addons-background-update-complete"
      );
      backgroundCheckCompleted = true;

      // If any new installs have started then we'll call the callback once they
      // are completed
      if (installCount) {
        return;
      }

      resolve();
    }, "addons-background-update-complete");

    AddonManagerPrivate.backgroundUpdateCheck();
  });
}

// Manually updates the test add-ons to the given version
function Pmanual_update(aVersion) {
  const names = ["soft1", "soft2", "soft3", "soft4", "hard", "regexp"];
  return Promise.all(
    names.map(async name => {
      let url = `http://example.com/addons/blocklist_${name}_${aVersion}.xpi`;
      let install = await AddonManager.getInstallForURL(url);

      // installAddonFromAOM() does more checking than install.install().
      // In particular, it will refuse to install an incompatible addon.

      return new Promise(resolve => {
        install.addListener({
          onDownloadCancelled: resolve,
          onInstallEnded: resolve,
        });

        AddonManager.installAddonFromAOM(null, null, install);
      });
    })
  );
}

// Checks that an add-ons properties match expected values
function check_addon(
  aAddon,
  aExpectedVersion,
  aExpectedUserDisabled,
  aExpectedSoftDisabled,
  aExpectedState
) {
  if (useMLBF) {
    if (aAddon.id.startsWith("soft")) {
      if (aExpectedState === Ci.nsIBlocklistService.STATE_SOFTBLOCKED) {
        // The whole test file assumes that an add-on is "user-disabled" after
        // an explicit disable(), or after a soft block (without enable()).
        // With useMLBF, soft blocks are not supported, so the "user-disabled"
        // state matches the usual behavior of "userDisabled" (=disable()).
        aExpectedUserDisabled = aAddon.userDisabled;
        aExpectedSoftDisabled = false;
        aExpectedState = Ci.nsIBlocklistService.STATE_BLOCKED;
      }
    }
  }

  Assert.notEqual(aAddon, null);
  info(
    "Testing " +
      aAddon.id +
      " version " +
      aAddon.version +
      " user " +
      aAddon.userDisabled +
      " soft " +
      aAddon.softDisabled +
      " perms " +
      aAddon.permissions
  );

  Assert.equal(aAddon.version, aExpectedVersion);
  Assert.equal(aAddon.blocklistState, aExpectedState);
  Assert.equal(aAddon.userDisabled, aExpectedUserDisabled);
  Assert.equal(aAddon.softDisabled, aExpectedSoftDisabled);
  if (aAddon.softDisabled) {
    Assert.ok(aAddon.userDisabled);
  }

  if (aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED) {
    info("blocked, PERM_CAN_ENABLE " + aAddon.id);
    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
    info("blocked, PERM_CAN_DISABLE " + aAddon.id);
    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
  } else if (aAddon.userDisabled) {
    info("userDisabled, PERM_CAN_ENABLE " + aAddon.id);
    Assert.ok(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
    info("userDisabled, PERM_CAN_DISABLE " + aAddon.id);
    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
  } else {
    info("other, PERM_CAN_ENABLE " + aAddon.id);
    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
    if (aAddon.type != "theme") {
      info("other, PERM_CAN_DISABLE " + aAddon.id);
      Assert.ok(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
    }
  }
  Assert.equal(
    aAddon.appDisabled,
    aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED
  );

  let willBeActive = aAddon.isActive;
  if (hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE)) {
    willBeActive = false;
  } else if (hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE)) {
    willBeActive = true;
  }

  if (
    aExpectedUserDisabled ||
    aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED
  ) {
    Assert.ok(!willBeActive);
  } else {
    Assert.ok(willBeActive);
  }
}

async function promiseRestartManagerWithAppChange(version) {
  await promiseShutdownManager();
  await promiseStartupManagerWithAppChange(version);
}

async function promiseStartupManagerWithAppChange(version) {
  if (version) {
    AddonTestUtils.appInfo.version = version;
  }
  if (useMLBF) {
    // The old ExtensionBlocklist enforced the app version/ID part of the block
    // when the blocklist entry is checked.
    // The new ExtensionBlocklist (with useMLBF=true) does not separately check
    // the app version/ID, but the underlying data source (Remote Settings)
    // does offer the ability to filter entries with `filter_expression`.
    // Force a reload to ensure that the BLOCK_APP_FILTER_EXPRESSION filter in
    // this test file is checked again against the new version.
    await Blocklist.ExtensionBlocklist._updateMLBF();
  }
  await promiseStartupManager();
}

add_task(async function setup() {
  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
  if (useMLBF) {
    const { ClientEnvironmentBase } = ChromeUtils.importESModule(
      "resource://gre/modules/components-utils/ClientEnvironment.sys.mjs"
    );
    Object.defineProperty(ClientEnvironmentBase, "appinfo", {
      configurable: true,
      get() {
        return gAppInfo;
      },
    });
  }

  function getxpibasename(id, version) {
    // pattern used to map ids like softblock1 to soft1
    let pattern = /^(soft|hard|regexp)block([1-9]*)@/;
    let match = id.match(pattern);
    return `blocklist_${match[1]}${match[2]}_${version}`;
  }
  for (let id of ADDON_IDS) {
    for (let version of [1, 2, 3, 4]) {
      let name = getxpibasename(id, version);

      let xpi = createTempWebExtensionFile({
        manifest: {
          name: "Test",
          version: `${version}.0`,
          browser_specific_settings: {
            gecko: {
              id,
              // This file is generated below, as updateJson.
              update_url: `http://example.com/addon_update${version}.json`,
            },
          },
        },
      });

      // To test updates, individual tasks in this test file start the test by
      // installing a set of add-ons with version |version| and trigger an
      // update check, from XPIS.${nameprefix}${version} (version = 1, 2, 3)
      if (version != 4) {
        XPIS[name] = xpi;
      }

      // update_url above points to a test manifest that references the next
      // version. The xpi is made available on the server, so that the test
      // can verify that the blocklist works as intended (i.e. update to newer
      // version is blocked).
      // There is nothing that updates to version 1, only to versions 2, 3, 4.
      if (version != 1) {
        testserver.registerFile(`/addons/${name}.xpi`, xpi);
      }
    }
  }

  // For each version that this test file uses, create a test manifest that
  // references the next version for each id in ADDON_IDS.
  for (let version of [1, 2, 3]) {
    let updateJson = { addons: {} };
    for (let id of ADDON_IDS) {
      let nextversion = version + 1;
      let name = getxpibasename(id, nextversion);
      updateJson.addons[id] = {
        updates: [
          {
            applications: {
              gecko: {
                strict_min_version: "0",
                advisory_max_version: "*",
              },
            },
            version: `${nextversion}.0`,
            update_link: `http://example.com/addons/${name}.xpi`,
          },
        ],
      };
    }
    AddonTestUtils.registerJSON(
      testserver,
      `/addon_update${version}.json`,
      updateJson
    );
  }

  await promiseStartupManager();

  await promiseInstallFile(XPIS.blocklist_soft1_1);
  await promiseInstallFile(XPIS.blocklist_soft2_1);
  await promiseInstallFile(XPIS.blocklist_soft3_1);
  await promiseInstallFile(XPIS.blocklist_soft4_1);
  await promiseInstallFile(XPIS.blocklist_hard_1);
  await promiseInstallFile(XPIS.blocklist_regexp_1);

  let s4 = await promiseAddonByID("softblock4@tests.mozilla.org");
  await s4.disable();
});

// Starts with add-ons unblocked and then switches application versions to
// change add-ons to blocked and back
add_task(async function run_app_update_test() {
  await Pload_blocklist("app_update");
  await promiseRestartManager();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s2,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
});

add_task(async function app_update_step_2() {
  await promiseRestartManagerWithAppChange("2");

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
});

add_task(async function app_update_step_3() {
  await promiseRestartManager();

  await promiseRestartManagerWithAppChange("2.5");

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
});

add_task(async function app_update_step_4() {
  await promiseRestartManagerWithAppChange("1");

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await s1.enable();
  await s2.enable();
});

// Starts with add-ons unblocked and then switches application versions to
// change add-ons to blocked and back. A DB schema change is faked to force a
// rebuild when the application version changes
add_task(async function run_app_update_schema_test() {
  await promiseRestartManager();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s2,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
});

add_task(async function update_schema_2() {
  await promiseShutdownManager();

  await changeXPIDBVersion(100);
  gAppInfo.version = "2";
  await promiseStartupManagerWithAppChange();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
});

add_task(async function update_schema_3() {
  await promiseRestartManager();

  await promiseShutdownManager();
  await changeXPIDBVersion(100);
  gAppInfo.version = "2.5";
  await promiseStartupManagerWithAppChange();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
});

add_task(async function update_schema_4() {
  await promiseShutdownManager();

  await changeXPIDBVersion(100);
  await promiseStartupManager();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
});

add_task(async function update_schema_5() {
  await promiseShutdownManager();

  await changeXPIDBVersion(100);
  gAppInfo.version = "1";
  await promiseStartupManagerWithAppChange();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await s1.enable();
  await s2.enable();
});

// Starts with add-ons unblocked and then loads new blocklists to change add-ons
// to blocked and back again.
add_task(async function run_blocklist_update_test() {
  await Pload_blocklist("empty_blocklist");
  await promiseRestartManager();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s2,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await Pload_blocklist("blocklist_update2");
  await promiseRestartManager();

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );

  await promiseRestartManager();

  await Pload_blocklist("blocklist_update2");
  await promiseRestartManager();

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await Pload_blocklist("empty_blocklist");
  await promiseRestartManager();

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await s1.enable();
  await s2.enable();
});

// Starts with add-ons unblocked and then new versions are installed outside of
// the app to change them to blocked and back again.
add_task(async function run_addon_change_test() {
  await Pload_blocklist("addon_change");
  await promiseRestartManager();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s2,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
});

add_task(async function run_addon_change_2() {
  await promiseInstallFile(XPIS.blocklist_soft1_2);
  await promiseInstallFile(XPIS.blocklist_soft2_2);
  await promiseInstallFile(XPIS.blocklist_soft3_2);
  await promiseInstallFile(XPIS.blocklist_soft4_2);
  await promiseInstallFile(XPIS.blocklist_hard_2);
  await promiseInstallFile(XPIS.blocklist_regexp_2);

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s4, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "2.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
});

add_task(async function run_addon_change_3() {
  await promiseInstallFile(XPIS.blocklist_soft1_3);
  await promiseInstallFile(XPIS.blocklist_soft2_3);
  await promiseInstallFile(XPIS.blocklist_soft3_3);
  await promiseInstallFile(XPIS.blocklist_soft4_3);
  await promiseInstallFile(XPIS.blocklist_hard_3);
  await promiseInstallFile(XPIS.blocklist_regexp_3);

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(
    s3,
    "3.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );
  check_addon(s4, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
});

add_task(async function run_addon_change_4() {
  await promiseInstallFile(XPIS.blocklist_soft1_1);
  await promiseInstallFile(XPIS.blocklist_soft2_1);
  await promiseInstallFile(XPIS.blocklist_soft3_1);
  await promiseInstallFile(XPIS.blocklist_soft4_1);
  await promiseInstallFile(XPIS.blocklist_hard_1);
  await promiseInstallFile(XPIS.blocklist_regexp_1);

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await s1.enable();
  await s2.enable();
});

// Add-ons are initially unblocked then attempts to upgrade to blocked versions
// in the background which should fail
add_task(async function run_background_update_test() {
  await promiseRestartManager();

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s2,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await Pbackground_update();
  await promiseRestartManager();

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s2,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
});

// Starts with add-ons blocked and then new versions are detected and installed
// automatically for unblocked versions.
add_task(async function run_background_update_2_test() {
  await promiseInstallFile(XPIS.blocklist_soft1_3);
  await promiseInstallFile(XPIS.blocklist_soft2_3);
  await promiseInstallFile(XPIS.blocklist_soft3_3);
  await promiseInstallFile(XPIS.blocklist_soft4_3);
  await promiseInstallFile(XPIS.blocklist_hard_3);
  await promiseInstallFile(XPIS.blocklist_regexp_3);

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "3.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );

  await Pbackground_update();

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "4.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "4.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "4.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(h, "4.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "4.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await s1.enable();
  await s2.enable();
  await s4.disable();
});

// The next test task (run_manual_update_test) was written to expect version 1,
// but after the previous test, version 4 of the add-ons were installed.
add_task(async function reset_addons_to_version_1_instead_of_4() {
  await promiseInstallFile(XPIS.blocklist_soft1_1);
  await promiseInstallFile(XPIS.blocklist_soft2_1);
  await promiseInstallFile(XPIS.blocklist_soft3_1);
  await promiseInstallFile(XPIS.blocklist_soft4_1);
  await promiseInstallFile(XPIS.blocklist_hard_1);
  await promiseInstallFile(XPIS.blocklist_regexp_1);
});

// Starts with add-ons blocked and then simulates the user upgrading them to
// unblocked versions.
add_task(async function run_manual_update_test() {
  await Pload_blocklist("manual_update");

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );

  await Pmanual_update("2");

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  // With useMLBF, s1/s2/s3 are hard blocks, so they cannot update.
  const sv2 = useMLBF ? "1.0" : "2.0";
  check_addon(s1, sv2, true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, sv2, true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, sv2, false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s4, sv2, true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  // Can't manually update to a hardblocked add-on
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await Pmanual_update("3");

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "3.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "3.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s4, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
});

// Starts with add-ons blocked and then new versions are installed outside of
// the app to change them to unblocked.
add_task(async function run_manual_update_2_test() {
  let addons = await promiseAddonsByIDs(ADDON_IDS);
  await Promise.all(addons.map(addon => addon.uninstall()));

  await promiseInstallFile(XPIS.blocklist_soft1_1);
  await promiseInstallFile(XPIS.blocklist_soft2_1);
  await promiseInstallFile(XPIS.blocklist_soft3_1);
  await promiseInstallFile(XPIS.blocklist_soft4_1);
  await promiseInstallFile(XPIS.blocklist_hard_1);
  await promiseInstallFile(XPIS.blocklist_regexp_1);

  let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await s2.enable();
  await s2.disable();
  check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  await s3.enable();
  check_addon(
    s3,
    "1.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_SOFTBLOCKED
  );

  await Pmanual_update("2");

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  // With useMLBF, s1/s2/s3 are hard blocks, so they cannot update.
  const sv2 = useMLBF ? "1.0" : "2.0";
  check_addon(s1, sv2, true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, sv2, true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, sv2, false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  // Can't manually update to a hardblocked add-on
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);

  await Pmanual_update("3");

  [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(
    s1,
    "3.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(
    s3,
    "3.0",
    false,
    false,
    Ci.nsIBlocklistService.STATE_NOT_BLOCKED
  );
  check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
  check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);

  await s1.enable();
  await s2.enable();
  await s4.disable();
});

// Uses the API to install blocked add-ons from the local filesystem
add_task(async function run_local_install_test() {
  let addons = await promiseAddonsByIDs(ADDON_IDS);
  await Promise.all(addons.map(addon => addon.uninstall()));

  await promiseInstallAllFiles([
    XPIS.blocklist_soft1_1,
    XPIS.blocklist_soft2_1,
    XPIS.blocklist_soft3_1,
    XPIS.blocklist_soft4_1,
    XPIS.blocklist_hard_1,
    XPIS.blocklist_regexp_1,
  ]);

  let installs = await AddonManager.getAllInstalls();
  // Should have finished all installs without needing to restart
  Assert.equal(installs.length, 0);

  let [s1, s2, s3 /* s4 */, , h, r] = await promiseAddonsByIDs(ADDON_IDS);

  check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
  check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
  check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
});
