summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/xpcshell/test_install.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_install.js')
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_install.js1050
1 files changed, 1050 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install.js b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
new file mode 100644
index 0000000000..2d2ee3df24
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
@@ -0,0 +1,1050 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+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",
+ update_url: "http://example.com/update.json",
+ },
+ },
+ },
+ },
+};
+
+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,
+ url: "http://example.com/test_install2_1.xpi",
+ },
+ ],
+ },
+ authors: [
+ {
+ name: "Test Creator",
+ url: "http://example.com/creator.html",
+ },
+ ],
+ 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);
+ ok(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();
+ ok(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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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 url = "http://example.com/addons/test_install2_2.xpi";
+ 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)
+ ok(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 url = "http://example.com/addons/test_install3.xpi";
+ 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 url = "http://example.com/addons/test_install3.xpi";
+ 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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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,
+ });
+
+ url = "http://example.com/addons/test_install2_2.xpi";
+ 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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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,
+ });
+
+ url = "http://example.com/addons/test_install2_2.xpi";
+ 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,
+ "http://example.com/getaddons.json"
+ );
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ Services.prefs.setBoolPref(
+ "extensions.addon2@tests.mozilla.org.getAddons.cache.enabled",
+ false
+ );
+
+ let url = "http://example.com/addons/test_install2_1.xpi";
+ 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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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");
+
+ 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 url = "http://example.com/addons/test_install2_1.xpi";
+ 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");
+
+ await addon.uninstall();
+});
+
+// Tests that an install can be restarted after being cancelled
+add_task(async function test_restart() {
+ let url = "http://example.com/addons/test_install1.xpi";
+ 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 url = "http://example.com/addons/test_install1.xpi";
+ 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 url = "http://example.com/addons/test_install1.xpi";
+ 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 url = "http://example.com/addons/test_install1.xpi";
+ 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 url = "http://example.com/redirect?/addons/test_install1.xpi";
+ 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 url = "http://example.com/addons/test_install1.xpi";
+ 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();
+});