Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
*/
var testserver = createHttpServer({ hosts: ["example.com"] });
var gInstallDate;
const ADDONS = {
test_install1: {
manifest: {
name: "Test 1",
version: "1.0",
browser_specific_settings: { gecko: { id: "addon1@tests.mozilla.org" } },
},
},
test_install2_1: {
manifest: {
name: "Test 2",
version: "2.0",
browser_specific_settings: { gecko: { id: "addon2@tests.mozilla.org" } },
},
},
test_install2_2: {
manifest: {
name: "Test 2",
version: "3.0",
browser_specific_settings: { gecko: { id: "addon2@tests.mozilla.org" } },
},
},
test_install3: {
manifest: {
name: "Test 3",
version: "1.0",
browser_specific_settings: {
gecko: {
id: "addon3@tests.mozilla.org",
strict_min_version: "0",
strict_max_version: "0",
},
},
},
},
};
const XPIS = {};
// The test extension uses an insecure update url.
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
const profileDir = gProfD.clone();
profileDir.append("extensions");
const UPDATE_JSON = {
addons: {
"addon3@tests.mozilla.org": {
updates: [
{
version: "1.0",
applications: {
gecko: {
strict_min_version: "0",
strict_max_version: "2",
},
},
},
],
},
},
};
const GETADDONS_JSON = {
page_size: 25,
page_count: 1,
count: 1,
next: null,
previous: null,
results: [
{
name: "Test 2",
type: "extension",
guid: "addon2@tests.mozilla.org",
current_version: {
version: "1.0",
files: [
{
size: 2,
},
],
},
authors: [
{
name: "Test Creator",
},
],
summary: "Repository summary",
description: "Repository description",
},
],
};
function checkInstall(install, expected) {
for (let [key, value] of Object.entries(expected)) {
if (value instanceof Ci.nsIURI) {
equal(
install[key] && install[key].spec,
value.spec,
`Expected value of install.${key}`
);
} else {
deepEqual(install[key], value, `Expected value of install.${key}`);
}
}
}
add_task(async function setup() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
for (let [name, data] of Object.entries(ADDONS)) {
XPIS[name] = AddonTestUtils.createTempWebExtensionFile(data);
testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
}
await promiseStartupManager();
// Create and configure the HTTP server.
AddonTestUtils.registerJSON(testserver, "/update.json", UPDATE_JSON);
testserver.registerDirectory("/data/", do_get_file("data"));
testserver.registerPathHandler("/redirect", function (aRequest, aResponse) {
aResponse.setStatusLine(null, 301, "Moved Permanently");
let url = aRequest.host + ":" + aRequest.port + aRequest.queryString;
aResponse.setHeader("Location", "http://" + url);
});
gPort = testserver.identity.primaryPort;
});
// Checks that an install from a local file proceeds as expected
add_task(async function test_install_file() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForFile(XPIS.test_install1),
]);
let uri = Services.io.newFileURI(XPIS.test_install1);
checkInstall(install, {
type: "extension",
version: "1.0",
name: "Test 1",
state: AddonManager.STATE_DOWNLOADED,
sourceURI: uri,
});
let { addon } = install;
checkAddon("addon1@tests.mozilla.org", addon, {
install,
sourceURI: uri,
});
notEqual(addon.syncGUID, null);
equal(
addon.getResourceURI("manifest.json").spec,
`jar:${uri.spec}!/manifest.json`
);
let activeInstalls = await AddonManager.getAllInstalls();
equal(activeInstalls.length, 1);
equal(activeInstalls[0], install);
let fooInstalls = await AddonManager.getInstallsByTypes(["foo"]);
equal(fooInstalls.length, 0);
let extensionInstalls = await AddonManager.getInstallsByTypes(["extension"]);
equal(extensionInstalls.length, 1);
equal(extensionInstalls[0], install);
await expectEvents(
{
addonEvents: {
"addon1@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
},
() => install.install()
);
addon = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
ok(addon);
ok(!hasFlag(addon.permissions, AddonManager.PERM_CAN_ENABLE));
ok(hasFlag(addon.permissions, AddonManager.PERM_CAN_DISABLE));
let updateDate = Date.now();
await promiseRestartManager();
activeInstalls = await AddonManager.getAllInstalls();
equal(activeInstalls, 0);
let a1 = await AddonManager.getAddonByID("addon1@tests.mozilla.org");
let uri2 = do_get_addon_root_uri(profileDir, "addon1@tests.mozilla.org");
checkAddon("addon1@tests.mozilla.org", a1, {
type: "extension",
version: "1.0",
name: "Test 1",
foreignInstall: false,
sourceURI: Services.io.newFileURI(XPIS.test_install1),
});
notEqual(a1.syncGUID, null);
Assert.greaterOrEqual(a1.syncGUID.length, 9);
ok(isExtensionInBootstrappedList(profileDir, a1.id));
ok(XPIS.test_install1.exists());
do_check_in_crash_annotation(a1.id, a1.version);
let difference = a1.installDate.getTime() - updateDate;
if (Math.abs(difference) > MAX_TIME_DIFFERENCE) {
do_throw("Add-on install time was out by " + difference + "ms");
}
difference = a1.updateDate.getTime() - updateDate;
if (Math.abs(difference) > MAX_TIME_DIFFERENCE) {
do_throw("Add-on update time was out by " + difference + "ms");
}
equal(a1.getResourceURI("manifest.json").spec, uri2 + "manifest.json");
// Ensure that extension bundle (or icon if unpacked) has updated
// lastModifiedDate.
let testFile = getAddonFile(a1);
ok(testFile.exists());
difference = testFile.lastModifiedTime - Date.now();
Assert.less(Math.abs(difference), MAX_TIME_DIFFERENCE);
await a1.uninstall();
let { id, version } = a1;
await promiseRestartManager();
do_check_not_in_crash_annotation(id, version);
});
// Tests that an install from a url downloads.
add_task(async function test_install_url() {
let install = await AddonManager.getInstallForURL(url, {
name: "Test 2",
version: "1.0",
});
checkInstall(install, {
version: "1.0",
name: "Test 2",
state: AddonManager.STATE_AVAILABLE,
sourceURI: Services.io.newURI(url),
});
let activeInstalls = await AddonManager.getAllInstalls();
equal(activeInstalls.length, 1);
equal(activeInstalls[0], install);
await expectEvents(
{
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded", returnValue: false },
],
},
() => {
install.install();
}
);
checkInstall(install, {
version: "2.0",
name: "Test 2",
state: AddonManager.STATE_DOWNLOADED,
});
equal(install.addon.install, install);
await expectEvents(
{
addonEvents: {
"addon2@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => install.install()
);
let updateDate = Date.now();
await promiseRestartManager();
let installs = await AddonManager.getAllInstalls();
equal(installs, 0);
let a2 = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
checkAddon("addon2@tests.mozilla.org", a2, {
type: "extension",
version: "2.0",
name: "Test 2",
sourceURI: Services.io.newURI(url),
});
notEqual(a2.syncGUID, null);
ok(isExtensionInBootstrappedList(profileDir, a2.id));
ok(XPIS.test_install2_1.exists());
do_check_in_crash_annotation(a2.id, a2.version);
let difference = a2.installDate.getTime() - updateDate;
Assert.lessOrEqual(
Math.abs(difference),
MAX_TIME_DIFFERENCE,
"Add-on install time was correct"
);
difference = a2.updateDate.getTime() - updateDate;
Assert.lessOrEqual(
Math.abs(difference),
MAX_TIME_DIFFERENCE,
"Add-on update time was correct"
);
gInstallDate = a2.installDate;
});
// Tests that installing a new version of an existing add-on works
add_task(async function test_install_new_version() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForURL(url, {
name: "Test 2",
version: "3.0",
}),
]);
checkInstall(install, {
version: "3.0",
name: "Test 2",
state: AddonManager.STATE_AVAILABLE,
existingAddon: null,
});
let activeInstalls = await AddonManager.getAllInstalls();
equal(activeInstalls.length, 1);
equal(activeInstalls[0], install);
await expectEvents(
{
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded", returnValue: false },
],
},
() => {
install.install();
}
);
checkInstall(install, {
version: "3.0",
name: "Test 2",
state: AddonManager.STATE_DOWNLOADED,
existingAddon: await AddonManager.getAddonByID("addon2@tests.mozilla.org"),
});
equal(install.addon.install, install);
// Installation will continue when there is nothing returned.
await expectEvents(
{
addonEvents: {
"addon2@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => install.install()
);
await promiseRestartManager();
let installs2 = await AddonManager.getInstallsByTypes(null);
equal(installs2.length, 0);
let a2 = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
checkAddon("addon2@tests.mozilla.org", a2, {
type: "extension",
version: "3.0",
name: "Test 2",
isActive: true,
foreignInstall: false,
sourceURI: Services.io.newURI(url),
installDate: gInstallDate,
});
ok(isExtensionInBootstrappedList(profileDir, a2.id));
ok(XPIS.test_install2_2.exists());
do_check_in_crash_annotation(a2.id, a2.version);
// Update date should be later (or the same if this test is too fast)
Assert.lessOrEqual(a2.installDate, a2.updateDate);
await a2.uninstall();
});
// Tests that an install that requires a compatibility update works
add_task(async function test_install_compat_update() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
await AddonManager.getInstallForURL(url, {
name: "Test 3",
version: "1.0",
}),
]);
checkInstall(install, {
version: "1.0",
name: "Test 3",
state: AddonManager.STATE_AVAILABLE,
});
let activeInstalls = await AddonManager.getInstallsByTypes(null);
equal(activeInstalls.length, 1);
equal(activeInstalls[0], install);
await expectEvents(
{
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded", returnValue: false },
],
},
() => {
install.install();
}
);
checkInstall(install, {
version: "1.0",
name: "Test 3",
state: AddonManager.STATE_DOWNLOADED,
existingAddon: null,
});
checkAddon("addon3@tests.mozilla.org", install.addon, {
appDisabled: false,
});
// Continue the install
await expectEvents(
{
addonEvents: {
"addon3@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => install.install()
);
await promiseRestartManager();
let installs = await AddonManager.getAllInstalls();
equal(installs, 0);
let a3 = await AddonManager.getAddonByID("addon3@tests.mozilla.org");
checkAddon("addon3@tests.mozilla.org", a3, {
type: "extension",
version: "1.0",
name: "Test 3",
isActive: true,
appDisabled: false,
});
notEqual(a3.syncGUID, null);
ok(isExtensionInBootstrappedList(profileDir, a3.id));
ok(XPIS.test_install3.exists());
await a3.uninstall();
});
add_task(async function test_compat_update_local() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForFile(XPIS.test_install3),
]);
ok(install.addon.isCompatible);
await expectEvents(
{
addonEvents: {
"addon3@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => install.install()
);
await promiseRestartManager();
let a3 = await AddonManager.getAddonByID("addon3@tests.mozilla.org");
checkAddon("addon3@tests.mozilla.org", a3, {
type: "extension",
version: "1.0",
name: "Test 3",
isActive: true,
appDisabled: false,
});
notEqual(a3.syncGUID, null);
ok(isExtensionInBootstrappedList(profileDir, a3.id));
ok(XPIS.test_install3.exists());
await a3.uninstall();
});
// Test that after cancelling a download it is removed from the active installs
add_task(async function test_cancel() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForURL(url, {
name: "Test 3",
version: "1.0",
}),
]);
checkInstall(install, {
version: "1.0",
name: "Test 3",
state: AddonManager.STATE_AVAILABLE,
});
let activeInstalls = await AddonManager.getInstallsByTypes(null);
equal(activeInstalls.length, 1);
equal(activeInstalls[0], install);
let promise;
function cancel() {
promise = expectEvents(
{
installEvents: [{ event: "onDownloadCancelled" }],
},
() => {
install.cancel();
}
);
}
await expectEvents(
{
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded", callback: cancel },
],
},
() => {
install.install();
}
);
await promise;
let file = install.file;
// Allow the file removal to complete
activeInstalls = await AddonManager.getAllInstalls();
equal(activeInstalls.length, 0);
ok(!file.exists());
});
// Check that cancelling the install from onDownloadStarted actually cancels it
add_task(async function test_cancel_onDownloadStarted() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForURL(url),
]);
equal(install.file, null);
install.addListener({
onDownloadStarted() {
install.removeListener(this);
executeSoon(() => install.cancel());
},
});
let promise = AddonTestUtils.promiseInstallEvent("onDownloadCancelled");
install.install();
await promise;
// Wait another tick to see if it continues downloading.
// The listener only really tests if we give it time to see progress, the
// file check isn't ideal either
install.addListener({
onDownloadProgress() {
do_throw("Download should not have continued");
},
onDownloadEnded() {
do_throw("Download should not have continued");
},
});
let file = install.file;
await Promise.resolve();
ok(!file.exists());
});
// Checks that cancelling the install from onDownloadEnded actually cancels it
add_task(async function test_cancel_onDownloadEnded() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForURL(url),
]);
equal(install.file, null);
let promise;
function cancel() {
promise = expectEvents(
{
installEvents: [{ event: "onDownloadCancelled" }],
},
async () => {
install.cancel();
}
);
}
await expectEvents(
{
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded", callback: cancel },
],
},
() => {
install.install();
}
);
await promise;
install.addListener({
onInstallStarted() {
do_throw("Install should not have continued");
},
});
});
// Verify that the userDisabled value carries over to the upgrade by default
add_task(async function test_userDisabled_update() {
let [, install] = await Promise.all([
AddonTestUtils.promiseInstallEvent("onNewInstall"),
AddonManager.getInstallForURL(url),
]);
await install.install();
ok(!install.addon.userDisabled);
await install.addon.disable();
let addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
checkAddon("addon2@tests.mozilla.org", addon, {
userDisabled: true,
isActive: false,
});
install = await AddonManager.getInstallForURL(url);
await install.install();
checkAddon("addon2@tests.mozilla.org", install.addon, {
userDisabled: true,
isActive: false,
});
await promiseRestartManager();
addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
checkAddon("addon2@tests.mozilla.org", addon, {
userDisabled: true,
isActive: false,
});
await addon.uninstall();
});
// Verify that changing the userDisabled value before onInstallEnded works
add_task(async function test_userDisabled() {
let install = await AddonManager.getInstallForURL(url);
await install.install();
ok(!install.addon.userDisabled);
let addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
checkAddon("addon2@tests.mozilla.org", addon, {
userDisabled: false,
isActive: true,
});
install = await AddonManager.getInstallForURL(url);
install.addListener({
onInstallStarted() {
ok(!install.addon.userDisabled);
install.addon.disable();
},
});
await install.install();
addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
checkAddon("addon2@tests.mozilla.org", addon, {
userDisabled: true,
isActive: false,
});
await addon.uninstall();
});
// Checks that metadata is not stored if the pref is set to false
add_task(async function test_18_1() {
AddonTestUtils.registerJSON(testserver, "/getaddons.json", GETADDONS_JSON);
Services.prefs.setCharPref(
PREF_GETADDONS_BYIDS,
);
Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
Services.prefs.setBoolPref(
"extensions.addon2@tests.mozilla.org.getAddons.cache.enabled",
false
);
let install = await AddonManager.getInstallForURL(url);
await install.install();
notEqual(install.addon.fullDescription, "Repository description");
await promiseRestartManager();
let addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
notEqual(addon.fullDescription, "Repository description");
await addon.uninstall();
});
// Checks that metadata is downloaded for new installs and is visible before and
// after restart
add_task(async function test_metadata() {
Services.prefs.setBoolPref(
"extensions.addon2@tests.mozilla.org.getAddons.cache.enabled",
true
);
let install = await AddonManager.getInstallForURL(url);
await install.install();
equal(install.addon.fullDescription, "Repository description");
equal(
install.addon.amoListingURL,
);
await promiseRestartManager();
let addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
equal(addon.fullDescription, "Repository description");
equal(
addon.amoListingURL,
);
await addon.uninstall();
});
// Do the same again to make sure it works when the data is already in the cache
add_task(async function test_metadata_again() {
let install = await AddonManager.getInstallForURL(url);
await install.install();
equal(install.addon.fullDescription, "Repository description");
await promiseRestartManager();
let addon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
equal(addon.fullDescription, "Repository description");
equal(
addon.amoListingURL,
);
await addon.uninstall();
});
// Tests that an install can be restarted after being cancelled
add_task(async function test_restart() {
let install = await AddonManager.getInstallForURL(url);
equal(install.state, AddonManager.STATE_AVAILABLE);
install.addListener({
onDownloadEnded() {
install.removeListener(this);
install.cancel();
},
});
try {
await install.install();
ok(false, "Install should not have succeeded");
} catch (err) {}
let promise = expectEvents(
{
addonEvents: {
"addon1@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded" },
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => {
install.install();
}
);
await Promise.all([
promise,
promiseWebExtensionStartup("addon1@tests.mozilla.org"),
]);
await install.addon.uninstall();
});
// Tests that an install can be restarted after being cancelled when a hash
// was provided
add_task(async function test_restart_hash() {
let install = await AddonManager.getInstallForURL(url, {
hash: do_get_file_hash(XPIS.test_install1),
});
equal(install.state, AddonManager.STATE_AVAILABLE);
install.addListener({
onDownloadEnded() {
install.removeListener(this);
install.cancel();
},
});
try {
await install.install();
ok(false, "Install should not have succeeded");
} catch (err) {}
let promise = expectEvents(
{
addonEvents: {
"addon1@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded" },
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => {
install.install();
}
);
await Promise.all([
promise,
promiseWebExtensionStartup("addon1@tests.mozilla.org"),
]);
await install.addon.uninstall();
});
// Tests that an install with a bad hash can be restarted after it fails, though
// it will only fail again
add_task(async function test_restart_badhash() {
let install = await AddonManager.getInstallForURL(url, { hash: "sha1:foo" });
equal(install.state, AddonManager.STATE_AVAILABLE);
install.addListener({
onDownloadEnded() {
install.removeListener(this);
install.cancel();
},
});
try {
await install.install();
ok(false, "Install should not have succeeded");
} catch (err) {}
try {
await install.install();
ok(false, "Install should not have succeeded");
} catch (err) {
ok(true, "Resumed install should have failed");
}
});
// Tests that installs with a hash for a local file work
add_task(async function test_local_hash() {
let url = Services.io.newFileURI(XPIS.test_install1).spec;
let install = await AddonManager.getInstallForURL(url, {
hash: do_get_file_hash(XPIS.test_install1),
});
checkInstall(install, {
state: AddonManager.STATE_DOWNLOADED,
error: 0,
});
install.cancel();
});
// Test that an install cannot be canceled after the install is completed.
add_task(async function test_cancel_completed() {
let install = await AddonManager.getInstallForURL(url);
let cancelPromise = new Promise((resolve, reject) => {
install.addListener({
onInstallEnded() {
try {
install.cancel();
reject("Cancel should fail.");
} catch (e) {
resolve();
}
},
});
});
install.install();
await cancelPromise;
equal(install.state, AddonManager.STATE_INSTALLED);
});
// Test that an install may be canceled after a redirect.
add_task(async function test_cancel_redirect() {
let install = await AddonManager.getInstallForURL(url);
install.addListener({
onDownloadProgress() {
install.cancel();
},
});
let promise = AddonTestUtils.promiseInstallEvent("onDownloadCancelled");
install.install();
await promise;
equal(install.state, AddonManager.STATE_CANCELLED);
});
// Tests that an install can be restarted during onDownloadCancelled after being
// cancelled in mid-download
add_task(async function test_restart2() {
let install = await AddonManager.getInstallForURL(url);
equal(install.state, AddonManager.STATE_AVAILABLE);
install.addListener({
onDownloadProgress() {
install.removeListener(this);
install.cancel();
},
});
let promise = AddonTestUtils.promiseInstallEvent("onDownloadCancelled");
install.install();
await promise;
equal(install.state, AddonManager.STATE_CANCELLED);
promise = expectEvents(
{
addonEvents: {
"addon1@tests.mozilla.org": [
{ event: "onInstalling" },
{ event: "onInstalled" },
],
},
installEvents: [
{ event: "onDownloadStarted" },
{ event: "onDownloadEnded" },
{ event: "onInstallStarted" },
{ event: "onInstallEnded" },
],
},
() => {
let file = install.file;
install.install();
notEqual(file.path, install.file.path);
ok(!file.exists());
}
);
await Promise.all([
promise,
promiseWebExtensionStartup("addon1@tests.mozilla.org"),
]);
await install.addon.uninstall();
});