Bug 1385057: Remove most code for handling unpacked side-loaded extensions. r=aswan,keeler

MozReview-Commit-ID: H4cSRBws4Ml

--HG--
extra : rebase_source : ddddef92344b6414ae4e5635b9841fcc274dfca9
This commit is contained in:
Kris Maglione 2018-05-09 16:04:04 -07:00
parent f0456b269d
commit 250e7028e3
25 changed files with 140 additions and 585 deletions

View File

@ -1453,12 +1453,3 @@ nsNSSCertificateDB::OpenSignedAppFileAsync(
aCallback));
return task->Dispatch("SignedJAR");
}
NS_IMETHODIMP
nsNSSCertificateDB::VerifySignedDirectoryAsync(AppTrustedRoot, nsIFile*,
nsIVerifySignedDirectoryCallback* aCallback)
{
NS_ENSURE_ARG_POINTER(aCallback);
return aCallback->VerifySignedDirectoryFinished(
NS_ERROR_SIGNED_JAR_NOT_SIGNED, nullptr);
}

View File

@ -28,15 +28,6 @@ interface nsIOpenSignedAppFileCallback : nsISupports
in nsIX509Cert aSignerCert);
};
// Only relevant while we transition away from legacy add-ons. rv will always be
// NS_ERROR_SIGNED_JAR_NOT_SIGNED. aSignerCert will always be null.
[scriptable, function, uuid(d5f97827-622a-488f-be08-d850432ac8ec)]
interface nsIVerifySignedDirectoryCallback : nsISupports
{
void verifySignedDirectoryFinished(in nsresult rv,
in nsIX509Cert aSignerCert);
};
/**
* Callback type for use with asyncVerifyCertAtTime.
* If aPRErrorCode is PRErrorCodeSuccess (i.e. 0), aVerifiedChain represents the
@ -258,16 +249,6 @@ interface nsIX509CertDB : nsISupports {
in nsIFile aJarFile,
in nsIOpenSignedAppFileCallback callback);
/**
* Vestigial implementation of verifying signed unpacked add-ons. trustedRoot
* and aUnpackedDir are ignored. The callback is always called with
* NS_ERROR_SIGNED_JAR_NOT_SIGNED and a null signer cert.
*/
[must_use]
void verifySignedDirectoryAsync(in AppTrustedRoot trustedRoot,
in nsIFile aUnpackedDir,
in nsIVerifySignedDirectoryCallback callback);
/*
* Add a cert to a cert DB from a binary string.
*

View File

@ -1,68 +0,0 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/
"use strict";
// Tests that signed extensions extracted/unpacked into a directory do not pass
// signature verification, because that's no longer supported.
const { ZipUtils } = ChromeUtils.import("resource://gre/modules/ZipUtils.jsm", {});
do_get_profile(); // must be called before getting nsIX509CertDB
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
.getService(Ci.nsIX509CertDB);
/**
* Signed test extension. This is any arbitrary Mozilla signed XPI that
* preferably has recently been signed (but note that it actually doesn't
* matter, since we ignore expired certificates when checking signing).
* @type nsIFile
*/
var gSignedXPI =
do_get_file("test_signed_dir/lightbeam_for_firefox-1.3.1-fx.xpi", false);
/**
* The directory that the test extension will be extracted to.
* @type nsIFile
*/
var gTarget = FileUtils.getDir("TmpD", ["test_signed_dir"]);
gTarget.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
/**
* Extracts the signed XPI into a directory, and tampers the files in that
* directory if instructed.
*
* @returns {nsIFile}
* The directory where the XPI was extracted to.
*/
function prepare() {
ZipUtils.extractFiles(gSignedXPI, gTarget);
return gTarget;
}
function checkResult(expectedRv, dir, resolve) {
return function verifySignedDirCallback(rv, aSignerCert) {
equal(rv, expectedRv, "Actual and expected return value should match");
equal(aSignerCert != null, Components.isSuccessCode(expectedRv),
"expecting certificate:");
dir.remove(true);
resolve();
};
}
function verifyDirAsync(expectedRv) {
let targetDir = prepare();
return new Promise((resolve, reject) => {
certdb.verifySignedDirectoryAsync(
Ci.nsIX509CertDB.AddonsPublicRoot, targetDir,
checkResult(expectedRv, targetDir, resolve));
});
}
add_task(async function testAPIFails() {
await verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED);
});
registerCleanupFunction(function() {
if (gTarget.exists()) {
gTarget.remove(true);
}
});

View File

@ -31,7 +31,6 @@ support-files =
test_sdr_preexisting/**
test_sdr_preexisting_with_password/**
test_signed_apps/**
test_signed_dir/**
test_startcom_wosign/**
test_symantec_apple_google/**
test_validity/**
@ -150,8 +149,6 @@ skip-if = toolkit == 'android'
[test_session_resumption.js]
run-sequentially = hardcoded ports
[test_signed_apps.js]
[test_signed_dir.js]
tags = addons psm
[test_ssl_status.js]
[test_sss_enumerate.js]
[test_sss_eviction.js]

View File

@ -2056,19 +2056,6 @@ var AddonManagerInternal = {
.installTemporaryAddon(aFile);
},
installAddonFromSources(aFile) {
if (!gStarted)
throw Components.Exception("AddonManager is not initialized",
Cr.NS_ERROR_NOT_INITIALIZED);
if (!(aFile instanceof Ci.nsIFile))
throw Components.Exception("aFile must be a nsIFile",
Cr.NS_ERROR_INVALID_ARG);
return AddonManagerInternal._getProviderByName("XPIProvider")
.installAddonFromSources(aFile);
},
/**
* Returns an Addon corresponding to an instance ID.
* @param aInstanceID
@ -3441,10 +3428,6 @@ var AddonManager = {
return AddonManagerInternal.installTemporaryAddon(aDirectory);
},
installAddonFromSources(aDirectory) {
return AddonManagerInternal.installAddonFromSources(aDirectory);
},
getAddonByInstanceID(aInstanceID) {
return AddonManagerInternal.getAddonByInstanceID(aInstanceID);
},

View File

@ -675,16 +675,6 @@ var AddonTestUtils = {
});
},
verifySignedDirectoryAsync(root, dir, callback) {
// First try calling the real cert DB
this._genuine.verifySignedDirectoryAsync(root, dir, (result, cert) => {
verifyCert(dir.clone(), result, cert, callback)
.then(([callback, result, cert]) => {
callback.verifySignedDirectoryFinished(result, cert);
});
});
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIX509CertDB]),
};

View File

@ -323,22 +323,8 @@ DirPackage = class DirPackage extends Package {
return OS.File.read(OS.Path.join(this.filePath, ...path));
}
verifySignedStateForRoot(addon, root) {
return new Promise(resolve => {
let callback = {
verifySignedDirectoryFinished(aRv, aCert) {
resolve({
signedState: getSignedStatus(aRv, aCert, addon.id),
cert: aCert,
});
}
};
// This allows the certificate DB to get the raw JS callback object so the
// test code can pass through objects that XPConnect would reject.
callback.wrappedJSObject = callback;
gCertDB.verifySignedDirectoryAsync(root, this.file, callback);
});
async verifySignedStateForRoot(addon, root) {
return {signedState: AddonManager.SIGNEDSTATE_UNKNOWN, cert: null};
}
};
@ -3027,9 +3013,7 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
if (action == "copy") {
transaction.copy(source, this._directory);
} else if (action == "move") {
if (source.isFile())
flushJarCache(source);
flushJarCache(source);
transaction.moveUnder(source, this._directory);
}
// Do nothing for the proxy file as we sideload an addon permanently
@ -3461,9 +3445,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
// If any of these operations fails the finally block will clean up the
// temporary directory
try {
if (source.isFile()) {
flushJarCache(source);
}
flushJarCache(source);
transaction.moveUnder(source, this._directory);
} finally {
@ -3891,51 +3873,20 @@ var XPIInstall = {
* @param {nsIFile} aFile
* An nsIFile for the unpacked add-on directory or XPI file.
*
* @returns {Addon}
* See installAddonFromLocation return value.
*/
installTemporaryAddon(aFile) {
return this.installAddonFromLocation(aFile, XPIInternal.TemporaryInstallLocation);
},
/**
* Permanently installs add-on from a local XPI file or directory.
* The signature is checked but the add-on persist on application restart.
*
* @param {nsIFile} aFile
* An nsIFile for the unpacked add-on directory or XPI file.
*
* @returns {Addon}
* See installAddonFromLocation return value.
*/
async installAddonFromSources(aFile) {
let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
return this.installAddonFromLocation(aFile, location, "proxy");
},
/**
* Installs add-on from a local XPI file or directory.
*
* @param {nsIFile} aFile
* An nsIFile for the unpacked add-on directory or XPI file.
* @param {InstallLocation} aInstallLocation
* Define a custom install location object to use for the install.
* @param {string?} [aInstallAction]
* Optional action mode to use when installing the addon
* (see MutableDirectoryInstallLocation.installAddon)
*
* @returns {Promise<Addon>}
* A Promise that resolves to an Addon object on success, or rejects
* if the add-on is not a valid restartless add-on or if the
* same ID is already installed.
*/
async installAddonFromLocation(aFile, aInstallLocation, aInstallAction) {
async installTemporaryAddon(aFile) {
let installLocation = XPIInternal.TemporaryInstallLocation;
if (aFile.exists() && aFile.isFile()) {
flushJarCache(aFile);
}
let addon = await loadManifestFromFile(aFile, aInstallLocation);
let addon = await loadManifestFromFile(aFile, installLocation);
aInstallLocation.installAddon({ id: addon.id, source: aFile, action: aInstallAction });
installLocation.installAddon({ id: addon.id, source: aFile });
if (addon.appDisabled) {
let message = `Add-on ${addon.id} is not compatible with application version.`;
@ -3960,7 +3911,7 @@ var XPIInstall = {
let callUpdate = false;
let extraParams = {};
extraParams.temporarilyInstalled = aInstallLocation === XPIInternal.TemporaryInstallLocation;
extraParams.temporarilyInstalled = true;
if (oldAddon) {
if (!oldAddon.bootstrap) {
logger.warn("Non-restartless Add-on is already installed", addon.id);

View File

@ -455,7 +455,7 @@ function getAllAliasesForTypes(aTypes) {
* An nsIURI pointing at the resource
*/
function getURIForResourceInFile(aFile, aPath) {
if (aFile.exists() && aFile.isDirectory()) {
if (!aFile.leafName.toLowerCase().endsWith(".xpi")) {
let resource = aFile.clone();
if (aPath)
aPath.split("/").forEach(part => resource.append(part));
@ -689,8 +689,8 @@ class XPIState {
// Modified time is the install manifest time, if any. If no manifest
// exists, we assume this is a packed .xpi and use the time stamp of
// {path}
let mtime = (tryGetMtime(getManifestFileForDir(aFile)) ||
tryGetMtime(aFile));
let mtime = (aFile.leafName.toLowerCase().endsWith(".xpi") ?
tryGetMtime(aFile) : tryGetMtime(getManifestFileForDir(aFile)));
if (!mtime) {
logger.warn("Can't get modified time of ${file}", {file: aFile.path});
}
@ -1949,16 +1949,12 @@ var XPIProvider = {
let distroDir;
try {
distroDir = FileUtils.getDir(KEY_APP_DISTRIBUTION, [DIR_EXTENSIONS]);
if (!distroDir.isDirectory())
return false;
} catch (e) {
return false;
}
if (!distroDir.exists())
return false;
if (!distroDir.isDirectory())
return false;
let changed = false;
let profileLocation = this.installLocationsByName[KEY_APP_PROFILE];
@ -1968,17 +1964,10 @@ var XPIProvider = {
while ((entry = entries.nextFile)) {
let id = entry.leafName;
if (entry.isFile()) {
if (id.endsWith(".xpi")) {
id = id.slice(0, -4);
} else {
logger.debug("Ignoring distribution add-on that isn't an XPI: " + entry.path);
continue;
}
} else if (!entry.isDirectory()) {
logger.debug("Ignoring distribution add-on that isn't a file or directory: " +
entry.path);
if (id.endsWith(".xpi")) {
id = id.slice(0, -4);
} else {
logger.debug("Ignoring distribution add-on that isn't an XPI: " + entry.path);
continue;
}
@ -2768,10 +2757,9 @@ var XPIProvider = {
};
for (let meth of ["cancelUninstallAddon", "getInstallForFile",
"getInstallForURL", "installAddonFromLocation",
"installAddonFromSources", "installTemporaryAddon",
"isInstallAllowed", "isInstallEnabled", "uninstallAddon",
"updateSystemAddons"]) {
"getInstallForURL", "installTemporaryAddon",
"isInstallAllowed", "isInstallEnabled",
"uninstallAddon", "updateSystemAddons"]) {
XPIProvider[meth] = function() {
return XPIInstall[meth](...arguments);
};
@ -2912,20 +2900,18 @@ class DirectoryInstallLocation {
if (id == DIR_STAGE || id == DIR_TRASH)
continue;
let directLoad = false;
if (entry.isFile() &&
id.substring(id.length - 4).toLowerCase() == ".xpi") {
directLoad = true;
let isFile = id.toLowerCase().endsWith(".xpi");
if (isFile) {
id = id.substring(0, id.length - 4);
}
if (!gIDTest.test(id)) {
logger.debug("Ignoring file entry whose name is not a valid add-on ID: " +
entry.path);
entry.path);
continue;
}
if (!directLoad && (entry.isFile() || entry.isSymlink())) {
if (!isFile && (entry.isFile() || entry.isSymlink())) {
let newEntry = this._readDirectoryFromFile(entry);
if (!newEntry) {
logger.debug("Deleting stale pointer file " + entry.path);

View File

@ -1,5 +1,3 @@
var AM_Ci = Ci;
const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
@ -25,12 +23,12 @@ fQthv3rDAcsWvi9YO7T+vylgZBgJfn1ZqpQqy58xN96uh6nPOw==`;
function overrideCertDB() {
// Unregister the real database.
let registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
let factory = registrar.getClassObject(CERTDB_CID, AM_Ci.nsIFactory);
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
let factory = registrar.getClassObject(CERTDB_CID, Ci.nsIFactory);
registrar.unregisterFactory(CERTDB_CID, factory);
// Get the real DB
let realCertDB = factory.createInstance(null, AM_Ci.nsIX509CertDB);
let realCertDB = factory.createInstance(null, Ci.nsIX509CertDB);
let fakeCert = realCertDB.constructX509FromBase64(CERT.replace(/\n/g, ""));
@ -39,11 +37,7 @@ function overrideCertDB() {
callback.openSignedAppFileFinished(Cr.NS_OK, null, fakeCert);
},
verifySignedDirectoryAsync(root, dir, callback) {
callback.verifySignedDirectoryFinished(Cr.NS_OK, fakeCert);
},
QueryInterface: ChromeUtils.generateQI([AM_Ci.nsIX509CertDB])
QueryInterface: ChromeUtils.generateQI([Ci.nsIX509CertDB])
};
for (let property of Object.keys(realCertDB)) {

View File

@ -1,25 +0,0 @@
/* exported startup, shutdown, install, uninstall, ADDON_ID, INSTALL_COMPLETE_PREF */
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
const INSTALL_COMPLETE_PREF = "bootstraptest.install_complete_done";
function install(data, reason) {}
// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside `XPIProvider.jsm`.
function startup(data, reason) {
// apply update immediately
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
upgrade.install();
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

View File

@ -1,35 +0,0 @@
/* exported startup, shutdown, install, uninstall, ADDON_ID, INSTALL_COMPLETE_PREF */
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
const INSTALL_COMPLETE_PREF = "bootstraptest.install_complete_done";
// global reference to hold upgrade object
let gUpgrade;
function install(data, reason) {}
// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside `XPIProvider.jsm`.
function startup(data, reason) {
// do not apply update immediately, hold on to for later
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
gUpgrade = upgrade;
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
// add a listener so the test can pass control back
AddonManager.addAddonListener({
onFakeEvent: () => {
gUpgrade.install();
}
});
}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

View File

@ -1,27 +0,0 @@
/* exported startup, shutdown, install, uninstall, ADDON_ID */
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
const ADDON_ID = "test_delay_update_ignore@tests.mozilla.org";
const TEST_IGNORE_PREF = "delaytest.ignore";
function install(data, reason) {}
// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside `XPIProvider.jsm`.
function startup(data, reason) {
Services.prefs.setBoolPref(TEST_IGNORE_PREF, false);
// explicitly ignore update, will be queued for next restart
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
Services.prefs.setBoolPref(TEST_IGNORE_PREF, true);
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

View File

@ -1,22 +0,0 @@
/* exported startup, shutdown, install, uninstall */
ChromeUtils.import("resource://gre/modules/Services.jsm");
function install(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 2);
Services.prefs.setIntPref("bootstraptest.install_reason", reason);
}
function startup(data, reason) {
Services.prefs.setIntPref("bootstraptest.active_version", 2);
Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
}
function shutdown(data, reason) {
Services.prefs.setIntPref("bootstraptest.active_version", 0);
Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
}
function uninstall(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 0);
Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
}

View File

@ -1,23 +0,0 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon2@tests.mozilla.org</em:id>
<em:version>2.0</em:version>
<!-- Front End MetaData -->
<em:name>Distributed add-ons test</em:name>
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>1</em:minVersion>
<em:maxVersion>5</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -1 +0,0 @@
Test of a file in a sub directory

View File

@ -47,35 +47,6 @@ add_task(async function setup() {
}],
name: "Packed, Disabled",
}, profileDir);
// Unpacked, enabled
await promiseWriteInstallRDFToDir({
id: "unpacked-enabled@tests.mozilla.org",
version: "1.0",
bootstrap: true,
unpack: true,
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}],
name: "Unpacked, Enabled",
}, profileDir, undefined, "extraFile.js");
// Unpacked, disabled
await promiseWriteInstallRDFToDir({
id: "unpacked-disabled@tests.mozilla.org",
version: "1.0",
bootstrap: true,
unpack: true,
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}],
name: "Unpacked, disabled",
}, profileDir, undefined, "extraFile.js");
});
// Keep track of the last time stamp we've used, so that we can keep moving
@ -113,16 +84,13 @@ async function getXSJSON() {
add_task(async function detect_touches() {
await promiseStartupManager();
let [/* pe */, pd, /* ue */, ud] = await promiseAddonsByIDs([
let [/* pe */, pd] = await promiseAddonsByIDs([
"packed-enabled@tests.mozilla.org",
"packed-disabled@tests.mozilla.org",
"unpacked-enabled@tests.mozilla.org",
"unpacked-disabled@tests.mozilla.org"
]);
info("Disable test add-ons");
pd.userDisabled = true;
ud.userDisabled = true;
let XS = getXS();
@ -134,8 +102,6 @@ add_task(async function detect_touches() {
// State should correctly reflect enabled/disabled
Assert.ok(states.get("packed-enabled@tests.mozilla.org").enabled);
Assert.ok(!states.get("packed-disabled@tests.mozilla.org").enabled);
Assert.ok(states.get("unpacked-enabled@tests.mozilla.org").enabled);
Assert.ok(!states.get("unpacked-disabled@tests.mozilla.org").enabled);
// Touch various files and make sure the change is detected.
@ -148,35 +114,6 @@ add_task(async function detect_touches() {
let pdFile = profileDir.clone();
pdFile.append("packed-disabled@tests.mozilla.org.xpi");
checkChange(XS, pdFile, true);
// We notice changing install.rdf for an enabled unpacked add-on.
let ueDir = profileDir.clone();
ueDir.append("unpacked-enabled@tests.mozilla.org");
let manifest = ueDir.clone();
manifest.append("install.rdf");
checkChange(XS, manifest, true);
// We notice changing install.rdf for a *disabled* unpacked add-on.
let udDir = profileDir.clone();
udDir.append("unpacked-disabled@tests.mozilla.org");
manifest = udDir.clone();
manifest.append("install.rdf");
checkChange(XS, manifest, true);
// Finally, the case we actually care about...
// We *don't* notice changing another file for disabled unpacked add-on.
let otherFile = udDir.clone();
otherFile.append("extraFile.js");
checkChange(XS, otherFile, false);
/*
* When we enable an unpacked add-on that was modified while it was
* disabled, we reflect the new timestamp in the add-on DB (otherwise, we'll
* think it changed on next restart).
*/
ud.userDisabled = false;
let xState = XS.getAddon("app-profile", ud.id);
Assert.ok(xState.enabled);
Assert.equal(xState.mtime, ud.updateDate.getTime());
});
/*
@ -184,11 +121,9 @@ add_task(async function detect_touches() {
* extensions.xpiState preference.
*/
add_task(async function uninstall_bootstrap() {
let [pe, /* pd, ue, ud */] = await promiseAddonsByIDs([
let [pe, /* pd */] = await promiseAddonsByIDs([
"packed-enabled@tests.mozilla.org",
"packed-disabled@tests.mozilla.org",
"unpacked-enabled@tests.mozilla.org",
"unpacked-disabled@tests.mozilla.org"
]);
pe.uninstall();

View File

@ -28,7 +28,7 @@ testserver.registerDirectory("/data/", do_get_file("data"));
testserver.registerDirectory("/addons/", do_get_file("addons"));
async function createIgnoreAddon() {
await promiseWriteInstallRDFToDir({
await promiseWriteInstallRDFToXPI({
id: IGNORE_ID,
version: "1.0",
bootstrap: true,
@ -40,16 +40,40 @@ async function createIgnoreAddon() {
maxVersion: "1"
}],
name: "Test Delay Update Ignore",
}, profileDir, IGNORE_ID, "bootstrap.js");
}, profileDir, IGNORE_ID, {
"bootstrap.js": String.raw`
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
let unpacked_addon = profileDir.clone();
unpacked_addon.append(IGNORE_ID);
do_get_file("data/test_delay_update_ignore/bootstrap.js")
.copyTo(unpacked_addon, "bootstrap.js");
const ADDON_ID = "test_delay_update_ignore@tests.mozilla.org";
const TEST_IGNORE_PREF = "delaytest.ignore";
function install(data, reason) {}
// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside XPIProvider.jsm.
function startup(data, reason) {
Services.prefs.setBoolPref(TEST_IGNORE_PREF, false);
// explicitly ignore update, will be queued for next restart
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
Services.prefs.setBoolPref(TEST_IGNORE_PREF, true);
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
}
function shutdown(data, reason) {}
function uninstall(data, reason) {}
`,
});
}
async function createCompleteAddon() {
await promiseWriteInstallRDFToDir({
await promiseWriteInstallRDFToXPI({
id: COMPLETE_ID,
version: "1.0",
bootstrap: true,
@ -61,16 +85,38 @@ async function createCompleteAddon() {
maxVersion: "1"
}],
name: "Test Delay Update Complete",
}, profileDir, COMPLETE_ID, "bootstrap.js");
}, profileDir, COMPLETE_ID, {
"bootstrap.js": String.raw`
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
let unpacked_addon = profileDir.clone();
unpacked_addon.append(COMPLETE_ID);
do_get_file("data/test_delay_update_complete/bootstrap.js")
.copyTo(unpacked_addon, "bootstrap.js");
const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
const INSTALL_COMPLETE_PREF = "bootstraptest.install_complete_done";
function install(data, reason) {}
// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside XPIProvider.jsm.
function startup(data, reason) {
// apply update immediately
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
upgrade.install();
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
}
function shutdown(data, reason) {}
function uninstall(data, reason) {}
`,
});
}
async function createDeferAddon() {
await promiseWriteInstallRDFToDir({
await promiseWriteInstallRDFToXPI({
id: DEFER_ID,
version: "1.0",
bootstrap: true,
@ -82,12 +128,44 @@ async function createDeferAddon() {
maxVersion: "1"
}],
name: "Test Delay Update Defer",
}, profileDir, DEFER_ID, "bootstrap.js");
}, profileDir, DEFER_ID, {
"bootstrap.js": String.raw`
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
let unpacked_addon = profileDir.clone();
unpacked_addon.append(DEFER_ID);
do_get_file("data/test_delay_update_defer/bootstrap.js")
.copyTo(unpacked_addon, "bootstrap.js");
const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
const INSTALL_COMPLETE_PREF = "bootstraptest.install_complete_done";
// global reference to hold upgrade object
let gUpgrade;
function install(data, reason) {}
// normally we would use BootstrapMonitor here, but we need a reference to
// the symbol inside XPIProvider.jsm.
function startup(data, reason) {
// do not apply update immediately, hold on to for later
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
gUpgrade = upgrade;
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
// add a listener so the test can pass control back
AddonManager.addAddonListener({
onFakeEvent: () => {
gUpgrade.install();
}
});
}
function shutdown(data, reason) {}
function uninstall(data, reason) {}
`,
});
}
// add-on registers upgrade listener, and ignores update.

View File

@ -53,14 +53,6 @@ var addon1_3 = {
}]
};
function getActiveVersion() {
return Services.prefs.getIntPref("bootstraptest.active_version");
}
function getInstalledVersion() {
return Services.prefs.getIntPref("bootstraptest.installed_version");
}
async function setOldModificationTime() {
// Make sure the installed extension has an old modification time so any
// changes will be detected
@ -213,49 +205,5 @@ async function run_test_8() {
Assert.equal(a1.scope, AddonManager.SCOPE_PROFILE);
a1.uninstall();
executeSoon(run_test_9);
}
// Tests that bootstrapped add-ons distributed start up correctly, also that
// add-ons with multiple directories get copied fully
async function run_test_9() {
await promiseRestartManager();
// Copy the test add-on to the distro dir
let addon = do_get_file("data/test_distribution2_2");
addon.copyTo(distroDir, "addon2@tests.mozilla.org");
await promiseRestartManager("5");
let a2 = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
Assert.notEqual(a2, null);
Assert.ok(a2.isActive);
Assert.equal(getInstalledVersion(), 2);
Assert.equal(getActiveVersion(), 2);
Assert.ok(a2.hasResource("bootstrap.js"));
Assert.ok(a2.hasResource("subdir/dummy.txt"));
Assert.ok(a2.hasResource("subdir/subdir2/dummy2.txt"));
// Currently installs are unpacked if the source is a directory regardless
// of the install.rdf property or the global preference
let addonDir = profileDir.clone();
addonDir.append("addon2@tests.mozilla.org");
Assert.ok(addonDir.exists());
Assert.ok(addonDir.isDirectory());
addonDir.append("subdir");
Assert.ok(addonDir.exists());
Assert.ok(addonDir.isDirectory());
addonDir.append("subdir2");
Assert.ok(addonDir.exists());
Assert.ok(addonDir.isDirectory());
addonDir.append("dummy2.txt");
Assert.ok(addonDir.exists());
Assert.ok(addonDir.isFile());
a2.uninstall();
executeSoon(do_test_finished);
}

View File

@ -1,81 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const ID = "bootstrap1@tests.mozilla.org";
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
BootstrapMonitor.init();
// Partial list of bootstrap reasons from XPIProvider.jsm
const BOOTSTRAP_REASONS = {
ADDON_INSTALL: 5,
ADDON_UPGRADE: 7,
ADDON_DOWNGRADE: 8,
};
// Install an unsigned add-on with no existing add-on present.
// Restart and make sure it is still around.
add_task(async function() {
await promiseStartupManager();
let extInstallCalled = false;
AddonManager.addInstallListener({
onExternalInstall: (aInstall) => {
Assert.equal(aInstall.id, ID);
Assert.equal(aInstall.version, "1.0");
extInstallCalled = true;
},
});
let installingCalled = false;
let installedCalled = false;
AddonManager.addAddonListener({
onInstalling: (aInstall) => {
Assert.equal(aInstall.id, ID);
Assert.equal(aInstall.version, "1.0");
installingCalled = true;
},
onInstalled: (aInstall) => {
Assert.equal(aInstall.id, ID);
Assert.equal(aInstall.version, "1.0");
installedCalled = true;
},
onInstallStarted: (aInstall) => {
do_throw("onInstallStarted called unexpectedly");
}
});
await AddonManager.installAddonFromSources(do_get_file("data/from_sources/"));
Assert.ok(extInstallCalled);
Assert.ok(installingCalled);
Assert.ok(installedCalled);
let install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
let addon = await promiseAddonByID(ID);
Assert.notEqual(addon, null);
Assert.equal(addon.version, "1.0");
Assert.equal(addon.name, "Test Bootstrap 1");
Assert.ok(addon.isCompatible);
Assert.ok(!addon.appDisabled);
Assert.ok(addon.isActive);
Assert.equal(addon.type, "extension");
Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
await promiseRestartManager();
install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
BootstrapMonitor.checkAddonStarted(ID, "1.0");
addon = await promiseAddonByID(ID);
Assert.notEqual(addon, null);
await promiseRestartManager();
});

View File

@ -10,6 +10,8 @@ BootstrapMonitor.init();
// Ensure that a proxy file to an add-on with a valid manifest works.
add_task(async function() {
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
await promiseStartupManager();
let tempdir = gTmpD.clone();
@ -50,7 +52,7 @@ add_task(async function() {
Assert.ok(!addon.appDisabled);
Assert.ok(addon.isActive);
Assert.equal(addon.type, "extension");
Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
Assert.ok(proxyFile.exists());

View File

@ -171,7 +171,7 @@ add_task(async function() {
};
let target;
if (packed) {
if (!packed) {
target = tempdir.clone();
target.append(ID);
@ -214,6 +214,8 @@ add_task(async function() {
addon = await promiseAddonByID(ID);
let signedState = packed ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_UNKNOWN;
// temporary add-on is installed and started
Assert.notEqual(addon, null);
Assert.equal(addon.version, newversion);
@ -222,7 +224,7 @@ add_task(async function() {
Assert.ok(!addon.appDisabled);
Assert.ok(addon.isActive);
Assert.equal(addon.type, "extension");
Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
Assert.equal(addon.signedState, mozinfo.addon_signing ? signedState : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
// Now restart, the temporary addon will go away which should
// be the opposite action (ie, if the temporary addon was an
@ -430,7 +432,7 @@ add_task(async function() {
Assert.ok(!addon.appDisabled);
Assert.ok(addon.isActive);
Assert.equal(addon.type, "extension");
Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
addon.uninstall();
@ -676,7 +678,7 @@ add_task(async function() {
Assert.ok(!tempAddon.appDisabled);
Assert.ok(tempAddon.isActive);
Assert.equal(tempAddon.type, "extension");
Assert.equal(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
Assert.equal(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
tempAddon.uninstall();
unpacked_addon.remove(true);

View File

@ -8,7 +8,5 @@ tags = addons
[test_webextension_paths.js]
tags = webextensions
[test_webextension_theme.js]
tags = webextensions
[test_filepointer.js]

View File

@ -140,7 +140,6 @@ tags = blocklist
skip-if = appname != "firefox"
[test_harness.js]
[test_install.js]
[test_install_from_sources.js]
[test_install_icons.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"
@ -182,6 +181,7 @@ skip-if = os == "android"
[test_provider_unsafe_access_shutdown.js]
[test_provider_unsafe_access_startup.js]
[test_proxies.js]
skip-if = require_signing
[test_proxy.js]
[test_registerchrome.js]
[test_registry.js]
@ -288,3 +288,5 @@ tags = webextensions
[test_webextension_langpack.js]
skip-if = appname == "thunderbird"
tags = webextensions
[test_webextension_theme.js]
tags = webextensions