diff options
Diffstat (limited to 'toolkit/mozapps/extensions/test/browser/browser_local_install.js')
-rw-r--r-- | toolkit/mozapps/extensions/test/browser/browser_local_install.js | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/browser/browser_local_install.js b/toolkit/mozapps/extensions/test/browser/browser_local_install.js new file mode 100644 index 0000000000..5200b69e39 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_local_install.js @@ -0,0 +1,245 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +AddonTestUtils.initMochitest(this); + +const XPI_INCOMPATIBLE_ID = "incompatible-xpi@tests.mozilla.org"; +// NOTE: we are using an HTTP url on purpose here, the test case fails +// otherwise... We disable `AddonManager.checkUpdateSecurity` to allow +// retrieving updates from HTTP (which is restored in a +// `registerCleanupFunction()` or at the end of the task). +// +// eslint-disable-next-line @microsoft/sdl/no-insecure-url +const BASE_URL = "http://fake-updates.example.com"; + +const server = AddonTestUtils.createHttpServer({ + hosts: ["fake-updates.example.com"], +}); + +const UPDATE_ENTRY_COMPATIBLE = { + // NOTE: this version must be the exact same one associated than the + // initially incompatible XPI, otherwise it won't override the initial + // compatibility range. + // See the check in `AddonUpdateChecker.getCompatibilityUpdate` here: + // https://searchfox.org/mozilla-central/rev/4044c340/toolkit/mozapps/extensions/internal/AddonUpdateChecker.sys.mjs#489 + version: "4.0", + // An empty compatibility range will make this update to be overriding the + // incompatible range in the xpi and makes the xpi version to be considered + // compatible. + applications: { gecko: {} }, +}; + +const UPDATE_ENTRY_INCOMPATIBLE = { + ...UPDATE_ENTRY_COMPATIBLE, + // This update entry instead is including a compatibility range that would + // makes the xpi version being installed to be considered still incompatible. + applications: { + gecko: { + strict_min_version: "41", + strict_max_version: "41.*", + }, + }, +}; + +AddonTestUtils.registerJSON(server, "/updates-still-incompatible.json", { + addons: { + [XPI_INCOMPATIBLE_ID]: { + updates: [UPDATE_ENTRY_INCOMPATIBLE], + }, + }, +}); + +AddonTestUtils.registerJSON(server, "/updates-now-compatible.json", { + addons: { + [XPI_INCOMPATIBLE_ID]: { + updates: [UPDATE_ENTRY_COMPATIBLE], + }, + }, +}); + +add_task(async function test_local_install_blocklisted() { + let id = "amosigned-xpi@tests.mozilla.org"; + let version = "2.1"; + + await AddonTestUtils.loadBlocklistRawData({ + extensionsMLBF: [ + { + stash: { blocked: [`${id}:${version}`], unblocked: [] }, + stash_time: 0, + }, + ], + }); + let needsCleanupBlocklist = true; + const cleanupBlocklist = async () => { + if (!needsCleanupBlocklist) { + return; + } + await AddonTestUtils.loadBlocklistRawData({ + extensionsMLBF: [ + { + stash: { blocked: [], unblocked: [] }, + stash_time: 0, + }, + ], + }); + needsCleanupBlocklist = false; + }; + registerCleanupFunction(cleanupBlocklist); + + const xpiFilePath = getTestFilePath("../xpinstall/amosigned.xpi"); + const xpiFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + xpiFile.initWithPath(xpiFilePath); + ok(xpiFile.exists(), "Expect the xpi file to exist"); + const xpiFileURI = Services.io.newFileURI(xpiFile); + + let install = await AddonManager.getInstallForURL(xpiFileURI.spec, { + telemetryInfo: { source: "file-url" }, + }); + const promiseInstallFailed = BrowserUtils.promiseObserved( + "addon-install-failed", + subject => { + return subject.wrappedJSObject.installs[0] == install; + } + ); + + AddonManager.installAddonFromWebpage( + "application/x-xpinstall", + gBrowser.selectedBrowser, + Services.scriptSecurityManager.getSystemPrincipal(), + install + ); + + info("Wait for addon-install-failed to be notified"); + await promiseInstallFailed; + Assert.equal( + install.error, + AddonManager.ERROR_BLOCKLISTED, + "LocalInstall cancelled with the expected error" + ); + + await cleanupBlocklist(); +}); + +add_task(async function test_local_install_incompatible() { + const xpiFilePath = getTestFilePath("../xpinstall/incompatible.xpi"); + const xpiFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + xpiFile.initWithPath(xpiFilePath); + ok(xpiFile.exists(), "Expect the xpi file to exist"); + const xpiFileURI = Services.io.newFileURI(xpiFile); + + const installTestExtension = async ({ expectIncompatible }) => { + let install = await AddonManager.getInstallForURL(xpiFileURI.spec, { + telemetryInfo: { source: "file-url" }, + }); + const promiseInstallDone = expectIncompatible + ? BrowserUtils.promiseObserved( + "addon-install-failed", + subject => subject.wrappedJSObject.installs[0] == install + ) + : BrowserUtils.promiseObserved( + "webextension-permission-prompt", + subject => subject.wrappedJSObject.info.addon == install.addon + ); + + AddonManager.installAddonFromWebpage( + "application/x-xpinstall", + gBrowser.selectedBrowser, + Services.scriptSecurityManager.getSystemPrincipal(), + install + ); + + if (expectIncompatible) { + info("Wait for addon-install-failed to be notified"); + await promiseInstallDone; + Assert.equal( + install.error, + AddonManager.ERROR_INCOMPATIBLE, + "LocalInstall cancelled with the expected error" + ); + } else { + info("Wait for webextension-permission-prompt to be notified"); + await promiseInstallDone; + Assert.equal( + install.error, + 0, + "no error expected on the LocalInstall instance" + ); + Assert.equal( + install.state, + AddonManager.STATE_DOWNLOADED, + "Got the expected LocalInstall state" + ); + Assert.ok( + install.addon.isCompatible, + "updated Addon XPI is expected to be compatible" + ); + Assert.equal( + install.addon.version, + "4.0", + "Addon version expected to match the updated xpi file" + ); + // Cancel the installation, before exiting the test. + await install.cancel(); + } + }; + + info("Test incompatible xpi without a compatibility override"); + // Use a new tab to make sure the doorhanger will be gone when + // the test tab is being removed (same when repeating the + // test with expectIncompatible set to false). + await BrowserTestUtils.withNewTab("about:blank", async () => { + await installTestExtension({ expectIncompatible: true }); + }); + + // Add the prefs to ignore signature checks for this test (allowed on all + // channels while running in automation). + SpecialPowers.pushPrefEnv({ + set: [ + ["extensions.update.url", `${BASE_URL}/updates.json`], + ["xpinstall.signatures.required", false], + ["extensions.ui.ignoreUnsigned", true], + ], + }); + AddonManager.checkUpdateSecurity = false; + registerCleanupFunction(() => { + AddonManager.checkUpdateSecurity = true; + }); + + info( + "Test incompatible xpi with a compatibility override that is still incompatible" + ); + // Add the prefs to provide a compatibility range override which is still + // incompatible. + SpecialPowers.pushPrefEnv({ + set: [ + ["extensions.update.url", `${BASE_URL}/updates-still-incompatible.json`], + ], + }); + await BrowserTestUtils.withNewTab("about:blank", async () => { + await installTestExtension({ expectIncompatible: true }); + }); + SpecialPowers.popPrefEnv(); + + info( + "Test incompatible xpi with a compatibility override that makes it compatible" + ); + // Add the prefs to provide a compatibility range override which is + // compatible. + SpecialPowers.pushPrefEnv({ + set: [["extensions.update.url", `${BASE_URL}/updates-now-compatible.json`]], + }); + await BrowserTestUtils.withNewTab("about:blank", async () => { + await installTestExtension({ expectIncompatible: false }); + }); + SpecialPowers.popPrefEnv(); + + SpecialPowers.popPrefEnv(); + AddonManager.checkUpdateSecurity = true; +}); |