summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/webextensions/test/xpcshell/head_addons.js')
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/head_addons.js1345
1 files changed, 0 insertions, 1345 deletions
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js b/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
deleted file mode 100644
index 960caceeb..000000000
--- a/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
+++ /dev/null
@@ -1,1345 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-var AM_Cc = Components.classes;
-var AM_Ci = Components.interfaces;
-var AM_Cu = Components.utils;
-
-AM_Cu.importGlobalProperties(["TextEncoder"]);
-
-const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
-const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
-
-const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
-const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
-const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
-const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
-const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
-const PREF_GETADDONS_BYIDS_PERFORMANCE = "extensions.getAddons.getWithPerformance.url";
-const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
-
-// Forcibly end the test if it runs longer than 15 minutes
-const TIMEOUT_MS = 900000;
-
-// Maximum error in file modification times. Some file systems don't store
-// modification times exactly. As long as we are closer than this then it
-// still passes.
-const MAX_TIME_DIFFERENCE = 3000;
-
-// Time to reset file modified time relative to Date.now() so we can test that
-// times are modified (10 hours old).
-const MAKE_FILE_OLD_DIFFERENCE = 10 * 3600 * 1000;
-
-Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/FileUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/NetUtil.jsm");
-Components.utils.import("resource://gre/modules/Promise.jsm");
-Components.utils.import("resource://gre/modules/Task.jsm");
-const { OS } = Components.utils.import("resource://gre/modules/osfile.jsm", {});
-Components.utils.import("resource://gre/modules/AsyncShutdown.jsm");
-
-Components.utils.import("resource://testing-common/AddonTestUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Extension",
- "resource://gre/modules/Extension.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestUtils",
- "resource://testing-common/ExtensionXPCShellUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
- "resource://testing-common/httpd.js");
-XPCOMUtils.defineLazyModuleGetter(this, "MockAsyncShutdown",
- "resource://testing-common/AddonTestUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "MockRegistrar",
- "resource://testing-common/MockRegistrar.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "MockRegistry",
- "resource://testing-common/MockRegistry.jsm");
-
-const {
- awaitPromise,
- createAppInfo,
- createInstallRDF,
- createTempWebExtensionFile,
- createUpdateRDF,
- getFileForAddon,
- manuallyInstall,
- manuallyUninstall,
- promiseAddonByID,
- promiseAddonEvent,
- promiseAddonsByIDs,
- promiseAddonsWithOperationsByTypes,
- promiseCompleteAllInstalls,
- promiseConsoleOutput,
- promiseFindAddonUpdates,
- promiseInstallAllFiles,
- promiseInstallFile,
- promiseRestartManager,
- promiseSetExtensionModifiedTime,
- promiseShutdownManager,
- promiseStartupManager,
- promiseWriteProxyFileToDir,
- registerDirectory,
- setExtensionModifiedTime,
- writeFilesToZip
-} = AddonTestUtils;
-
-// WebExtension wrapper for ease of testing
-ExtensionTestUtils.init(this);
-
-AddonTestUtils.init(this);
-AddonTestUtils.overrideCertDB();
-
-Object.defineProperty(this, "gAppInfo", {
- get() {
- return AddonTestUtils.appInfo;
- },
-});
-
-Object.defineProperty(this, "gExtensionsINI", {
- get() {
- return AddonTestUtils.extensionsINI.clone();
- },
-});
-
-Object.defineProperty(this, "gInternalManager", {
- get() {
- return AddonTestUtils.addonIntegrationService.QueryInterface(AM_Ci.nsITimerCallback);
- },
-});
-
-Object.defineProperty(this, "gProfD", {
- get() {
- return AddonTestUtils.profileDir.clone();
- },
-});
-
-Object.defineProperty(this, "gTmpD", {
- get() {
- return AddonTestUtils.tempDir.clone();
- },
-});
-
-Object.defineProperty(this, "gUseRealCertChecks", {
- get() {
- return AddonTestUtils.useRealCertChecks;
- },
- set(val) {
- return AddonTestUtils.useRealCertChecks = val;
- },
-});
-
-Object.defineProperty(this, "TEST_UNPACKED", {
- get() {
- return AddonTestUtils.testUnpacked;
- },
- set(val) {
- return AddonTestUtils.testUnpacked = val;
- },
-});
-
-// We need some internal bits of AddonManager
-var AMscope = Components.utils.import("resource://gre/modules/AddonManager.jsm", {});
-var { AddonManager, AddonManagerInternal, AddonManagerPrivate } = AMscope;
-
-var gPort = null;
-var gUrlToFileMap = {};
-
-// Map resource://xpcshell-data/ to the data directory
-var resHandler = Services.io.getProtocolHandler("resource")
- .QueryInterface(AM_Ci.nsISubstitutingProtocolHandler);
-// Allow non-existent files because of bug 1207735
-var dataURI = NetUtil.newURI(do_get_file("data", true));
-resHandler.setSubstitution("xpcshell-data", dataURI);
-
-function isManifestRegistered(file) {
- let manifests = Components.manager.getManifestLocations();
- for (let i = 0; i < manifests.length; i++) {
- let manifest = manifests.queryElementAt(i, AM_Ci.nsIURI);
-
- // manifest is the url to the manifest file either in an XPI or a directory.
- // We want the location of the XPI or directory itself.
- if (manifest instanceof AM_Ci.nsIJARURI) {
- manifest = manifest.JARFile.QueryInterface(AM_Ci.nsIFileURL).file;
- }
- else if (manifest instanceof AM_Ci.nsIFileURL) {
- manifest = manifest.file.parent;
- }
- else {
- continue;
- }
-
- if (manifest.equals(file))
- return true;
- }
- return false;
-}
-
-// Listens to messages from bootstrap.js telling us what add-ons were started
-// and stopped etc. and performs some sanity checks that only installed add-ons
-// are started etc.
-this.BootstrapMonitor = {
- inited: false,
-
- // Contain the current state of add-ons in the system
- installed: new Map(),
- started: new Map(),
-
- // Contain the last state of shutdown and uninstall calls for an add-on
- stopped: new Map(),
- uninstalled: new Map(),
-
- startupPromises: [],
- installPromises: [],
-
- init() {
- this.inited = true;
- Services.obs.addObserver(this, "bootstrapmonitor-event", false);
- },
-
- shutdownCheck() {
- if (!this.inited)
- return;
-
- do_check_eq(this.started.size, 0);
- },
-
- clear(id) {
- this.installed.delete(id);
- this.started.delete(id);
- this.stopped.delete(id);
- this.uninstalled.delete(id);
- },
-
- promiseAddonStartup(id) {
- return new Promise(resolve => {
- this.startupPromises.push(resolve);
- });
- },
-
- promiseAddonInstall(id) {
- return new Promise(resolve => {
- this.installPromises.push(resolve);
- });
- },
-
- checkMatches(cached, current) {
- do_check_neq(cached, undefined);
- do_check_eq(current.data.version, cached.data.version);
- do_check_eq(current.data.installPath, cached.data.installPath);
- do_check_eq(current.data.resourceURI, cached.data.resourceURI);
- },
-
- checkAddonStarted(id, version = undefined) {
- let started = this.started.get(id);
- do_check_neq(started, undefined);
- if (version != undefined)
- do_check_eq(started.data.version, version);
-
- // Chrome should be registered by now
- let installPath = new FileUtils.File(started.data.installPath);
- let isRegistered = isManifestRegistered(installPath);
- do_check_true(isRegistered);
- },
-
- checkAddonNotStarted(id) {
- do_check_false(this.started.has(id));
- },
-
- checkAddonInstalled(id, version = undefined) {
- const installed = this.installed.get(id);
- notEqual(installed, undefined);
- if (version !== undefined) {
- equal(installed.data.version, version);
- }
- return installed;
- },
-
- checkAddonNotInstalled(id) {
- do_check_false(this.installed.has(id));
- },
-
- observe(subject, topic, data) {
- let info = JSON.parse(data);
- let id = info.data.id;
- let installPath = new FileUtils.File(info.data.installPath);
-
- if (subject && subject.wrappedJSObject) {
- // NOTE: in some of the new tests, we need to received the real objects instead of
- // their JSON representations, but most of the current tests expect intallPath
- // and resourceURI to have been converted to strings.
- info.data = Object.assign({}, subject.wrappedJSObject.data, {
- installPath: info.data.installPath,
- resourceURI: info.data.resourceURI,
- });
- }
-
- // If this is the install event the add-ons shouldn't already be installed
- if (info.event == "install") {
- this.checkAddonNotInstalled(id);
-
- this.installed.set(id, info);
-
- for (let resolve of this.installPromises)
- resolve();
- this.installPromises = [];
- }
- else {
- this.checkMatches(this.installed.get(id), info);
- }
-
- // If this is the shutdown event than the add-on should already be started
- if (info.event == "shutdown") {
- this.checkMatches(this.started.get(id), info);
-
- this.started.delete(id);
- this.stopped.set(id, info);
-
- // Chrome should still be registered at this point
- let isRegistered = isManifestRegistered(installPath);
- do_check_true(isRegistered);
-
- // XPIProvider doesn't bother unregistering chrome on app shutdown but
- // since we simulate restarts we must do so manually to keep the registry
- // consistent.
- if (info.reason == 2 /* APP_SHUTDOWN */)
- Components.manager.removeBootstrappedManifestLocation(installPath);
- }
- else {
- this.checkAddonNotStarted(id);
- }
-
- if (info.event == "uninstall") {
- // Chrome should be unregistered at this point
- let isRegistered = isManifestRegistered(installPath);
- do_check_false(isRegistered);
-
- this.installed.delete(id);
- this.uninstalled.set(id, info)
- }
- else if (info.event == "startup") {
- this.started.set(id, info);
-
- // Chrome should be registered at this point
- let isRegistered = isManifestRegistered(installPath);
- do_check_true(isRegistered);
-
- for (let resolve of this.startupPromises)
- resolve();
- this.startupPromises = [];
- }
- }
-}
-
-AddonTestUtils.on("addon-manager-shutdown", () => BootstrapMonitor.shutdownCheck());
-
-function isNightlyChannel() {
- var channel = "default";
- try {
- channel = Services.prefs.getCharPref("app.update.channel");
- }
- catch (e) { }
-
- return channel != "aurora" && channel != "beta" && channel != "release" && channel != "esr";
-}
-
-/**
- * Tests that an add-on does appear in the crash report annotations, if
- * crash reporting is enabled. The test will fail if the add-on is not in the
- * annotation.
- * @param aId
- * The ID of the add-on
- * @param aVersion
- * The version of the add-on
- */
-function do_check_in_crash_annotation(aId, aVersion) {
- if (!("nsICrashReporter" in AM_Ci))
- return;
-
- if (!("Add-ons" in gAppInfo.annotations)) {
- do_check_false(true);
- return;
- }
-
- let addons = gAppInfo.annotations["Add-ons"].split(",");
- do_check_false(addons.indexOf(encodeURIComponent(aId) + ":" +
- encodeURIComponent(aVersion)) < 0);
-}
-
-/**
- * Tests that an add-on does not appear in the crash report annotations, if
- * crash reporting is enabled. The test will fail if the add-on is in the
- * annotation.
- * @param aId
- * The ID of the add-on
- * @param aVersion
- * The version of the add-on
- */
-function do_check_not_in_crash_annotation(aId, aVersion) {
- if (!("nsICrashReporter" in AM_Ci))
- return;
-
- if (!("Add-ons" in gAppInfo.annotations)) {
- do_check_true(true);
- return;
- }
-
- let addons = gAppInfo.annotations["Add-ons"].split(",");
- do_check_true(addons.indexOf(encodeURIComponent(aId) + ":" +
- encodeURIComponent(aVersion)) < 0);
-}
-
-/**
- * Returns a testcase xpi
- *
- * @param aName
- * The name of the testcase (without extension)
- * @return an nsIFile pointing to the testcase xpi
- */
-function do_get_addon(aName) {
- return do_get_file("addons/" + aName + ".xpi");
-}
-
-function do_get_addon_hash(aName, aAlgorithm) {
- let file = do_get_addon(aName);
- return do_get_file_hash(file);
-}
-
-function do_get_file_hash(aFile, aAlgorithm) {
- if (!aAlgorithm)
- aAlgorithm = "sha1";
-
- let crypto = AM_Cc["@mozilla.org/security/hash;1"].
- createInstance(AM_Ci.nsICryptoHash);
- crypto.initWithString(aAlgorithm);
- let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
- createInstance(AM_Ci.nsIFileInputStream);
- fis.init(aFile, -1, -1, false);
- crypto.updateFromStream(fis, aFile.fileSize);
-
- // return the two-digit hexadecimal code for a byte
- let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
-
- let binary = crypto.finish(false);
- let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
- return aAlgorithm + ":" + hash.join("");
-}
-
-/**
- * Returns an extension uri spec
- *
- * @param aProfileDir
- * The extension install directory
- * @return a uri spec pointing to the root of the extension
- */
-function do_get_addon_root_uri(aProfileDir, aId) {
- let path = aProfileDir.clone();
- path.append(aId);
- if (!path.exists()) {
- path.leafName += ".xpi";
- return "jar:" + Services.io.newFileURI(path).spec + "!/";
- }
- return Services.io.newFileURI(path).spec;
-}
-
-function do_get_expected_addon_name(aId) {
- if (TEST_UNPACKED)
- return aId;
- return aId + ".xpi";
-}
-
-/**
- * Check that an array of actual add-ons is the same as an array of
- * expected add-ons.
- *
- * @param aActualAddons
- * The array of actual add-ons to check.
- * @param aExpectedAddons
- * The array of expected add-ons to check against.
- * @param aProperties
- * An array of properties to check.
- */
-function do_check_addons(aActualAddons, aExpectedAddons, aProperties) {
- do_check_neq(aActualAddons, null);
- do_check_eq(aActualAddons.length, aExpectedAddons.length);
- for (let i = 0; i < aActualAddons.length; i++)
- do_check_addon(aActualAddons[i], aExpectedAddons[i], aProperties);
-}
-
-/**
- * Check that the actual add-on is the same as the expected add-on.
- *
- * @param aActualAddon
- * The actual add-on to check.
- * @param aExpectedAddon
- * The expected add-on to check against.
- * @param aProperties
- * An array of properties to check.
- */
-function do_check_addon(aActualAddon, aExpectedAddon, aProperties) {
- do_check_neq(aActualAddon, null);
-
- aProperties.forEach(function(aProperty) {
- let actualValue = aActualAddon[aProperty];
- let expectedValue = aExpectedAddon[aProperty];
-
- // Check that all undefined expected properties are null on actual add-on
- if (!(aProperty in aExpectedAddon)) {
- if (actualValue !== undefined && actualValue !== null) {
- do_throw("Unexpected defined/non-null property for add-on " +
- aExpectedAddon.id + " (addon[" + aProperty + "] = " +
- actualValue.toSource() + ")");
- }
-
- return;
- }
- else if (expectedValue && !actualValue) {
- do_throw("Missing property for add-on " + aExpectedAddon.id +
- ": expected addon[" + aProperty + "] = " + expectedValue);
- return;
- }
-
- switch (aProperty) {
- case "creator":
- do_check_author(actualValue, expectedValue);
- break;
-
- case "developers":
- case "translators":
- case "contributors":
- do_check_eq(actualValue.length, expectedValue.length);
- for (let i = 0; i < actualValue.length; i++)
- do_check_author(actualValue[i], expectedValue[i]);
- break;
-
- case "screenshots":
- do_check_eq(actualValue.length, expectedValue.length);
- for (let i = 0; i < actualValue.length; i++)
- do_check_screenshot(actualValue[i], expectedValue[i]);
- break;
-
- case "sourceURI":
- do_check_eq(actualValue.spec, expectedValue);
- break;
-
- case "updateDate":
- do_check_eq(actualValue.getTime(), expectedValue.getTime());
- break;
-
- case "compatibilityOverrides":
- do_check_eq(actualValue.length, expectedValue.length);
- for (let i = 0; i < actualValue.length; i++)
- do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
- break;
-
- case "icons":
- do_check_icons(actualValue, expectedValue);
- break;
-
- default:
- if (remove_port(actualValue) !== remove_port(expectedValue))
- do_throw("Failed for " + aProperty + " for add-on " + aExpectedAddon.id +
- " (" + actualValue + " === " + expectedValue + ")");
- }
- });
-}
-
-/**
- * Check that the actual author is the same as the expected author.
- *
- * @param aActual
- * The actual author to check.
- * @param aExpected
- * The expected author to check against.
- */
-function do_check_author(aActual, aExpected) {
- do_check_eq(aActual.toString(), aExpected.name);
- do_check_eq(aActual.name, aExpected.name);
- do_check_eq(aActual.url, aExpected.url);
-}
-
-/**
- * Check that the actual screenshot is the same as the expected screenshot.
- *
- * @param aActual
- * The actual screenshot to check.
- * @param aExpected
- * The expected screenshot to check against.
- */
-function do_check_screenshot(aActual, aExpected) {
- do_check_eq(aActual.toString(), aExpected.url);
- do_check_eq(aActual.url, aExpected.url);
- do_check_eq(aActual.width, aExpected.width);
- do_check_eq(aActual.height, aExpected.height);
- do_check_eq(aActual.thumbnailURL, aExpected.thumbnailURL);
- do_check_eq(aActual.thumbnailWidth, aExpected.thumbnailWidth);
- do_check_eq(aActual.thumbnailHeight, aExpected.thumbnailHeight);
- do_check_eq(aActual.caption, aExpected.caption);
-}
-
-/**
- * Check that the actual compatibility override is the same as the expected
- * compatibility override.
- *
- * @param aAction
- * The actual compatibility override to check.
- * @param aExpected
- * The expected compatibility override to check against.
- */
-function do_check_compatibilityoverride(aActual, aExpected) {
- do_check_eq(aActual.type, aExpected.type);
- do_check_eq(aActual.minVersion, aExpected.minVersion);
- do_check_eq(aActual.maxVersion, aExpected.maxVersion);
- do_check_eq(aActual.appID, aExpected.appID);
- do_check_eq(aActual.appMinVersion, aExpected.appMinVersion);
- do_check_eq(aActual.appMaxVersion, aExpected.appMaxVersion);
-}
-
-function do_check_icons(aActual, aExpected) {
- for (var size in aExpected) {
- do_check_eq(remove_port(aActual[size]), remove_port(aExpected[size]));
- }
-}
-
-function startupManager(aAppChanged) {
- promiseStartupManager(aAppChanged);
-}
-
-/**
- * Restarts the add-on manager as if the host application was restarted.
- *
- * @param aNewVersion
- * An optional new version to use for the application. Passing this
- * will change nsIXULAppInfo.version and make the startup appear as if
- * the application version has changed.
- */
-function restartManager(aNewVersion) {
- awaitPromise(promiseRestartManager(aNewVersion));
-}
-
-function shutdownManager() {
- awaitPromise(promiseShutdownManager());
-}
-
-function isItemMarkedMPIncompatible(aId) {
- return AddonTestUtils.addonsList.isMultiprocessIncompatible(aId);
-}
-
-function isThemeInAddonsList(aDir, aId) {
- return AddonTestUtils.addonsList.hasTheme(aDir, aId);
-}
-
-function isExtensionInAddonsList(aDir, aId) {
- return AddonTestUtils.addonsList.hasExtension(aDir, aId);
-}
-
-function check_startup_changes(aType, aIds) {
- var ids = aIds.slice(0);
- ids.sort();
- var changes = AddonManager.getStartupChanges(aType);
- changes = changes.filter(aEl => /@tests.mozilla.org$/.test(aEl));
- changes.sort();
-
- do_check_eq(JSON.stringify(ids), JSON.stringify(changes));
-}
-
-/**
- * Writes an install.rdf manifest into a directory using the properties passed
- * in a JS object. The objects should contain a property for each property to
- * appear in the RDF. The object may contain an array of objects with id,
- * minVersion and maxVersion in the targetApplications property to give target
- * application compatibility.
- *
- * @param aData
- * The object holding data about the add-on
- * @param aDir
- * The directory to add the install.rdf to
- * @param aId
- * An optional string to override the default installation aId
- * @param aExtraFile
- * An optional dummy file to create in the directory
- * @return An nsIFile for the directory in which the add-on is installed.
- */
-function writeInstallRDFToDir(aData, aDir, aId = aData.id, aExtraFile = null) {
- let files = {
- "install.rdf": AddonTestUtils.createInstallRDF(aData),
- };
- if (aExtraFile)
- files[aExtraFile] = "";
-
- let dir = aDir.clone();
- dir.append(aId);
-
- awaitPromise(AddonTestUtils.promiseWriteFilesToDir(dir.path, files));
- return dir;
-}
-
-/**
- * Writes an install.rdf manifest into a packed extension using the properties passed
- * in a JS object. The objects should contain a property for each property to
- * appear in the RDF. The object may contain an array of objects with id,
- * minVersion and maxVersion in the targetApplications property to give target
- * application compatibility.
- *
- * @param aData
- * The object holding data about the add-on
- * @param aDir
- * The install directory to add the extension to
- * @param aId
- * An optional string to override the default installation aId
- * @param aExtraFile
- * An optional dummy file to create in the extension
- * @return A file pointing to where the extension was installed
- */
-function writeInstallRDFToXPI(aData, aDir, aId = aData.id, aExtraFile = null) {
- let files = {
- "install.rdf": AddonTestUtils.createInstallRDF(aData),
- };
- if (aExtraFile)
- files[aExtraFile] = "";
-
- if (!aDir.exists())
- aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
-
- var file = aDir.clone();
- file.append(`${aId}.xpi`);
-
- AddonTestUtils.writeFilesToZip(file.path, files);
-
- return file;
-}
-
-/**
- * Writes an install.rdf manifest into an extension using the properties passed
- * in a JS object. The objects should contain a property for each property to
- * appear in the RDF. The object may contain an array of objects with id,
- * minVersion and maxVersion in the targetApplications property to give target
- * application compatibility.
- *
- * @param aData
- * The object holding data about the add-on
- * @param aDir
- * The install directory to add the extension to
- * @param aId
- * An optional string to override the default installation aId
- * @param aExtraFile
- * An optional dummy file to create in the extension
- * @return A file pointing to where the extension was installed
- */
-function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
- if (TEST_UNPACKED) {
- return writeInstallRDFToDir(aData, aDir, aId, aExtraFile);
- }
- return writeInstallRDFToXPI(aData, aDir, aId, aExtraFile);
-}
-
-/**
- * Writes a manifest.json manifest into an extension using the properties passed
- * in a JS object.
- *
- * @param aManifest
- * The data to write
- * @param aDir
- * The install directory to add the extension to
- * @param aId
- * An optional string to override the default installation aId
- * @return A file pointing to where the extension was installed
- */
-function promiseWriteWebManifestForExtension(aData, aDir, aId = aData.applications.gecko.id) {
- let files = {
- "manifest.json": JSON.stringify(aData),
- }
- return AddonTestUtils.promiseWriteFilesToExtension(aDir.path, aId, files);
-}
-
-/**
- * Creates an XPI file for some manifest data in the temporary directory and
- * returns the nsIFile for it. The file will be deleted when the test completes.
- *
- * @param aData
- * The object holding data about the add-on
- * @return A file pointing to the created XPI file
- */
-function createTempXPIFile(aData, aExtraFile) {
- let files = {
- "install.rdf": aData,
- };
- if (typeof aExtraFile == "object")
- Object.assign(files, aExtraFile);
- else if (aExtraFile)
- files[aExtraFile] = "";
-
- return AddonTestUtils.createTempXPIFile(files);
-}
-
-var gExpectedEvents = {};
-var gExpectedInstalls = [];
-var gNext = null;
-
-function getExpectedEvent(aId) {
- if (!(aId in gExpectedEvents))
- do_throw("Wasn't expecting events for " + aId);
- if (gExpectedEvents[aId].length == 0)
- do_throw("Too many events for " + aId);
- let event = gExpectedEvents[aId].shift();
- if (event instanceof Array)
- return event;
- return [event, true];
-}
-
-function getExpectedInstall(aAddon) {
- if (gExpectedInstalls instanceof Array)
- return gExpectedInstalls.shift();
- if (!aAddon || !aAddon.id)
- return gExpectedInstalls["NO_ID"].shift();
- let id = aAddon.id;
- if (!(id in gExpectedInstalls) || !(gExpectedInstalls[id] instanceof Array))
- do_throw("Wasn't expecting events for " + id);
- if (gExpectedInstalls[id].length == 0)
- do_throw("Too many events for " + id);
- return gExpectedInstalls[id].shift();
-}
-
-const AddonListener = {
- onPropertyChanged: function(aAddon, aProperties) {
- do_print(`Got onPropertyChanged event for ${aAddon.id}`);
- let [event, properties] = getExpectedEvent(aAddon.id);
- do_check_eq("onPropertyChanged", event);
- do_check_eq(aProperties.length, properties.length);
- properties.forEach(function(aProperty) {
- // Only test that the expected properties are listed, having additional
- // properties listed is not necessary a problem
- if (aProperties.indexOf(aProperty) == -1)
- do_throw("Did not see property change for " + aProperty);
- });
- return check_test_completed(arguments);
- },
-
- onEnabling: function(aAddon, aRequiresRestart) {
- do_print(`Got onEnabling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onEnabling", event);
- do_check_eq(aRequiresRestart, expectedRestart);
- if (expectedRestart)
- do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
- do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
- return check_test_completed(arguments);
- },
-
- onEnabled: function(aAddon) {
- do_print(`Got onEnabled event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onEnabled", event);
- do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
- return check_test_completed(arguments);
- },
-
- onDisabling: function(aAddon, aRequiresRestart) {
- do_print(`Got onDisabling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onDisabling", event);
- do_check_eq(aRequiresRestart, expectedRestart);
- if (expectedRestart)
- do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE));
- do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
- return check_test_completed(arguments);
- },
-
- onDisabled: function(aAddon) {
- do_print(`Got onDisabled event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onDisabled", event);
- do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
- return check_test_completed(arguments);
- },
-
- onInstalling: function(aAddon, aRequiresRestart) {
- do_print(`Got onInstalling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onInstalling", event);
- do_check_eq(aRequiresRestart, expectedRestart);
- if (expectedRestart)
- do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL));
- return check_test_completed(arguments);
- },
-
- onInstalled: function(aAddon) {
- do_print(`Got onInstalled event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onInstalled", event);
- return check_test_completed(arguments);
- },
-
- onUninstalling: function(aAddon, aRequiresRestart) {
- do_print(`Got onUninstalling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onUninstalling", event);
- do_check_eq(aRequiresRestart, expectedRestart);
- if (expectedRestart)
- do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL));
- return check_test_completed(arguments);
- },
-
- onUninstalled: function(aAddon) {
- do_print(`Got onUninstalled event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onUninstalled", event);
- return check_test_completed(arguments);
- },
-
- onOperationCancelled: function(aAddon) {
- do_print(`Got onOperationCancelled event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- do_check_eq("onOperationCancelled", event);
- return check_test_completed(arguments);
- }
-};
-
-const InstallListener = {
- onNewInstall: function(install) {
- if (install.state != AddonManager.STATE_DOWNLOADED &&
- install.state != AddonManager.STATE_DOWNLOAD_FAILED &&
- install.state != AddonManager.STATE_AVAILABLE)
- do_throw("Bad install state " + install.state);
- if (install.state != AddonManager.STATE_DOWNLOAD_FAILED)
- do_check_eq(install.error, 0);
- else
- do_check_neq(install.error, 0);
- do_check_eq("onNewInstall", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadStarted: function(install) {
- do_check_eq(install.state, AddonManager.STATE_DOWNLOADING);
- do_check_eq(install.error, 0);
- do_check_eq("onDownloadStarted", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadEnded: function(install) {
- do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
- do_check_eq(install.error, 0);
- do_check_eq("onDownloadEnded", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadFailed: function(install) {
- do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
- do_check_eq("onDownloadFailed", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadCancelled: function(install) {
- do_check_eq(install.state, AddonManager.STATE_CANCELLED);
- do_check_eq(install.error, 0);
- do_check_eq("onDownloadCancelled", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onInstallStarted: function(install) {
- do_check_eq(install.state, AddonManager.STATE_INSTALLING);
- do_check_eq(install.error, 0);
- do_check_eq("onInstallStarted", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onInstallEnded: function(install, newAddon) {
- do_check_eq(install.state, AddonManager.STATE_INSTALLED);
- do_check_eq(install.error, 0);
- do_check_eq("onInstallEnded", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onInstallFailed: function(install) {
- do_check_eq(install.state, AddonManager.STATE_INSTALL_FAILED);
- do_check_eq("onInstallFailed", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onInstallCancelled: function(install) {
- // If the install was cancelled by a listener returning false from
- // onInstallStarted, then the state will revert to STATE_DOWNLOADED.
- let possibleStates = [AddonManager.STATE_CANCELLED,
- AddonManager.STATE_DOWNLOADED];
- do_check_true(possibleStates.indexOf(install.state) != -1);
- do_check_eq(install.error, 0);
- do_check_eq("onInstallCancelled", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onExternalInstall: function(aAddon, existingAddon, aRequiresRestart) {
- do_check_eq("onExternalInstall", getExpectedInstall(aAddon));
- do_check_false(aRequiresRestart);
- return check_test_completed(arguments);
- }
-};
-
-function hasFlag(aBits, aFlag) {
- return (aBits & aFlag) != 0;
-}
-
-// Just a wrapper around setting the expected events
-function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
- AddonManager.addAddonListener(AddonListener);
- AddonManager.addInstallListener(InstallListener);
-
- gExpectedInstalls = aExpectedInstalls;
- gExpectedEvents = aExpectedEvents;
- gNext = aNext;
-}
-
-// Checks if all expected events have been seen and if so calls the callback
-function check_test_completed(aArgs) {
- if (!gNext)
- return undefined;
-
- if (gExpectedInstalls instanceof Array &&
- gExpectedInstalls.length > 0)
- return undefined;
-
- for (let id in gExpectedInstalls) {
- let installList = gExpectedInstalls[id];
- if (installList.length > 0)
- return undefined;
- }
-
- for (let id in gExpectedEvents) {
- if (gExpectedEvents[id].length > 0)
- return undefined;
- }
-
- return gNext.apply(null, aArgs);
-}
-
-// Verifies that all the expected events for all add-ons were seen
-function ensure_test_completed() {
- for (let i in gExpectedEvents) {
- if (gExpectedEvents[i].length > 0)
- do_throw("Didn't see all the expected events for " + i);
- }
- gExpectedEvents = {};
- if (gExpectedInstalls)
- do_check_eq(gExpectedInstalls.length, 0);
-}
-
-/**
- * A helper method to install an array of AddonInstall to completion and then
- * call a provided callback.
- *
- * @param aInstalls
- * The array of AddonInstalls to install
- * @param aCallback
- * The callback to call when all installs have finished
- */
-function completeAllInstalls(aInstalls, aCallback) {
- promiseCompleteAllInstalls(aInstalls).then(aCallback);
-}
-
-/**
- * A helper method to install an array of files and call a callback after the
- * installs are completed.
- *
- * @param aFiles
- * The array of files to install
- * @param aCallback
- * The callback to call when all installs have finished
- * @param aIgnoreIncompatible
- * Optional parameter to ignore add-ons that are incompatible in
- * aome way with the application
- */
-function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
- promiseInstallAllFiles(aFiles, aIgnoreIncompatible).then(aCallback);
-}
-
-const EXTENSIONS_DB = "extensions.json";
-var gExtensionsJSON = gProfD.clone();
-gExtensionsJSON.append(EXTENSIONS_DB);
-
-
-// By default use strict compatibility
-Services.prefs.setBoolPref("extensions.strictCompatibility", true);
-
-// By default, set min compatible versions to 0
-Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0");
-Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, "0");
-
-// Ensure signature checks are enabled by default
-Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
-
-
-// Copies blocklistFile (an nsIFile) to gProfD/blocklist.xml.
-function copyBlocklistToProfile(blocklistFile) {
- var dest = gProfD.clone();
- dest.append("blocklist.xml");
- if (dest.exists())
- dest.remove(false);
- blocklistFile.copyTo(gProfD, "blocklist.xml");
- dest.lastModifiedTime = Date.now();
-}
-
-// Throw a failure and attempt to abandon the test if it looks like it is going
-// to timeout
-function timeout() {
- timer = null;
- do_throw("Test ran longer than " + TIMEOUT_MS + "ms");
-
- // Attempt to bail out of the test
- do_test_finished();
-}
-
-var timer = AM_Cc["@mozilla.org/timer;1"].createInstance(AM_Ci.nsITimer);
-timer.init(timeout, TIMEOUT_MS, AM_Ci.nsITimer.TYPE_ONE_SHOT);
-
-// Make sure that a given path does not exist
-function pathShouldntExist(file) {
- if (file.exists()) {
- do_throw(`Test cleanup: path ${file.path} exists when it should not`);
- }
-}
-
-do_register_cleanup(function addon_cleanup() {
- if (timer)
- timer.cancel();
-});
-
-/**
- * Creates a new HttpServer for testing, and begins listening on the
- * specified port. Automatically shuts down the server when the test
- * unit ends.
- *
- * @param port
- * The port to listen on. If omitted, listen on a random
- * port. The latter is the preferred behavior.
- *
- * @return HttpServer
- */
-function createHttpServer(port = -1) {
- let server = new HttpServer();
- server.start(port);
-
- do_register_cleanup(() => {
- return new Promise(resolve => {
- server.stop(resolve);
- });
- });
-
- return server;
-}
-
-/**
- * Handler function that responds with the interpolated
- * static file associated to the URL specified by request.path.
- * This replaces the %PORT% entries in the file with the actual
- * value of the running server's port (stored in gPort).
- */
-function interpolateAndServeFile(request, response) {
- try {
- let file = gUrlToFileMap[request.path];
- var data = "";
- var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
- createInstance(Components.interfaces.nsIFileInputStream);
- var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
- createInstance(Components.interfaces.nsIConverterInputStream);
- fstream.init(file, -1, 0, 0);
- cstream.init(fstream, "UTF-8", 0, 0);
-
- let str = {};
- let read = 0;
- do {
- // read as much as we can and put it in str.value
- read = cstream.readString(0xffffffff, str);
- data += str.value;
- } while (read != 0);
- data = data.replace(/%PORT%/g, gPort);
-
- response.write(data);
- } catch (e) {
- do_throw(`Exception while serving interpolated file: ${e}\n${e.stack}`);
- } finally {
- cstream.close(); // this closes fstream as well
- }
-}
-
-/**
- * Sets up a path handler for the given URL and saves the
- * corresponding file in the global url -> file map.
- *
- * @param url
- * the actual URL
- * @param file
- * nsILocalFile representing a static file
- */
-function mapUrlToFile(url, file, server) {
- server.registerPathHandler(url, interpolateAndServeFile);
- gUrlToFileMap[url] = file;
-}
-
-function mapFile(path, server) {
- mapUrlToFile(path, do_get_file(path), server);
-}
-
-/**
- * Take out the port number in an URL
- *
- * @param url
- * String that represents an URL with a port number in it
- */
-function remove_port(url) {
- if (typeof url === "string")
- return url.replace(/:\d+/, "");
- return url;
-}
-// Wrap a function (typically a callback) to catch and report exceptions
-function do_exception_wrap(func) {
- return function() {
- try {
- func.apply(null, arguments);
- }
- catch (e) {
- do_report_unexpected_exception(e);
- }
- };
-}
-
-/**
- * Change the schema version of the JSON extensions database
- */
-function changeXPIDBVersion(aNewVersion, aMutator = undefined) {
- let jData = loadJSON(gExtensionsJSON);
- jData.schemaVersion = aNewVersion;
- if (aMutator)
- aMutator(jData);
- saveJSON(jData, gExtensionsJSON);
-}
-
-/**
- * Load a file into a string
- */
-function loadFile(aFile) {
- let data = "";
- let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
- createInstance(Components.interfaces.nsIFileInputStream);
- let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
- createInstance(Components.interfaces.nsIConverterInputStream);
- fstream.init(aFile, -1, 0, 0);
- cstream.init(fstream, "UTF-8", 0, 0);
- let str = {};
- let read = 0;
- do {
- read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
- data += str.value;
- } while (read != 0);
- cstream.close();
- return data;
-}
-
-/**
- * Raw load of a JSON file
- */
-function loadJSON(aFile) {
- let data = loadFile(aFile);
- do_print("Loaded JSON file " + aFile.path);
- return (JSON.parse(data));
-}
-
-/**
- * Raw save of a JSON blob to file
- */
-function saveJSON(aData, aFile) {
- do_print("Starting to save JSON file " + aFile.path);
- let stream = FileUtils.openSafeFileOutputStream(aFile);
- let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
- createInstance(AM_Ci.nsIConverterOutputStream);
- converter.init(stream, "UTF-8", 0, 0x0000);
- // XXX pretty print the JSON while debugging
- converter.writeString(JSON.stringify(aData, null, 2));
- converter.flush();
- // nsConverterOutputStream doesn't finish() safe output streams on close()
- FileUtils.closeSafeFileOutputStream(stream);
- converter.close();
- do_print("Done saving JSON file " + aFile.path);
-}
-
-/**
- * Create a callback function that calls do_execute_soon on an actual callback and arguments
- */
-function callback_soon(aFunction) {
- return function(...args) {
- do_execute_soon(function() {
- aFunction.apply(null, args);
- }, aFunction.name ? "delayed callback " + aFunction.name : "delayed callback");
- }
-}
-
-function writeProxyFileToDir(aDir, aAddon, aId) {
- awaitPromise(promiseWriteProxyFileToDir(aDir, aAddon, aId));
-
- let file = aDir.clone();
- file.append(aId);
- return file
-}
-
-function* serveSystemUpdate(xml, perform_update, testserver) {
- testserver.registerPathHandler("/data/update.xml", (request, response) => {
- response.write(xml);
- });
-
- try {
- yield perform_update();
- }
- finally {
- testserver.registerPathHandler("/data/update.xml", null);
- }
-}
-
-// Runs an update check making it use the passed in xml string. Uses the direct
-// call to the update function so we get rejections on failure.
-function* installSystemAddons(xml, testserver) {
- do_print("Triggering system add-on update check.");
-
- yield serveSystemUpdate(xml, function*() {
- let { XPIProvider } = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
- yield XPIProvider.updateSystemAddons();
- }, testserver);
-}
-
-// Runs a full add-on update check which will in some cases do a system add-on
-// update check. Always succeeds.
-function* updateAllSystemAddons(xml, testserver) {
- do_print("Triggering full add-on update check.");
-
- yield serveSystemUpdate(xml, function() {
- return new Promise(resolve => {
- Services.obs.addObserver(function() {
- Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
-
- resolve();
- }, "addons-background-update-complete", false);
-
- // Trigger the background update timer handler
- gInternalManager.notify(null);
- });
- }, testserver);
-}
-
-// Builds an update.xml file for an update check based on the data passed.
-function* buildSystemAddonUpdates(addons, root) {
- let xml = `<?xml version="1.0" encoding="UTF-8"?>\n\n<updates>\n`;
- if (addons) {
- xml += ` <addons>\n`;
- for (let addon of addons) {
- xml += ` <addon id="${addon.id}" URL="${root + addon.path}" version="${addon.version}"`;
- if (addon.size)
- xml += ` size="${addon.size}"`;
- if (addon.hashFunction)
- xml += ` hashFunction="${addon.hashFunction}"`;
- if (addon.hashValue)
- xml += ` hashValue="${addon.hashValue}"`;
- xml += `/>\n`;
- }
- xml += ` </addons>\n`;
- }
- xml += `</updates>\n`;
-
- return xml;
-}