diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/mozapps/extensions/test/xpcshell/test_delay_update_webextension.js | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_delay_update_webextension.js b/toolkit/mozapps/extensions/test/xpcshell/test_delay_update_webextension.js new file mode 100644 index 0000000000..7b1c6fbef9 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_delay_update_webextension.js @@ -0,0 +1,556 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This verifies that delaying an update works for WebExtensions. + +// The test extension uses an insecure update url. +Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false); + +if (AppConstants.platform == "win" && AppConstants.DEBUG) { + // Shutdown timing is flaky in this test, and remote extensions + // sometimes wind up leaving the XPI locked at the point when we try + // to remove it. + Services.prefs.setBoolPref("extensions.webextensions.remote", false); +} + +PromiseTestUtils.allowMatchingRejectionsGlobally( + /Message manager disconnected/ +); + +/* globals browser*/ + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); +const stageDir = profileDir.clone(); +stageDir.append("staged"); + +const IGNORE_ID = "test_delay_update_ignore_webext@tests.mozilla.org"; +const COMPLETE_ID = "test_delay_update_complete_webext@tests.mozilla.org"; +const DEFER_ID = "test_delay_update_defer_webext@tests.mozilla.org"; +const STAGED_ID = "test_delay_update_staged_webext@tests.mozilla.org"; +const STAGED_NO_UPDATE_URL_ID = + "test_delay_update_staged_webext_no_update_url@tests.mozilla.org"; +const NOUPDATE_ID = "test_no_update_webext@tests.mozilla.org"; + +// Create and configure the HTTP server. +var testserver = AddonTestUtils.createHttpServer({ hosts: ["example.com"] }); +testserver.registerDirectory("/data/", do_get_file("data")); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42", "42"); +BootstrapMonitor.init(); + +const ADDONS = { + test_delay_update_complete_webextension_v2: { + "manifest.json": { + manifest_version: 2, + name: "Delay Upgrade", + version: "2.0", + browser_specific_settings: { + gecko: { id: COMPLETE_ID }, + }, + }, + }, + test_delay_update_defer_webextension_v2: { + "manifest.json": { + manifest_version: 2, + name: "Delay Upgrade", + version: "2.0", + browser_specific_settings: { + gecko: { id: DEFER_ID }, + }, + }, + }, + test_delay_update_staged_webextension_v2: { + "manifest.json": { + manifest_version: 2, + name: "Delay Upgrade", + version: "2.0", + browser_specific_settings: { + gecko: { + id: STAGED_ID, + update_url: `http://example.com/data/test_delay_updates_staged.json`, + strict_min_version: "1", + strict_max_version: "41", + }, + }, + }, + }, + test_delay_update_staged_webextension_no_update_url_v2: { + "manifest.json": { + manifest_version: 2, + name: "Delay Upgrade", + version: "2.0", + browser_specific_settings: { + gecko: { + id: STAGED_NO_UPDATE_URL_ID, + strict_min_version: "1", + strict_max_version: "41", + }, + }, + }, + }, + test_delay_update_ignore_webextension_v2: { + "manifest.json": { + manifest_version: 2, + name: "Delay Upgrade", + version: "2.0", + browser_specific_settings: { + gecko: { id: IGNORE_ID }, + }, + }, + }, +}; + +const XPIS = {}; +for (let [name, files] of Object.entries(ADDONS)) { + XPIS[name] = AddonTestUtils.createTempXPIFile(files); + testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]); +} + +// add-on registers upgrade listener, and ignores update. +add_task(async function delay_updates_ignore() { + await promiseStartupManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + browser_specific_settings: { + gecko: { + id: IGNORE_ID, + update_url: `http://example.com/data/test_delay_updates_ignore.json`, + }, + }, + }, + background() { + browser.runtime.onUpdateAvailable.addListener(details => { + if (details) { + if (details.version) { + // This should be the version of the pending update. + browser.test.assertEq("2.0", details.version, "correct version"); + browser.test.notifyPass("delay"); + } + } else { + browser.test.fail("no details object passed"); + } + }); + browser.test.sendMessage("ready"); + }, + }); + + await Promise.all([extension.startup(), extension.awaitMessage("ready")]); + BootstrapMonitor.checkInstalled(IGNORE_ID, "1.0"); + + let addon = await promiseAddonByID(IGNORE_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + let update = await promiseFindAddonUpdates(addon); + let install = update.updateAvailable; + + await promiseCompleteAllInstalls([install]); + + Assert.equal(install.state, AddonManager.STATE_POSTPONED); + BootstrapMonitor.checkInstalled(IGNORE_ID, "1.0"); + + // addon upgrade has been delayed + let addon_postponed = await promiseAddonByID(IGNORE_ID); + Assert.notEqual(addon_postponed, null); + Assert.equal(addon_postponed.version, "1.0"); + Assert.equal(addon_postponed.name, "Generated extension"); + Assert.ok(addon_postponed.isCompatible); + Assert.ok(!addon_postponed.appDisabled); + Assert.ok(addon_postponed.isActive); + Assert.equal(addon_postponed.type, "extension"); + + await extension.awaitFinish("delay"); + + // restarting allows upgrade to proceed + await promiseRestartManager(); + + let addon_upgraded = await promiseAddonByID(IGNORE_ID); + await extension.awaitStartup(); + BootstrapMonitor.checkUpdated(IGNORE_ID, "2.0"); + + Assert.notEqual(addon_upgraded, null); + Assert.equal(addon_upgraded.version, "2.0"); + Assert.equal(addon_upgraded.name, "Delay Upgrade"); + Assert.ok(addon_upgraded.isCompatible); + Assert.ok(!addon_upgraded.appDisabled); + Assert.ok(addon_upgraded.isActive); + Assert.equal(addon_upgraded.type, "extension"); + + await extension.unload(); + await promiseShutdownManager(); +}); + +// add-on registers upgrade listener, and allows update. +add_task(async function delay_updates_complete() { + await promiseStartupManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + browser_specific_settings: { + gecko: { + id: COMPLETE_ID, + update_url: `http://example.com/data/test_delay_updates_complete.json`, + }, + }, + }, + background() { + browser.runtime.onUpdateAvailable.addListener(details => { + browser.test.notifyPass("reload"); + browser.runtime.reload(); + }); + browser.test.sendMessage("ready"); + }, + }); + + await Promise.all([extension.startup(), extension.awaitMessage("ready")]); + + let addon = await promiseAddonByID(COMPLETE_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + let update = await promiseFindAddonUpdates(addon); + let install = update.updateAvailable; + + let promiseInstalled = promiseAddonEvent("onInstalled"); + await promiseCompleteAllInstalls([install]); + + await extension.awaitFinish("reload"); + + // addon upgrade has been allowed + let [addon_allowed] = await promiseInstalled; + await extension.awaitStartup(); + + Assert.notEqual(addon_allowed, null); + Assert.equal(addon_allowed.version, "2.0"); + Assert.equal(addon_allowed.name, "Delay Upgrade"); + Assert.ok(addon_allowed.isCompatible); + Assert.ok(!addon_allowed.appDisabled); + Assert.ok(addon_allowed.isActive); + Assert.equal(addon_allowed.type, "extension"); + + await new Promise(executeSoon); + + if (stageDir.exists()) { + do_throw( + "Staging directory should not exist for formerly-postponed extension" + ); + } + + await extension.unload(); + await promiseShutdownManager(); +}); + +// add-on registers upgrade listener, initially defers update then allows upgrade +add_task(async function delay_updates_defer() { + await promiseStartupManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + browser_specific_settings: { + gecko: { + id: DEFER_ID, + update_url: `http://example.com/data/test_delay_updates_defer.json`, + }, + }, + }, + background() { + browser.runtime.onUpdateAvailable.addListener(details => { + // Upgrade will only proceed when "allow" message received. + browser.test.onMessage.addListener(msg => { + if (msg == "allow") { + browser.test.notifyPass("allowed"); + browser.runtime.reload(); + } else { + browser.test.fail(`wrong message: ${msg}`); + } + }); + browser.test.sendMessage("truly ready"); + }); + browser.test.sendMessage("ready"); + }, + }); + + await Promise.all([extension.startup(), extension.awaitMessage("ready")]); + + let addon = await promiseAddonByID(DEFER_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + let update = await promiseFindAddonUpdates(addon); + let install = update.updateAvailable; + + let promiseInstalled = promiseAddonEvent("onInstalled"); + await promiseCompleteAllInstalls([install]); + + Assert.equal(install.state, AddonManager.STATE_POSTPONED); + + // upgrade is initially postponed + let addon_postponed = await promiseAddonByID(DEFER_ID); + Assert.notEqual(addon_postponed, null); + Assert.equal(addon_postponed.version, "1.0"); + Assert.equal(addon_postponed.name, "Generated extension"); + Assert.ok(addon_postponed.isCompatible); + Assert.ok(!addon_postponed.appDisabled); + Assert.ok(addon_postponed.isActive); + Assert.equal(addon_postponed.type, "extension"); + + // add-on will not allow upgrade until message is received + await extension.awaitMessage("truly ready"); + extension.sendMessage("allow"); + await extension.awaitFinish("allowed"); + + // addon upgrade has been allowed + let [addon_allowed] = await promiseInstalled; + await extension.awaitStartup(); + + Assert.notEqual(addon_allowed, null); + Assert.equal(addon_allowed.version, "2.0"); + Assert.equal(addon_allowed.name, "Delay Upgrade"); + Assert.ok(addon_allowed.isCompatible); + Assert.ok(!addon_allowed.appDisabled); + Assert.ok(addon_allowed.isActive); + Assert.equal(addon_allowed.type, "extension"); + + await promiseRestartManager(); + + // restart changes nothing + addon_allowed = await promiseAddonByID(DEFER_ID); + await extension.awaitStartup(); + + Assert.notEqual(addon_allowed, null); + Assert.equal(addon_allowed.version, "2.0"); + Assert.equal(addon_allowed.name, "Delay Upgrade"); + Assert.ok(addon_allowed.isCompatible); + Assert.ok(!addon_allowed.appDisabled); + Assert.ok(addon_allowed.isActive); + Assert.equal(addon_allowed.type, "extension"); + + await extension.unload(); + await promiseShutdownManager(); +}); + +// add-on registers upgrade listener to deny update, completes after restart, +// even though the updated XPI is incompatible - the information returned +// by the update server defined in its manifest returns a compatible range +add_task(async function delay_updates_staged() { + await promiseStartupManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + browser_specific_settings: { + gecko: { + id: STAGED_ID, + update_url: `http://example.com/data/test_delay_updates_staged.json`, + }, + }, + }, + background() { + browser.runtime.onUpdateAvailable.addListener(details => { + browser.test.sendMessage("denied"); + }); + browser.test.sendMessage("ready"); + }, + }); + + await Promise.all([extension.startup(), extension.awaitMessage("ready")]); + + let addon = await promiseAddonByID(STAGED_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + let update = await promiseFindAddonUpdates(addon); + let install = update.updateAvailable; + await promiseCompleteAllInstalls([install]); + + Assert.equal(install.state, AddonManager.STATE_POSTPONED); + + // upgrade is initially postponed + let addon_postponed = await promiseAddonByID(STAGED_ID); + Assert.notEqual(addon_postponed, null); + Assert.equal(addon_postponed.version, "1.0"); + Assert.equal(addon_postponed.name, "Generated extension"); + Assert.ok(addon_postponed.isCompatible); + Assert.ok(!addon_postponed.appDisabled); + Assert.ok(addon_postponed.isActive); + Assert.equal(addon_postponed.type, "extension"); + + // add-on reports an available upgrade, but denied it till next restart + await extension.awaitMessage("denied"); + + await promiseRestartManager(); + await extension.awaitStartup(); + + // add-on should have been updated during restart + let addon_upgraded = await promiseAddonByID(STAGED_ID); + Assert.notEqual(addon_upgraded, null); + Assert.equal(addon_upgraded.version, "2.0"); + Assert.equal(addon_upgraded.name, "Delay Upgrade"); + Assert.ok(addon_upgraded.isCompatible); + Assert.ok(!addon_upgraded.appDisabled); + Assert.ok(addon_upgraded.isActive); + Assert.equal(addon_upgraded.type, "extension"); + + await extension.unload(); + await promiseShutdownManager(); +}); + +// add-on registers upgrade listener to deny update, does not complete after +// restart, because the updated XPI is incompatible - there is no update server +// defined in its manifest, which could return a compatible range +add_task(async function delay_updates_staged_no_update_url() { + await promiseStartupManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + browser_specific_settings: { + gecko: { + id: STAGED_NO_UPDATE_URL_ID, + update_url: `http://example.com/data/test_delay_updates_staged.json`, + }, + }, + }, + background() { + browser.runtime.onUpdateAvailable.addListener(details => { + browser.test.sendMessage("denied"); + }); + browser.test.sendMessage("ready"); + }, + }); + + await Promise.all([extension.startup(), extension.awaitMessage("ready")]); + + let addon = await promiseAddonByID(STAGED_NO_UPDATE_URL_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + let update = await promiseFindAddonUpdates(addon); + let install = update.updateAvailable; + await promiseCompleteAllInstalls([install]); + + Assert.equal(install.state, AddonManager.STATE_POSTPONED); + + // upgrade is initially postponed + let addon_postponed = await promiseAddonByID(STAGED_NO_UPDATE_URL_ID); + Assert.notEqual(addon_postponed, null); + Assert.equal(addon_postponed.version, "1.0"); + Assert.equal(addon_postponed.name, "Generated extension"); + Assert.ok(addon_postponed.isCompatible); + Assert.ok(!addon_postponed.appDisabled); + Assert.ok(addon_postponed.isActive); + Assert.equal(addon_postponed.type, "extension"); + + // add-on reports an available upgrade, but denied it till next restart + await extension.awaitMessage("denied"); + + await promiseRestartManager(); + await extension.awaitStartup(); + + // add-on should not have been updated during restart + let addon_upgraded = await promiseAddonByID(STAGED_NO_UPDATE_URL_ID); + Assert.notEqual(addon_upgraded, null); + Assert.equal(addon_upgraded.version, "1.0"); + Assert.equal(addon_upgraded.name, "Generated extension"); + Assert.ok(addon_upgraded.isCompatible); + Assert.ok(!addon_upgraded.appDisabled); + Assert.ok(addon_upgraded.isActive); + Assert.equal(addon_upgraded.type, "extension"); + + await extension.unload(); + await promiseShutdownManager(); +}); + +// browser.runtime.reload() without a pending upgrade should just reload. +add_task(async function runtime_reload() { + await promiseStartupManager(); + + let extension = ExtensionTestUtils.loadExtension({ + useAddonManager: "permanent", + manifest: { + version: "1.0", + browser_specific_settings: { + gecko: { + id: NOUPDATE_ID, + update_url: `http://example.com/data/test_no_update.json`, + }, + }, + }, + background() { + browser.test.onMessage.addListener(msg => { + if (msg == "reload") { + browser.runtime.reload(); + } else { + browser.test.fail(`wrong message: ${msg}`); + } + }); + browser.test.sendMessage("ready"); + }, + }); + + await Promise.all([extension.startup(), extension.awaitMessage("ready")]); + + let addon = await promiseAddonByID(NOUPDATE_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + await promiseFindAddonUpdates(addon); + + extension.sendMessage("reload"); + // Wait for extension to restart, to make sure reload works. + await AddonTestUtils.promiseWebExtensionStartup(NOUPDATE_ID); + await extension.awaitMessage("ready"); + + addon = await promiseAddonByID(NOUPDATE_ID); + Assert.notEqual(addon, null); + Assert.equal(addon.version, "1.0"); + Assert.equal(addon.name, "Generated extension"); + Assert.ok(addon.isCompatible); + Assert.ok(!addon.appDisabled); + Assert.ok(addon.isActive); + Assert.equal(addon.type, "extension"); + + await extension.unload(); + await promiseShutdownManager(); +}); |