summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js')
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js485
1 files changed, 485 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js b/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
new file mode 100644
index 0000000000..2cd479ccf9
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
@@ -0,0 +1,485 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that delaying a system add-on update works.
+
+PromiseTestUtils.allowMatchingRejectionsGlobally(
+ /Message manager disconnected/
+);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+const IGNORE_ID = "system_delay_ignore@tests.mozilla.org";
+const COMPLETE_ID = "system_delay_complete@tests.mozilla.org";
+const DEFER_ID = "system_delay_defer@tests.mozilla.org";
+const DEFER2_ID = "system_delay_defer2@tests.mozilla.org";
+const DEFER_ALSO_ID = "system_delay_defer_also@tests.mozilla.org";
+const NORMAL_ID = "system1@tests.mozilla.org";
+
+const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
+registerDirectory("XREAppFeat", distroDir);
+
+registerCleanupFunction(() => {
+ distroDir.remove(true);
+});
+
+AddonTestUtils.usePrivilegedSignatures = id => "system";
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+function promiseInstallPostponed(addonID1, addonID2) {
+ return new Promise((resolve, reject) => {
+ let seen = [];
+ let listener = {
+ onInstallFailed: () => {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: install => {
+ AddonManager.removeInstallListener(listener);
+ reject(
+ `extension installation should not have ended for ${install.addon.id}`
+ );
+ },
+ onInstallPostponed: install => {
+ seen.push(install.addon.id);
+ if (seen.includes(addonID1) && seen.includes(addonID2)) {
+ AddonManager.removeInstallListener(listener);
+ resolve();
+ }
+ },
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+function promiseInstallResumed(addonID1, addonID2) {
+ return new Promise((resolve, reject) => {
+ let seenPostponed = [];
+ let seenEnded = [];
+ let listener = {
+ onInstallFailed: () => {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: install => {
+ seenEnded.push(install.addon.id);
+ if (
+ seenEnded.includes(addonID1) &&
+ seenEnded.includes(addonID2) &&
+ seenPostponed.includes(addonID1) &&
+ seenPostponed.includes(addonID2)
+ ) {
+ AddonManager.removeInstallListener(listener);
+ resolve();
+ }
+ },
+ onInstallPostponed: install => {
+ seenPostponed.push(install.addon.id);
+ },
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+function promiseInstallDeferred(addonID1, addonID2) {
+ return new Promise((resolve, reject) => {
+ let seenEnded = [];
+ let listener = {
+ onInstallFailed: () => {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: install => {
+ seenEnded.push(install.addon.id);
+ if (seenEnded.includes(addonID1) && seenEnded.includes(addonID2)) {
+ AddonManager.removeInstallListener(listener);
+ resolve();
+ }
+ },
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+async function checkAddon(addonID, { version }) {
+ let addon = await promiseAddonByID(addonID);
+ Assert.notEqual(addon, null);
+ Assert.equal(addon.version, version);
+ Assert.ok(addon.isCompatible);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.isActive);
+ Assert.equal(addon.type, "extension");
+}
+
+// Tests below have webextension background scripts inline.
+/* globals browser */
+
+// add-on registers upgrade listener, and ignores update.
+add_task(async function test_addon_upgrade_on_restart() {
+ // discard system addon updates
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ let xpi = await getSystemAddonXPI(1, "1.0");
+ xpi.copyTo(distroDir, `${NORMAL_ID}.xpi`);
+
+ // Version 1.0 of an extension that ignores updates.
+ function background() {
+ browser.runtime.onUpdateAvailable.addListener(() => {
+ browser.test.sendMessage("got-update");
+ });
+ }
+
+ xpi = await createTempWebExtensionFile({
+ background,
+
+ manifest: {
+ version: "1.0",
+ browser_specific_settings: { gecko: { id: IGNORE_ID } },
+ },
+ });
+ xpi.copyTo(distroDir, `${IGNORE_ID}.xpi`);
+
+ // Version 2.0 of the same extension.
+ let xpi2 = await createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ browser_specific_settings: { gecko: { id: IGNORE_ID } },
+ },
+ });
+
+ await overrideBuiltIns({ system: [IGNORE_ID, NORMAL_ID] });
+
+ let extension = ExtensionTestUtils.expectExtension(IGNORE_ID);
+
+ await Promise.all([promiseStartupManager(), extension.awaitStartup()]);
+
+ let updateList = [
+ {
+ id: IGNORE_ID,
+ version: "2.0",
+ path: "system_delay_ignore_2.xpi",
+ xpi: xpi2,
+ },
+ {
+ id: NORMAL_ID,
+ version: "2.0",
+ path: "system1_2.xpi",
+ xpi: await getSystemAddonXPI(1, "2.0"),
+ },
+ ];
+
+ await Promise.all([
+ promiseInstallPostponed(IGNORE_ID, NORMAL_ID),
+ installSystemAddons(buildSystemAddonUpdates(updateList)),
+ extension.awaitMessage("got-update"),
+ ]);
+
+ // addon upgrade has been delayed.
+ await checkAddon(IGNORE_ID, { version: "1.0" });
+ // other addons in the set are delayed as well.
+ await checkAddon(NORMAL_ID, { version: "1.0" });
+
+ // restarting allows upgrades to proceed
+ await Promise.all([promiseRestartManager(), extension.awaitStartup()]);
+
+ await checkAddon(IGNORE_ID, { version: "2.0" });
+ await checkAddon(NORMAL_ID, { version: "2.0" });
+
+ await promiseShutdownManager();
+});
+
+// add-on registers upgrade listener, and allows update.
+add_task(async function test_addon_upgrade_on_reload() {
+ // discard system addon updates
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ let xpi = await getSystemAddonXPI(1, "1.0");
+ xpi.copyTo(distroDir, `${NORMAL_ID}.xpi`);
+
+ // Version 1.0 of an extension that listens for and immediately
+ // applies updates.
+ function background() {
+ browser.runtime.onUpdateAvailable.addListener(function listener() {
+ browser.runtime.onUpdateAvailable.removeListener(listener);
+ browser.test.sendMessage("got-update");
+ browser.runtime.reload();
+ });
+ }
+
+ xpi = await createTempWebExtensionFile({
+ background,
+
+ manifest: {
+ version: "1.0",
+ browser_specific_settings: { gecko: { id: COMPLETE_ID } },
+ },
+ });
+ xpi.copyTo(distroDir, `${COMPLETE_ID}.xpi`);
+
+ // Version 2.0 of the same extension.
+ let xpi2 = await createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ browser_specific_settings: { gecko: { id: COMPLETE_ID } },
+ },
+ });
+
+ await overrideBuiltIns({ system: [COMPLETE_ID, NORMAL_ID] });
+
+ let extension = ExtensionTestUtils.expectExtension(COMPLETE_ID);
+
+ await Promise.all([promiseStartupManager(), extension.awaitStartup()]);
+
+ let updateList = [
+ {
+ id: COMPLETE_ID,
+ version: "2.0",
+ path: "system_delay_complete_2.xpi",
+ xpi: xpi2,
+ },
+ {
+ id: NORMAL_ID,
+ version: "2.0",
+ path: "system1_2.xpi",
+ xpi: await getSystemAddonXPI(1, "2.0"),
+ },
+ ];
+
+ // initial state
+ await checkAddon(COMPLETE_ID, { version: "1.0" });
+ await checkAddon(NORMAL_ID, { version: "1.0" });
+
+ // We should see that the onUpdateListener executed, then see the
+ // update resume.
+ await Promise.all([
+ extension.awaitMessage("got-update"),
+ promiseInstallResumed(COMPLETE_ID, NORMAL_ID),
+ installSystemAddons(buildSystemAddonUpdates(updateList)),
+ ]);
+ await extension.awaitStartup();
+
+ // addon upgrade has been allowed
+ await checkAddon(COMPLETE_ID, { version: "2.0" });
+ // other upgrades in the set are allowed as well
+ await checkAddon(NORMAL_ID, { version: "2.0" });
+
+ // restarting changes nothing
+ await Promise.all([promiseRestartManager(), extension.awaitStartup()]);
+
+ await checkAddon(COMPLETE_ID, { version: "2.0" });
+ await checkAddon(NORMAL_ID, { version: "2.0" });
+
+ await promiseShutdownManager();
+});
+
+function delayBackground() {
+ browser.test.onMessage.addListener(msg => {
+ if (msg !== "reload") {
+ browser.test.fail(`Got unexpected test message: ${msg}`);
+ }
+ browser.runtime.reload();
+ });
+ browser.runtime.onUpdateAvailable.addListener(async function listener() {
+ browser.runtime.onUpdateAvailable.removeListener(listener);
+ browser.test.sendMessage("got-update");
+ });
+}
+
+// Upgrade listener initially defers then proceeds after a pause.
+add_task(async function test_addon_upgrade_after_pause() {
+ // discard system addon updates
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ let xpi = await getSystemAddonXPI(1, "1.0");
+ xpi.copyTo(distroDir, `${NORMAL_ID}.xpi`);
+
+ // Version 1.0 of an extension that delays upgrades.
+ xpi = await createTempWebExtensionFile({
+ background: delayBackground,
+ manifest: {
+ version: "1.0",
+ browser_specific_settings: { gecko: { id: DEFER_ID } },
+ },
+ });
+ xpi.copyTo(distroDir, `${DEFER_ID}.xpi`);
+
+ // Version 2.0 of the same xtension.
+ let xpi2 = await createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ browser_specific_settings: { gecko: { id: DEFER_ID } },
+ },
+ });
+
+ await overrideBuiltIns({ system: [DEFER_ID, NORMAL_ID] });
+
+ let extension = ExtensionTestUtils.expectExtension(DEFER_ID);
+
+ await Promise.all([promiseStartupManager(), extension.awaitStartup()]);
+
+ let updateList = [
+ {
+ id: DEFER_ID,
+ version: "2.0",
+ path: "system_delay_defer_2.xpi",
+ xpi: xpi2,
+ },
+ {
+ id: NORMAL_ID,
+ version: "2.0",
+ path: "system1_2.xpi",
+ xpi: await getSystemAddonXPI(1, "2.0"),
+ },
+ ];
+
+ await Promise.all([
+ promiseInstallPostponed(DEFER_ID, NORMAL_ID),
+ installSystemAddons(buildSystemAddonUpdates(updateList)),
+ extension.awaitMessage("got-update"),
+ ]);
+
+ // upgrade is initially postponed
+ await checkAddon(DEFER_ID, { version: "1.0" });
+ // other addons in the set are postponed as well.
+ await checkAddon(NORMAL_ID, { version: "1.0" });
+
+ let deferred = promiseInstallDeferred(DEFER_ID, NORMAL_ID);
+
+ // Tell the extension to proceed with the update.
+ extension.setRestarting();
+ extension.sendMessage("reload");
+
+ await Promise.all([deferred, extension.awaitStartup()]);
+
+ // addon upgrade has been allowed
+ await checkAddon(DEFER_ID, { version: "2.0" });
+ // other addons in the set are allowed as well.
+ await checkAddon(NORMAL_ID, { version: "2.0" });
+
+ // restarting changes nothing
+ await promiseRestartManager();
+ await extension.awaitStartup();
+
+ await checkAddon(DEFER_ID, { version: "2.0" });
+ await checkAddon(NORMAL_ID, { version: "2.0" });
+
+ await promiseShutdownManager();
+});
+
+// Multiple add-ons register update listeners, initially defers then
+// each unblock in turn.
+add_task(async function test_multiple_addon_upgrade_postpone() {
+ // discard system addon updates.
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ let updateList = [];
+
+ let xpi = await createTempWebExtensionFile({
+ background: delayBackground,
+ manifest: {
+ version: "1.0",
+ browser_specific_settings: { gecko: { id: DEFER2_ID } },
+ },
+ });
+ xpi.copyTo(distroDir, `${DEFER2_ID}.xpi`);
+
+ xpi = await createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ browser_specific_settings: { gecko: { id: DEFER2_ID } },
+ },
+ });
+ updateList.push({
+ id: DEFER2_ID,
+ version: "2.0",
+ path: "system_delay_defer_2.xpi",
+ xpi,
+ });
+
+ xpi = await createTempWebExtensionFile({
+ background: delayBackground,
+ manifest: {
+ version: "1.0",
+ browser_specific_settings: { gecko: { id: DEFER_ALSO_ID } },
+ },
+ });
+ xpi.copyTo(distroDir, `${DEFER_ALSO_ID}.xpi`);
+
+ xpi = await createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ browser_specific_settings: { gecko: { id: DEFER_ALSO_ID } },
+ },
+ });
+ updateList.push({
+ id: DEFER_ALSO_ID,
+ version: "2.0",
+ path: "system_delay_defer_also_2.xpi",
+ xpi,
+ });
+
+ await overrideBuiltIns({ system: [DEFER2_ID, DEFER_ALSO_ID] });
+
+ let extension1 = ExtensionTestUtils.expectExtension(DEFER2_ID);
+ let extension2 = ExtensionTestUtils.expectExtension(DEFER_ALSO_ID);
+
+ await Promise.all([
+ promiseStartupManager(),
+ extension1.awaitStartup(),
+ extension2.awaitStartup(),
+ ]);
+
+ await Promise.all([
+ promiseInstallPostponed(DEFER2_ID, DEFER_ALSO_ID),
+ installSystemAddons(buildSystemAddonUpdates(updateList)),
+ extension1.awaitMessage("got-update"),
+ extension2.awaitMessage("got-update"),
+ ]);
+
+ // upgrade is initially postponed
+ await checkAddon(DEFER2_ID, { version: "1.0" });
+ // other addons in the set are postponed as well.
+ await checkAddon(DEFER_ALSO_ID, { version: "1.0" });
+
+ let deferred = promiseInstallDeferred(DEFER2_ID, DEFER_ALSO_ID);
+
+ // Let one extension request that the update proceed.
+ extension1.setRestarting();
+ extension1.sendMessage("reload");
+
+ // Upgrade blockers still present.
+ await checkAddon(DEFER2_ID, { version: "1.0" });
+ await checkAddon(DEFER_ALSO_ID, { version: "1.0" });
+
+ // Let the second extension allow the update to proceed.
+ extension2.setRestarting();
+ extension2.sendMessage("reload");
+
+ await Promise.all([
+ deferred,
+ extension1.awaitStartup(),
+ extension2.awaitStartup(),
+ ]);
+
+ // addon upgrade has been allowed
+ await checkAddon(DEFER2_ID, { version: "2.0" });
+ await checkAddon(DEFER_ALSO_ID, { version: "2.0" });
+
+ // restarting changes nothing
+ await Promise.all([
+ promiseRestartManager(),
+ extension1.awaitStartup(),
+ extension2.awaitStartup(),
+ ]);
+
+ await checkAddon(DEFER2_ID, { version: "2.0" });
+ await checkAddon(DEFER_ALSO_ID, { version: "2.0" });
+
+ await promiseShutdownManager();
+});