711 lines
21 KiB
JavaScript
711 lines
21 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const { XPIExports } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/addons/XPIExports.sys.mjs"
|
|
);
|
|
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.sys.mjs",
|
|
Management: "resource://gre/modules/Extension.sys.mjs",
|
|
});
|
|
|
|
AddonTestUtils.init(this);
|
|
AddonTestUtils.overrideCertDB();
|
|
AddonTestUtils.usePrivilegedSignatures = false;
|
|
|
|
const testStartTime = Date.now();
|
|
const not_before = new Date(testStartTime - 3600000).toISOString();
|
|
const not_after = new Date(testStartTime + 3600000).toISOString();
|
|
const RECOMMENDATION_FILE_NAME = "mozilla-recommendation.json";
|
|
|
|
const server = AddonTestUtils.createHttpServer();
|
|
const SERVER_BASE_URL = `http://localhost:${server.identity.primaryPort}`;
|
|
// Allow the test extensions to be updated from an insecure update url.
|
|
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
|
|
Services.prefs.setCharPref(
|
|
"extensions.update.background.url",
|
|
`${SERVER_BASE_URL}/upgrade.json`
|
|
);
|
|
|
|
function createFileWithRecommendations(id, recommendation, version = "1.0.0") {
|
|
let files = {};
|
|
if (recommendation) {
|
|
files[RECOMMENDATION_FILE_NAME] = recommendation;
|
|
}
|
|
return AddonTestUtils.createTempWebExtensionFile({
|
|
manifest: {
|
|
version,
|
|
browser_specific_settings: { gecko: { id } },
|
|
},
|
|
files,
|
|
});
|
|
}
|
|
|
|
async function installAddonWithRecommendations(id, recommendation) {
|
|
let xpi = createFileWithRecommendations(id, recommendation);
|
|
let install = await AddonTestUtils.promiseInstallFile(xpi);
|
|
return install.addon;
|
|
}
|
|
|
|
function checkRecommended(addon, recommended = true) {
|
|
equal(
|
|
addon.isRecommended,
|
|
recommended,
|
|
"The add-on isRecommended state is correct"
|
|
);
|
|
equal(
|
|
addon.recommendationStates.includes("recommended"),
|
|
recommended,
|
|
"The add-on recommendationStates is correct"
|
|
);
|
|
}
|
|
|
|
function waitForPendingExtension(extId) {
|
|
return new Promise(resolve => {
|
|
Management.on("startup", function startupListener() {
|
|
const pendingExtensionsMap =
|
|
Services.ppmm.sharedData.get("extensions/pending");
|
|
if (pendingExtensionsMap.has(extId)) {
|
|
Management.off("startup", startupListener);
|
|
resolve(pendingExtensionsMap.get(extId));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
async function assertPendingExtensionIgnoreQuarantined({
|
|
addonId,
|
|
expectedIgnoreQuarantined,
|
|
}) {
|
|
info(
|
|
`Reload ${addonId} and verify ignoreQuarantine in extensions/pending sharedData`
|
|
);
|
|
const promisePendingExtension = waitForPendingExtension(addonId);
|
|
const addon = await AddonManager.getAddonByID(addonId);
|
|
await addon.disable();
|
|
await addon.enable();
|
|
Assert.deepEqual(
|
|
(await promisePendingExtension).ignoreQuarantine,
|
|
expectedIgnoreQuarantined,
|
|
`Expect ignoreQuarantine to be true in pending/extensions details for ${addon.id}`
|
|
);
|
|
}
|
|
|
|
function assertQuarantinedFromURI({ domain, expected }) {
|
|
const { processType, PROCESS_TYPE_DEFAULT } = Services.appinfo;
|
|
const processTypeStr =
|
|
processType === PROCESS_TYPE_DEFAULT ? "Main Process" : "Child Process";
|
|
const testURI = Services.io.newURI(`https://${domain}/`);
|
|
for (const [addonId, expectedQuarantinedFromURI] of Object.entries(
|
|
expected
|
|
)) {
|
|
Assert.equal(
|
|
WebExtensionPolicy.getByID(addonId).quarantinedFromURI(testURI),
|
|
expectedQuarantinedFromURI,
|
|
`Expect ${addonId} to ${
|
|
expectedQuarantinedFromURI ? "not be" : "be"
|
|
} quarantined from ${domain} in ${processTypeStr}`
|
|
);
|
|
}
|
|
}
|
|
|
|
async function assertQuarantinedFromURIInChildProcessAsync({
|
|
domain,
|
|
expected,
|
|
}) {
|
|
// Doesn't matter what content url we us here, as long as we are
|
|
// using a content url to be able to run the assertions from a
|
|
// child process.
|
|
const testUrl = SERVER_BASE_URL;
|
|
const page = await ExtensionTestUtils.loadContentPage(testUrl);
|
|
// TODO(rpl): look into Bug 1648545 changes and determine what
|
|
// would need to change to use page.spawn instead.
|
|
await page.legacySpawn({ domain, expected }, assertQuarantinedFromURI);
|
|
await page.close();
|
|
}
|
|
|
|
function getUpdatesJSONFor(id, version) {
|
|
return {
|
|
updates: [
|
|
{
|
|
version,
|
|
update_link: `${SERVER_BASE_URL}/addons/${id}.xpi`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function registerUpdateXPIFile({ id, version, recommendationStates }) {
|
|
const recommendation = {
|
|
addon_id: id,
|
|
states: recommendationStates,
|
|
validity: { not_before, not_after },
|
|
};
|
|
let xpi = createFileWithRecommendations(id, recommendation, version);
|
|
server.registerFile(`/addons/${id}.xpi`, xpi);
|
|
}
|
|
|
|
function waitForBootstrapUpdateMethod(addonId, newVersion) {
|
|
return new Promise(resolve => {
|
|
function listener(_evt, { method, params }) {
|
|
if (
|
|
method === "update" &&
|
|
params.id === addonId &&
|
|
params.newVersion === newVersion
|
|
) {
|
|
AddonTestUtils.off("bootstrap-method", listener);
|
|
info(`Update bootstrap method called for ${addonId} ${newVersion}`);
|
|
resolve({ addonId, method, params });
|
|
}
|
|
}
|
|
AddonTestUtils.on("bootstrap-method", listener);
|
|
});
|
|
}
|
|
|
|
function assertUpdateBootstrapCall(detailsBootstrapUpdates, expected) {
|
|
const actualPerAddonId = detailsBootstrapUpdates
|
|
.map(({ addonId, params }) => {
|
|
return [addonId, params.recommendationState?.states];
|
|
})
|
|
.reduce((acc, [addonId, states]) => {
|
|
acc[addonId] = states;
|
|
return acc;
|
|
}, {});
|
|
Assert.deepEqual(
|
|
actualPerAddonId,
|
|
expected,
|
|
`Got the expected recommendation states in the update bootstrap calls`
|
|
);
|
|
}
|
|
|
|
add_setup(async () => {
|
|
await ExtensionTestUtils.startAddonManager();
|
|
});
|
|
|
|
add_task(async function text_no_file() {
|
|
const id = "no-recommendations-file@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, null);
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function text_malformed_file() {
|
|
const id = "no-recommendations-file@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, "This is not JSON");
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_valid_recommendation_file() {
|
|
const id = "recommended@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_multiple_valid_recommendation_file() {
|
|
const id = "recommended@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended", "something"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon);
|
|
ok(
|
|
addon.recommendationStates.includes("something"),
|
|
"The add-on recommendationStates contains something"
|
|
);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_unsigned() {
|
|
// Don't override the certificate, so that the test add-on is unsigned.
|
|
AddonTestUtils.useRealCertChecks = true;
|
|
// Allow unsigned add-on to be installed.
|
|
Services.prefs.setBoolPref("xpinstall.signatures.required", false);
|
|
|
|
const id = "unsigned@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
AddonTestUtils.useRealCertChecks = false;
|
|
Services.prefs.setBoolPref("xpinstall.signatures.required", true);
|
|
});
|
|
|
|
add_task(async function test_temporary() {
|
|
const id = "temporary@test.web.extension";
|
|
let xpi = createFileWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
let addon = await XPIExports.XPIInstall.installTemporaryAddon(xpi);
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
// Tests that unpacked temporary add-ons are not recommended.
|
|
add_task(async function test_temporary_directory() {
|
|
const id = "temporary-dir@test.web.extension";
|
|
let files = ExtensionTestCommon.generateFiles({
|
|
manifest: {
|
|
browser_specific_settings: { gecko: { id } },
|
|
},
|
|
files: {
|
|
[RECOMMENDATION_FILE_NAME]: {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
},
|
|
},
|
|
});
|
|
let extDir = await AddonTestUtils.promiseWriteFilesToExtension(
|
|
gTmpD.path,
|
|
id,
|
|
files,
|
|
true
|
|
);
|
|
|
|
let addon = await XPIExports.XPIInstall.installTemporaryAddon(extDir);
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
extDir.remove(true);
|
|
});
|
|
|
|
add_task(async function test_builtin() {
|
|
const id = "builtin@test.web.extension";
|
|
let extension = await installBuiltinExtension({
|
|
manifest: {
|
|
browser_specific_settings: { gecko: { id } },
|
|
},
|
|
background: `browser.test.sendMessage("started");`,
|
|
files: {
|
|
[RECOMMENDATION_FILE_NAME]: {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
},
|
|
},
|
|
});
|
|
await extension.awaitMessage("started");
|
|
|
|
checkRecommended(extension.addon, false);
|
|
|
|
await extension.unload();
|
|
});
|
|
|
|
add_task(
|
|
// Non-extension add-ons are not supported on Android, but test installs static theme.
|
|
{ skip_if: () => AppConstants.platform == "android" },
|
|
async function test_theme() {
|
|
const id = "theme@test.web.extension";
|
|
let xpi = AddonTestUtils.createTempWebExtensionFile({
|
|
manifest: {
|
|
browser_specific_settings: { gecko: { id } },
|
|
theme: {},
|
|
},
|
|
files: {
|
|
[RECOMMENDATION_FILE_NAME]: {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
},
|
|
},
|
|
});
|
|
let { addon } = await AddonTestUtils.promiseInstallFile(xpi);
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
}
|
|
);
|
|
|
|
add_task(async function test_not_recommended() {
|
|
const id = "not-recommended@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["something"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
ok(
|
|
addon.recommendationStates.includes("something"),
|
|
"The add-on recommendationStates contains something"
|
|
);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_id_missing() {
|
|
const id = "no-id@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_expired() {
|
|
const id = "expired@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended", "something"],
|
|
validity: { not_before, not_after: not_before },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
ok(
|
|
!addon.recommendationStates.length,
|
|
"The add-on recommendationStates does not contain anything"
|
|
);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_not_valid_yet() {
|
|
const id = "expired@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before: not_after, not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_states_missing() {
|
|
const id = "states-missing@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_validity_missing() {
|
|
const id = "validity-missing@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_not_before_missing() {
|
|
const id = "not-before-missing@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_bad_states() {
|
|
const id = "bad-states@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: { recommended: true },
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon, false);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_recommendation_persist_restart() {
|
|
const id = "persisted-recommendation@test.web.extension";
|
|
let addon = await installAddonWithRecommendations(id, {
|
|
addon_id: id,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
checkRecommended(addon);
|
|
|
|
await AddonTestUtils.promiseRestartManager();
|
|
|
|
addon = await AddonManager.getAddonByID(id);
|
|
|
|
checkRecommended(addon);
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(async function test_isLineExtension_internal_svg_permission() {
|
|
async function assertLineExtensionStateAndPermission(
|
|
addonId,
|
|
expectLineExtension,
|
|
isRestart
|
|
) {
|
|
const { extension } = WebExtensionPolicy.getByID(addonId);
|
|
|
|
const msgShould = expectLineExtension ? "should" : "should not";
|
|
|
|
equal(
|
|
extension.hasPermission("internal:svgContextPropertiesAllowed"),
|
|
expectLineExtension,
|
|
`"${addonId}" ${msgShould} have permission internal:svgContextPropertiesAllowed`
|
|
);
|
|
if (isRestart) {
|
|
const { permissions } = await ExtensionPermissions.get(addonId);
|
|
Assert.deepEqual(
|
|
permissions,
|
|
expectLineExtension ? ["internal:svgContextPropertiesAllowed"] : [],
|
|
`ExtensionPermission.get("${addonId}") result ${msgShould} include internal:svgContextPropertiesAllowed permission`
|
|
);
|
|
}
|
|
}
|
|
|
|
const idLineExt = "line-extension@test.web.extension";
|
|
await installAddonWithRecommendations(idLineExt, {
|
|
addon_id: idLineExt,
|
|
states: ["line"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
info(`Test line extension ${idLineExt}`);
|
|
await assertLineExtensionStateAndPermission(idLineExt, true, false);
|
|
await AddonTestUtils.promiseRestartManager();
|
|
info(`Test ${idLineExt} again after AOM restart`);
|
|
await assertLineExtensionStateAndPermission(idLineExt, true, true);
|
|
let addon = await AddonManager.getAddonByID(idLineExt);
|
|
await addon.uninstall();
|
|
|
|
const idNonLineExt = "non-line-extension@test.web.extension";
|
|
await installAddonWithRecommendations(idNonLineExt, {
|
|
addon_id: idNonLineExt,
|
|
states: ["recommended"],
|
|
validity: { not_before, not_after },
|
|
});
|
|
|
|
info(`Test non line extension: ${idNonLineExt}`);
|
|
await assertLineExtensionStateAndPermission(idNonLineExt, false, false);
|
|
await AddonTestUtils.promiseRestartManager();
|
|
info(`Test ${idNonLineExt} again after AOM restart`);
|
|
await assertLineExtensionStateAndPermission(idNonLineExt, false, true);
|
|
addon = await AddonManager.getAddonByID(idNonLineExt);
|
|
await addon.uninstall();
|
|
});
|
|
|
|
add_task(
|
|
{
|
|
pref_set: [
|
|
["extensions.quarantinedDomains.enabled", true],
|
|
["extensions.quarantinedDomains.list", "quarantined.example.org"],
|
|
],
|
|
},
|
|
async function test_recommended_exempt_from_quarantined() {
|
|
const invalidRecommendedId = "invalid-recommended@test.web.extension";
|
|
const validRecommendedId = "recommended@test.web.extension";
|
|
const validAndroidRecommendedId = "recommended-android@test.web.extension";
|
|
const lineExtensionId = "line@test.web.extension";
|
|
const validMultiRecommendedId = "recommended-multi@test.web.extension";
|
|
// NOTE: confirm that any future recommendation state that was considered
|
|
// valid and signed by AMO is also going to be exempt, which does also include
|
|
// recommendation states that we are not using anymore but are still technically
|
|
// supported by autograph (e.g. verified), see:
|
|
// https://github.com/mozilla-services/autograph/blob/8a34847a/autograph.yaml#L1456-L1460
|
|
const validFutureRecStateId = "fake-future-valid-state@test.web.extension";
|
|
|
|
const recommendationStatesPerId = {
|
|
[invalidRecommendedId]: null,
|
|
[validRecommendedId]: ["recommended"],
|
|
[validAndroidRecommendedId]: ["recommended-android"],
|
|
[lineExtensionId]: ["line"],
|
|
[validFutureRecStateId]: ["fake-future-valid-state"],
|
|
[validMultiRecommendedId]: ["recommended", "recommended-android"],
|
|
};
|
|
|
|
for (const [extId, expectedRecStates] of Object.entries(
|
|
recommendationStatesPerId
|
|
)) {
|
|
const recommendationData = expectedRecStates
|
|
? {
|
|
addon_id: extId,
|
|
states: expectedRecStates,
|
|
validity: { not_before, not_after },
|
|
}
|
|
: null;
|
|
await installAddonWithRecommendations(extId, recommendationData);
|
|
// Check that the expected recommendation states are reflected by the
|
|
// value returned by the AddonWrapper.recommendationStates getter.
|
|
const addon = await AddonManager.getAddonByID(extId);
|
|
Assert.deepEqual(
|
|
addon.recommendationStates,
|
|
expectedRecStates ?? [],
|
|
`Addon ${extId} has the expected recommendation states`
|
|
);
|
|
}
|
|
|
|
assertQuarantinedFromURI({
|
|
domain: "quarantined.example.org",
|
|
expected: {
|
|
[invalidRecommendedId]: true,
|
|
[validRecommendedId]: false,
|
|
[validAndroidRecommendedId]: false,
|
|
[lineExtensionId]: false,
|
|
[validFutureRecStateId]: false,
|
|
[validMultiRecommendedId]: false,
|
|
},
|
|
});
|
|
|
|
await assertQuarantinedFromURIInChildProcessAsync({
|
|
domain: "quarantined.example.org",
|
|
expected: {
|
|
[invalidRecommendedId]: true,
|
|
[validRecommendedId]: false,
|
|
[validAndroidRecommendedId]: false,
|
|
[lineExtensionId]: false,
|
|
[validFutureRecStateId]: false,
|
|
[validMultiRecommendedId]: false,
|
|
},
|
|
});
|
|
|
|
// NOTE: we only cover the 3 basic cases in the rest of this test case
|
|
// (we have verified that ignoreQuarantine is being set to the expected
|
|
// value and so the other cases shouldn't matter for the behaviors being
|
|
// explicitly covered by the remaining part of this test task).
|
|
|
|
// Make sure the ignoreQuarantine property is also propagated in the child
|
|
// processes while the extensions may still be not fully initialized (and
|
|
// so listed in the `extensions/pending` sharedData entry).
|
|
await assertPendingExtensionIgnoreQuarantined({
|
|
addonId: validRecommendedId,
|
|
expectedIgnoreQuarantined: true,
|
|
});
|
|
await assertPendingExtensionIgnoreQuarantined({
|
|
addonId: lineExtensionId,
|
|
expectedIgnoreQuarantined: true,
|
|
});
|
|
await assertPendingExtensionIgnoreQuarantined({
|
|
addonId: invalidRecommendedId,
|
|
expectedIgnoreQuarantined: false,
|
|
});
|
|
|
|
info("Verify ignoreQuarantine again after application restart");
|
|
|
|
await AddonTestUtils.promiseRestartManager();
|
|
assertQuarantinedFromURI({
|
|
domain: "quarantined.example.org",
|
|
expected: {
|
|
[invalidRecommendedId]: true,
|
|
[validRecommendedId]: false,
|
|
[lineExtensionId]: false,
|
|
},
|
|
});
|
|
|
|
info("Verify ignoreQuarantine again after addon updates");
|
|
|
|
AddonTestUtils.registerJSON(server, "/upgrade.json", {
|
|
addons: {
|
|
[invalidRecommendedId]: getUpdatesJSONFor(
|
|
invalidRecommendedId,
|
|
"2.0.0"
|
|
),
|
|
[validRecommendedId]: getUpdatesJSONFor(validRecommendedId, "2.0.0"),
|
|
[lineExtensionId]: getUpdatesJSONFor(lineExtensionId, "2.0.0"),
|
|
},
|
|
});
|
|
registerUpdateXPIFile({
|
|
id: invalidRecommendedId,
|
|
version: "2.0.0",
|
|
recommendationStates: recommendationStatesPerId[invalidRecommendedId],
|
|
});
|
|
registerUpdateXPIFile({
|
|
id: validRecommendedId,
|
|
version: "2.0.0",
|
|
recommendationStates: recommendationStatesPerId[validRecommendedId],
|
|
});
|
|
registerUpdateXPIFile({
|
|
id: lineExtensionId,
|
|
version: "2.0.0",
|
|
recommendationStates: recommendationStatesPerId[lineExtensionId],
|
|
});
|
|
|
|
const promiseUpdatesInstalled = Promise.all([
|
|
waitForBootstrapUpdateMethod(invalidRecommendedId, "2.0.0"),
|
|
waitForBootstrapUpdateMethod(validRecommendedId, "2.0.0"),
|
|
waitForBootstrapUpdateMethod(lineExtensionId, "2.0.0"),
|
|
]);
|
|
|
|
const promiseBackgroundUpdatesFound = TestUtils.topicObserved(
|
|
"addons-background-updates-found"
|
|
);
|
|
let [
|
|
extensionInvalidRecommended,
|
|
extensionValidRecommended,
|
|
extensionLine,
|
|
] = [
|
|
ExtensionTestUtils.expectExtension(invalidRecommendedId),
|
|
ExtensionTestUtils.expectExtension(validRecommendedId),
|
|
ExtensionTestUtils.expectExtension(lineExtensionId),
|
|
];
|
|
|
|
await AddonManagerPrivate.backgroundUpdateCheck();
|
|
await promiseBackgroundUpdatesFound;
|
|
|
|
assertUpdateBootstrapCall(await promiseUpdatesInstalled, {
|
|
[invalidRecommendedId]: null,
|
|
[validRecommendedId]: ["recommended"],
|
|
[lineExtensionId]: ["line"],
|
|
});
|
|
|
|
// Wait the test extension to be fully started (prevents logspam
|
|
// due to the AOM trying to uninstall them while being started).
|
|
await Promise.all([
|
|
extensionInvalidRecommended.awaitStartup(),
|
|
extensionValidRecommended.awaitStartup(),
|
|
extensionLine.awaitStartup(),
|
|
]);
|
|
|
|
// Uninstall all test extensions.
|
|
await Promise.all(
|
|
Object.keys(recommendationStatesPerId).map(async addonId => {
|
|
const addon = await AddonManager.getAddonByID(addonId);
|
|
await addon.uninstall();
|
|
})
|
|
);
|
|
}
|
|
);
|