From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../test/xpcshell/test_webextension_install.js | 694 +++++++++++++++++++++ 1 file changed, 694 insertions(+) create mode 100644 toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js') diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js new file mode 100644 index 0000000000..c01f3ad703 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js @@ -0,0 +1,694 @@ +let profileDir; +add_task(async function setup() { + profileDir = gProfD.clone(); + profileDir.append("extensions"); + + if (!profileDir.exists()) { + profileDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + } + + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + await promiseStartupManager(); +}); + +const IMPLICIT_ID_XPI = "data/webext-implicit-id.xpi"; +const IMPLICIT_ID_ID = "webext_implicit_id@tests.mozilla.org"; + +// webext-implicit-id.xpi has a minimal manifest with no +// applications or browser_specific_settings, so its id comes +// from its signature, which should be the ID constant defined below. +add_task(async function test_implicit_id() { + let addon = await promiseAddonByID(IMPLICIT_ID_ID); + equal(addon, null, "Add-on is not installed"); + + await promiseInstallFile(do_get_file(IMPLICIT_ID_XPI)); + + addon = await promiseAddonByID(IMPLICIT_ID_ID); + notEqual(addon, null, "Add-on is installed"); + + await addon.uninstall(); +}); + +// We should also be able to install webext-implicit-id.xpi temporarily +// and it should look just like the regular install (ie, the ID should +// come from the signature) +add_task(async function test_implicit_id_temp() { + let addon = await promiseAddonByID(IMPLICIT_ID_ID); + equal(addon, null, "Add-on is not installed"); + + let xpifile = do_get_file(IMPLICIT_ID_XPI); + await AddonManager.installTemporaryAddon(xpifile); + + addon = await promiseAddonByID(IMPLICIT_ID_ID); + notEqual(addon, null, "Add-on is installed"); + + // The sourceURI of a temporary installed addon should be equal to the + // file url of the installed xpi file. + equal( + addon.sourceURI && addon.sourceURI.spec, + Services.io.newFileURI(xpifile).spec, + "SourceURI of the add-on has the expected value" + ); + + await addon.uninstall(); +}); + +// Test that extension install error attach the detailed error messages to the +// Error object. +add_task(async function test_invalid_extension_install_errors() { + const manifest = { + name: "invalid", + browser_specific_settings: { + gecko: { + id: "invalid@tests.mozilla.org", + }, + }, + description: "extension with an invalid 'matches' value", + manifest_version: 2, + content_scripts: [ + { + matches: "*://*.foo.com/*", + js: ["content.js"], + }, + ], + version: "1.0", + }; + + const addonDir = await promiseWriteWebManifestForExtension( + manifest, + gTmpD, + "the-addon-sub-dir" + ); + + await Assert.rejects( + AddonManager.installTemporaryAddon(addonDir), + err => { + return ( + err.additionalErrors.length == 1 && + err.additionalErrors[0] == + `Reading manifest: Error processing content_scripts.0.matches: ` + + `Expected array instead of "*://*.foo.com/*"` + ); + }, + "Exception has the proper additionalErrors details" + ); + + Services.obs.notifyObservers(addonDir, "flush-cache-entry"); + addonDir.remove(true); +}); + +// We should be able to temporarily install an unsigned web extension +// that does not have an ID in its manifest. +add_task(async function test_unsigned_no_id_temp_install() { + AddonTestUtils.useRealCertChecks = true; + const manifest = { + name: "no ID", + description: "extension without an ID", + manifest_version: 2, + version: "1.0", + }; + + const addonDir = await promiseWriteWebManifestForExtension( + manifest, + gTmpD, + "the-addon-sub-dir" + ); + const testDate = new Date(); + const addon = await AddonManager.installTemporaryAddon(addonDir); + + ok(addon.id, "ID should have been auto-generated"); + ok( + Math.abs(addon.installDate - testDate) < 10000, + "addon has an expected installDate" + ); + ok( + Math.abs(addon.updateDate - testDate) < 10000, + "addon has an expected updateDate" + ); + + // The sourceURI of a temporary installed addon should be equal to the + // file url of the installed source dir. + equal( + addon.sourceURI && addon.sourceURI.spec, + Services.io.newFileURI(addonDir).spec, + "SourceURI of the add-on has the expected value" + ); + + // Install the same directory again, as if re-installing or reloading. + const secondAddon = await AddonManager.installTemporaryAddon(addonDir); + // The IDs should be the same. + equal(secondAddon.id, addon.id, "Reinstalled add-on has the expected ID"); + equal( + secondAddon.installDate.valueOf(), + addon.installDate.valueOf(), + "Reloaded add-on has the expected installDate." + ); + + await secondAddon.uninstall(); + Services.obs.notifyObservers(addonDir, "flush-cache-entry"); + addonDir.remove(true); + AddonTestUtils.useRealCertChecks = false; +}); + +// We should be able to install two extensions from manifests without IDs +// at different locations and get two unique extensions. +add_task(async function test_multiple_no_id_extensions() { + AddonTestUtils.useRealCertChecks = true; + const manifest = { + name: "no ID", + description: "extension without an ID", + manifest_version: 2, + version: "1.0", + }; + + let extension1 = ExtensionTestUtils.loadExtension({ + manifest, + useAddonManager: "temporary", + }); + + let extension2 = ExtensionTestUtils.loadExtension({ + manifest, + useAddonManager: "temporary", + }); + + await Promise.all([extension1.startup(), extension2.startup()]); + + const allAddons = await AddonManager.getAllAddons(); + + info(`Found these add-ons: ${allAddons.map(a => a.name).join(", ")}`); + const filtered = allAddons.filter(addon => addon.name === manifest.name); + // Make sure we have two add-ons by the same name. + equal(filtered.length, 2, "Two add-ons are installed with the same name"); + + await extension1.unload(); + await extension2.unload(); + AddonTestUtils.useRealCertChecks = false; +}); + +// Test that we can get the ID from browser_specific_settings +add_task(async function test_bss_id() { + const ID = "webext_bss_id@tests.mozilla.org"; + + let manifest = { + name: "bss test", + description: "test that ID may be in browser_specific_settings", + manifest_version: 2, + version: "1.0", + + browser_specific_settings: { + gecko: { + id: ID, + }, + }, + }; + + let addon = await promiseAddonByID(ID); + equal(addon, null, "Add-on is not installed"); + + let extension = ExtensionTestUtils.loadExtension({ + manifest, + useAddonManager: "temporary", + }); + await extension.startup(); + + addon = await promiseAddonByID(ID); + notEqual(addon, null, "Add-on is installed"); + + await extension.unload(); +}); + +// Test that if we have IDs in both browser_specific_settings and applications, +// that we prefer the ID in browser_specific_settings. +add_task(async function test_two_ids() { + const GOOD_ID = "two_ids@tests.mozilla.org"; + const BAD_ID = "i_am_obsolete@tests.mozilla.org"; + + let manifest = { + name: "two id test", + description: + "test a web extension with ids in both applications and browser_specific_settings", + manifest_version: 2, + version: "1.0", + + applications: { + gecko: { + id: BAD_ID, + }, + }, + + browser_specific_settings: { + gecko: { + id: GOOD_ID, + }, + }, + }; + + let extension = ExtensionTestUtils.loadExtension({ + manifest, + useAddonManager: "temporary", + }); + await extension.startup(); + + let addon = await promiseAddonByID(BAD_ID); + equal(addon, null, "Add-on is not found using bad ID"); + addon = await promiseAddonByID(GOOD_ID); + notEqual(addon, null, "Add-on is found using good ID"); + + await extension.unload(); +}); + +// Test that strict_min_version and strict_max_version are enforced for +// loading temporary extension. +add_task(async function test_strict_min_max() { + // the app version being compared to is 1.9.2 + const addonId = "strict_min_max@tests.mozilla.org"; + const MANIFEST = { + name: "strict min max test", + description: "test strict min and max with temporary loading", + manifest_version: 2, + version: "1.0", + }; + + // bad max good min + let apps = { + browser_specific_settings: { + gecko: { + id: addonId, + strict_min_version: "1", + strict_max_version: "1", + }, + }, + }; + let testManifest = Object.assign(apps, MANIFEST); + + let extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + let expectedMsg = new RegExp( + "Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " + + "add-on minVersion: 1. add-on maxVersion: 1." + ); + await Assert.rejects( + extension.startup(), + expectedMsg, + "Install rejects when specified maxVersion is not valid" + ); + + let addon = await promiseAddonByID(addonId); + equal(addon, null, "Add-on is not installed"); + + // bad min good max + apps = { + browser_specific_settings: { + gecko: { + id: addonId, + strict_min_version: "2", + strict_max_version: "2", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + expectedMsg = new RegExp( + "Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " + + "add-on minVersion: 2. add-on maxVersion: 2." + ); + await Assert.rejects( + extension.startup(), + expectedMsg, + "Install rejects when specified minVersion is not valid" + ); + + addon = await promiseAddonByID(addonId); + equal(addon, null, "Add-on is not installed"); + + // bad both + apps = { + browser_specific_settings: { + gecko: { + id: addonId, + strict_min_version: "2", + strict_max_version: "1", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + expectedMsg = new RegExp( + "Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " + + "add-on minVersion: 2. add-on maxVersion: 1." + ); + await Assert.rejects( + extension.startup(), + expectedMsg, + "Install rejects when specified minVersion and maxVersion are not valid" + ); + + addon = await promiseAddonByID(addonId); + equal(addon, null, "Add-on is not installed"); + + // bad only min + apps = { + browser_specific_settings: { + gecko: { + id: addonId, + strict_min_version: "2", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + expectedMsg = new RegExp( + "Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " + + "add-on minVersion: 2." + ); + await Assert.rejects( + extension.startup(), + expectedMsg, + "Install rejects when specified minVersion and maxVersion are not valid" + ); + + addon = await promiseAddonByID(addonId); + equal(addon, null, "Add-on is not installed"); + + // bad only max + apps = { + browser_specific_settings: { + gecko: { + id: addonId, + strict_max_version: "1", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + expectedMsg = new RegExp( + "Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " + + "add-on maxVersion: 1." + ); + await Assert.rejects( + extension.startup(), + expectedMsg, + "Install rejects when specified minVersion and maxVersion are not valid" + ); + + addon = await promiseAddonByID(addonId); + equal(addon, null, "Add-on is not installed"); + + // good both + apps = { + browser_specific_settings: { + gecko: { + id: addonId, + strict_min_version: "1", + strict_max_version: "2", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + await extension.startup(); + addon = await promiseAddonByID(addonId); + + notEqual(addon, null, "Add-on is installed"); + equal(addon.id, addonId, "Installed add-on has the expected ID"); + await extension.unload(); + + // good only min + let newId = "strict_min_only@tests.mozilla.org"; + apps = { + browser_specific_settings: { + gecko: { + id: newId, + strict_min_version: "1", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + await extension.startup(); + addon = await promiseAddonByID(newId); + + notEqual(addon, null, "Add-on is installed"); + equal(addon.id, newId, "Installed add-on has the expected ID"); + + await extension.unload(); + + // good only max + newId = "strict_max_only@tests.mozilla.org"; + apps = { + browser_specific_settings: { + gecko: { + id: newId, + strict_max_version: "2", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + await extension.startup(); + addon = await promiseAddonByID(newId); + + notEqual(addon, null, "Add-on is installed"); + equal(addon.id, newId, "Installed add-on has the expected ID"); + + await extension.unload(); + + // * in min will throw an error + for (let version of ["0.*", "0.*.0"]) { + newId = "strict_min_star@tests.mozilla.org"; + let minStarApps = { + browser_specific_settings: { + gecko: { + id: newId, + strict_min_version: version, + }, + }, + }; + + let minStarTestManifest = Object.assign(minStarApps, MANIFEST); + + let minStarExtension = ExtensionTestUtils.loadExtension({ + manifest: minStarTestManifest, + useAddonManager: "temporary", + }); + + await Assert.rejects( + minStarExtension.startup(), + /The use of '\*' in strict_min_version is invalid/, + "loading an extension with a * in strict_min_version throws an exception" + ); + + let minStarAddon = await promiseAddonByID(newId); + equal(minStarAddon, null, "Add-on is not installed"); + } + + // incompatible extension but with compatibility checking off + newId = "checkCompatibility@tests.mozilla.org"; + apps = { + browser_specific_settings: { + gecko: { + id: newId, + strict_max_version: "1", + }, + }, + }; + testManifest = Object.assign(apps, MANIFEST); + + extension = ExtensionTestUtils.loadExtension({ + manifest: testManifest, + useAddonManager: "temporary", + }); + + let savedCheckCompatibilityValue = AddonManager.checkCompatibility; + AddonManager.checkCompatibility = false; + await extension.startup(); + addon = await promiseAddonByID(newId); + + notEqual(addon, null, "Add-on is installed"); + equal(addon.id, newId, "Installed add-on has the expected ID"); + + await extension.unload(); + AddonManager.checkCompatibility = savedCheckCompatibilityValue; +}); + +// Check permissions prompt +add_task(async function test_permissions_prompt() { + const manifest = { + name: "permissions test", + description: "permissions test", + manifest_version: 2, + version: "1.0", + + permissions: ["tabs", "storage", "https://*.example.com/*", ""], + }; + + let xpi = ExtensionTestCommon.generateXPI({ manifest }); + + let install = await AddonManager.getInstallForFile(xpi); + + let perminfo; + install.promptHandler = info => { + perminfo = info; + return Promise.resolve(); + }; + + await install.install(); + + notEqual(perminfo, undefined, "Permission handler was invoked"); + equal( + perminfo.existingAddon, + null, + "Permission info does not include an existing addon" + ); + notEqual(perminfo.addon, null, "Permission info includes the new addon"); + let perms = perminfo.addon.userPermissions; + deepEqual( + perms.permissions, + ["tabs", "storage"], + "API permissions are correct" + ); + deepEqual( + perms.origins, + ["https://*.example.com/*", ""], + "Host permissions are correct" + ); + + let addon = await promiseAddonByID(perminfo.addon.id); + notEqual(addon, null, "Extension was installed"); + + await addon.uninstall(); + await IOUtils.remove(xpi.path); +}); + +// Check permissions prompt cancellation +add_task(async function test_permissions_prompt_cancel() { + const manifest = { + name: "permissions test", + description: "permissions test", + manifest_version: 2, + version: "1.0", + + permissions: ["webRequestBlocking"], + }; + + let xpi = ExtensionTestCommon.generateXPI({ manifest }); + + let install = await AddonManager.getInstallForFile(xpi); + + let perminfo; + install.promptHandler = info => { + perminfo = info; + return Promise.reject(); + }; + + await promiseCompleteInstall(install); + + notEqual(perminfo, undefined, "Permission handler was invoked"); + + let addon = await promiseAddonByID(perminfo.addon.id); + equal(addon, null, "Extension was not installed"); + + await IOUtils.remove(xpi.path); +}); + +// Test that presence of 'edge' property in 'browser_specific_settings' doesn't prevent installation from completing successfully +add_task(async function test_non_gecko_bss_install() { + const ID = "ms_edge@tests.mozilla.org"; + + const manifest = { + name: "MS Edge and unknown browser test", + description: + "extension with bss properties for 'edge', and 'unknown_browser'", + manifest_version: 2, + version: "1.0", + applications: { gecko: { id: ID } }, + browser_specific_settings: { + edge: { + browser_action_next_to_addressbar: true, + }, + unknown_browser: { + unknown_setting: true, + }, + }, + }; + + const extension = ExtensionTestUtils.loadExtension({ + manifest, + useAddonManager: "temporary", + }); + ExtensionTestUtils.failOnSchemaWarnings(false); + await extension.startup(); + ExtensionTestUtils.failOnSchemaWarnings(true); + + const addon = await promiseAddonByID(ID); + notEqual(addon, null, "Add-on is installed"); + + await extension.unload(); +}); + +// Test that bss overrides applications if both are present. +add_task(async function test_duplicate_bss() { + const ID = "expected@tests.mozilla.org"; + + const manifest = { + manifest_version: 2, + version: "1.0", + applications: { + gecko: { id: "unexpected@tests.mozilla.org" }, + }, + browser_specific_settings: { + gecko: { id: ID }, + }, + }; + + const extension = ExtensionTestUtils.loadExtension({ + manifest, + useAddonManager: "temporary", + }); + ExtensionTestUtils.failOnSchemaWarnings(false); + await extension.startup(); + ExtensionTestUtils.failOnSchemaWarnings(true); + + const addon = await promiseAddonByID(ID); + notEqual(addon, null, "Add-on is installed"); + + await extension.unload(); +}); -- cgit v1.2.3