Merge inbound to mozilla-central. a=merge

This commit is contained in:
Gurzau Raul 2018-05-20 12:53:43 +03:00
Родитель 5b08ef94fe 7ea7d29fbb
Коммит 8affe6e831
28 изменённых файлов: 999 добавлений и 1331 удалений

Просмотреть файл

@ -30,9 +30,6 @@ pref("extensions.logging.enabled", false);
// Disables strict compatibility, making addons compatible-by-default.
pref("extensions.strictCompatibility", false);
// Specifies a minimum maxVersion an addon needs to say it's compatible with
// for it to be compatible by default.
pref("extensions.minCompatibleAppVersion", "4.0");
// Temporary preference to forcibly make themes more safe with Australis even if
// extensions.checkCompatibility=false has been set.
pref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "29.0a1");

Просмотреть файл

@ -1,138 +0,0 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
function testForExpectedSymbols(stage, data) {
const expectedSymbols = [ "Worker", "ChromeWorker" ];
for (var symbol of expectedSymbols) {
Services.prefs.setBoolPref("workertest.bootstrap." + stage + "." + symbol,
symbol in this);
}
}
var gWorkerAndCallback = {
_ensureStarted: function() {
if (!this._worker) {
throw new Error("Not yet started!");
}
},
start: function(data) {
if (!this._worker) {
this._worker = new Worker("chrome://workerbootstrap/content/worker.js");
this._worker.onerror = function(event) {
Cu.reportError(event.message);
event.preventDefault();
};
}
},
stop: function() {
if (this._worker) {
this._worker.terminate();
delete this._worker;
}
},
set callback(val) {
this._ensureStarted();
var callback = val.QueryInterface(Ci.nsIObserver);
if (this._callback != callback) {
if (callback) {
this._worker.onmessage = function(event) {
callback.observe(this, event.type, event.data);
};
this._worker.onerror = function(event) {
callback.observe(this, event.type, event.message);
event.preventDefault();
};
}
else {
this._worker.onmessage = null;
this._worker.onerror = null;
}
this._callback = callback;
}
},
get callback() {
return this._callback;
},
postMessage: function(data) {
this._ensureStarted();
this._worker.postMessage(data);
},
terminate: function() {
this._ensureStarted();
this._worker.terminate();
delete this._callback;
}
};
function WorkerTestBootstrap() {
}
WorkerTestBootstrap.prototype = {
observe: function(subject, topic, data) {
gWorkerAndCallback.callback = subject;
switch (topic) {
case "postMessage":
gWorkerAndCallback.postMessage(data);
break;
case "terminate":
gWorkerAndCallback.terminate();
break;
default:
throw new Error("Unknown worker command");
}
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver])
};
var gFactory = {
register: function() {
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
var classID = Components.ID("{36b5df0b-8dcf-4aa2-9c45-c51d871295f9}");
var description = "WorkerTestBootstrap";
var contractID = "@mozilla.org/test/workertestbootstrap;1";
var factory = XPCOMUtils._getFactory(WorkerTestBootstrap);
registrar.registerFactory(classID, description, contractID, factory);
this.unregister = function() {
registrar.unregisterFactory(classID, factory);
delete this.unregister;
};
}
};
function install(data, reason) {
testForExpectedSymbols("install");
}
function startup(data, reason) {
testForExpectedSymbols("startup");
gFactory.register();
gWorkerAndCallback.start(data);
}
function shutdown(data, reason) {
testForExpectedSymbols("shutdown");
gWorkerAndCallback.stop();
gFactory.unregister();
}
function uninstall(data, reason) {
testForExpectedSymbols("uninstall");
}

Просмотреть файл

@ -1,31 +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:name>WorkerTestBootstrap</em:name>
<em:description>Worker functions for use in testing.</em:description>
<em:creator>Mozilla</em:creator>
<em:version>2016.03.09</em:version>
<em:id>workerbootstrap-test@mozilla.org</em:id>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<!-- Firefox -->
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>45.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<em:targetApplication>
<Description>
<!-- Fennec -->
<em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
<em:minVersion>45.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

Просмотреть файл

@ -1,3 +0,0 @@
workerbootstrap.jar:
% content workerbootstrap %content/
content/worker.js (worker.js)

Просмотреть файл

@ -4,17 +4,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPI_NAME = 'workerbootstrap'
JAR_MANIFESTS += ['jar.mn']
USE_EXTENSION_MANIFEST = True
NO_JS_MANIFEST = True
FINAL_TARGET_FILES += [
'bootstrap.js',
'install.rdf',
]
TEST_HARNESS_FILES.testing.mochitest.extensions += [
'workerbootstrap-test@mozilla.org.xpi',
]

Просмотреть файл

@ -1,7 +0,0 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
onmessage = function(event) {
postMessage(event.data);
}

Двоичный файл не отображается.

Просмотреть файл

@ -1,7 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['bootstrap']

Просмотреть файл

@ -4899,7 +4899,6 @@ pref("xpinstall.whitelist.required", true);
// Only Firefox requires add-on signatures
pref("xpinstall.signatures.required", false);
pref("extensions.langpacks.signatures.required", false);
pref("extensions.minCompatiblePlatformVersion", "2.0");
pref("extensions.webExtensionsMinPlatformVersion", "42.0a1");
pref("extensions.legacy.enabled", true);

Просмотреть файл

@ -576,7 +576,6 @@ class UrlFinder(object):
dir_parts = parts[0].rsplit(app_name + '/', 1)
url = mozpath.normpath(mozpath.join(self.topobjdir, 'dist', 'bin', dir_parts[1].lstrip('/'), parts[1].lstrip('/')))
elif '.xpi!' in url:
# e.g. file:///tmp/tmpMdo5gV.mozrunner/extensions/workerbootstrap-test@mozilla.org.xpi!/bootstrap.js
# This matching mechanism is quite brittle and based on examples seen in the wild.
# There's no rule to match the XPI name to the path in dist/xpi-stage.
parts = url_obj.path.split('.xpi!', 1)

Просмотреть файл

@ -297,10 +297,6 @@ class TestUrlFinder(unittest.TestCase):
'path4',
None
],
'dist/xpi-stage/workerbootstrap/bootstrap.js': [
'path5',
None
],
'dist/bin/modules/osfile/osfile_async_worker.js': [
'toolkit/components/osfile/modules/osfile_async_worker.js',
None
@ -340,7 +336,6 @@ class TestUrlFinder(unittest.TestCase):
('jar:file:///home/worker/workspace/build/application/' + app_name + '/' + omnijar_name + '!/components/MainProcessSingleton.js', 'path1'),
('jar:file:///home/worker/workspace/build/application/' + app_name + '/browser/' + omnijar_name + '!/components/nsSessionStartup.js', 'path2'),
('jar:file:///home/worker/workspace/build/application/' + app_name + '/browser/features/firefox@getpocket.com.xpi!/bootstrap.js', 'path4'),
('jar:file:///tmp/tmpMdo5gV.mozrunner/extensions/workerbootstrap-test@mozilla.org.xpi!/bootstrap.js', 'path5'),
]
url_finder = lcov_rewriter.UrlFinder(self._chrome_map_file, '', '', [])

Просмотреть файл

@ -239,7 +239,6 @@ class RobocopTestRunner(MochitestDesktop):
self.options.extensionsToExclude.extend([
'mochikit@mozilla.org',
'workerbootstrap-test@mozilla.org.xpi',
'indexedDB-test@mozilla.org.xpi',
])

Просмотреть файл

@ -0,0 +1,3 @@
[history_go_zero.html]
disabled:
if debug and (os == "linux"): https://bugzilla.mozilla.org/show_bug.cgi?id=1217701

Просмотреть файл

@ -375,11 +375,8 @@ var AddonTestUtils = {
}
// Check that the temporary directory is empty
var dirEntries = this.tempDir.directoryEntries
.QueryInterface(Ci.nsIDirectoryEnumerator);
var entries = [];
while (dirEntries.hasMoreElements()) {
let {leafName} = dirEntries.nextFile;
for (let {leafName} of this.iterDirectory(this.tempDir)) {
if (!ignoreEntries.has(leafName)) {
entries.push(leafName);
}
@ -387,8 +384,6 @@ var AddonTestUtils = {
if (entries.length)
throw new Error(`Found unexpected files in temporary directory: ${entries.join(", ")}`);
dirEntries.close();
try {
appDirForAddons.remove(true);
} catch (ex) {
@ -399,20 +394,9 @@ var AddonTestUtils = {
let featuresDir = this.profileDir.clone();
featuresDir.append("features");
// upgrade directories will be in UUID folders under features/
let systemAddonDirs = [];
if (featuresDir.exists()) {
let featuresDirEntries = featuresDir.directoryEntries
.QueryInterface(Ci.nsIDirectoryEnumerator);
while (featuresDirEntries.hasMoreElements()) {
let entry = featuresDirEntries.getNext();
entry.QueryInterface(Ci.nsIFile);
systemAddonDirs.push(entry);
}
systemAddonDirs.map(dir => {
dir.append("stage");
pathShouldntExist(dir);
});
for (let dir of this.iterDirectory(featuresDir)) {
dir.append("stage");
pathShouldntExist(dir);
}
// ensure no leftover files in the user addon location
@ -447,6 +431,33 @@ var AddonTestUtils = {
});
},
/**
* Iterates over the entries in a given directory.
*
* Fails silently if the given directory does not exist.
*
* @param {nsIFile} dir
* Directory to iterate.
*/
* iterDirectory(dir) {
let dirEnum;
try {
dirEnum = dir.directoryEntries;
let file;
while ((file = dirEnum.nextFile)) {
yield file;
}
} catch (e) {
if (dir.exists()) {
Cu.reportError(e);
}
} finally {
if (dirEnum) {
dirEnum.close();
}
}
},
/**
* Creates a new HttpServer for testing, and begins listening on the
* specified port. Automatically shuts down the server when the test
@ -1178,11 +1189,8 @@ var AddonTestUtils = {
setExtensionModifiedTime(ext, time) {
ext.lastModifiedTime = time;
if (ext.isDirectory()) {
let entries = ext.directoryEntries
.QueryInterface(Ci.nsIDirectoryEnumerator);
while (entries.hasMoreElements())
this.setExtensionModifiedTime(entries.nextFile, time);
entries.close();
for (let file of this.iterDirectory(ext))
this.setExtensionModifiedTime(file, time);
}
},

Просмотреть файл

@ -121,7 +121,7 @@ const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"];
// Properties to save in JSON file
const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
const PROP_JSON_FIELDS = ["id", "syncGUID", "version", "type",
"updateURL", "optionsURL",
"optionsType", "optionsBrowserStyle", "aboutURL",
"defaultLocale", "visible", "active", "userDisabled",
@ -269,12 +269,15 @@ class AddonInternal {
}
copyProperties(addonData, PROP_JSON_FIELDS, this);
this.location = addonData.location;
if (!this.dependencies)
this.dependencies = [];
Object.freeze(this.dependencies);
this.addedToDatabase();
if (this.location) {
this.addedToDatabase();
}
if (!addonData._sourceBundle) {
throw new Error("Expected passed argument to contain a path");
@ -292,14 +295,7 @@ class AddonInternal {
}
addedToDatabase() {
if (this._installLocation) {
this.location = this._installLocation.name;
} else if (this.location) {
this._installLocation = XPIProvider.installLocationsByName[this.location];
}
this._key = `${this.location}:${this.id}`;
this._key = `${this.location.name}:${this.id}`;
this.inDatabase = true;
}
@ -357,7 +353,7 @@ class AddonInternal {
}
get isCorrectlySigned() {
switch (this._installLocation.name) {
switch (this.location.name) {
case KEY_APP_SYSTEM_ADDONS:
// System add-ons must be signed by the system key.
return this.signedState == AddonManager.SIGNEDSTATE_SYSTEM;
@ -386,6 +382,10 @@ class AddonInternal {
return this.isCompatibleWith();
}
get hidden() {
return this.location.isSystem;
}
get disabled() {
return (this.userDisabled || this.appDisabled || this.softDisabled);
}
@ -467,17 +467,6 @@ class AddonInternal {
}
}
// Extremely old extensions should not be compatible by default.
let minCompatVersion;
if (app.id == Services.appinfo.ID)
minCompatVersion = XPIProvider.minCompatibleAppVersion;
else if (app.id == TOOLKIT_ID)
minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
if (minCompatVersion &&
Services.vc.compare(minCompatVersion, maxVersion) > 0)
return false;
return Services.vc.compare(version, minVersion) >= 0;
}
@ -571,7 +560,9 @@ class AddonInternal {
}
toJSON() {
return copyProperties(this, PROP_JSON_FIELDS);
let obj = copyProperties(this, PROP_JSON_FIELDS);
obj.location = this.location.name;
return obj;
}
/**
@ -611,11 +602,11 @@ class AddonInternal {
// Add-ons that are in locked install locations, or are pending uninstall
// cannot be upgraded or uninstalled
if (!this._installLocation.locked && !this.pendingUninstall) {
if (!this.location.locked && !this.pendingUninstall) {
// System add-on upgrades are triggered through a different mechanism (see updateSystemAddons())
let isSystem = this._installLocation.isSystem;
let isSystem = this.location.isSystem;
// Add-ons that are installed by a file link cannot be upgraded.
if (!this._installLocation.isLinkedAddon(this.id) && !isSystem) {
if (!this.location.isLinkedAddon(this.id) && !isSystem) {
permissions |= AddonManager.PERM_CAN_UPGRADE;
}
@ -670,7 +661,7 @@ AddonWrapper = class {
}
get temporarilyInstalled() {
return addonFor(this)._installLocation == XPIInternal.TemporaryInstallLocation;
return addonFor(this).location.isTemporary;
}
get aboutURL() {
@ -843,8 +834,8 @@ AddonWrapper = class {
get scope() {
let addon = addonFor(this);
if (addon._installLocation)
return addon._installLocation.scope;
if (addon.location)
return addon.location.scope;
return AddonManager.SCOPE_PROFILE;
}
@ -963,22 +954,22 @@ AddonWrapper = class {
get hidden() {
let addon = addonFor(this);
if (addon._installLocation.name == KEY_APP_TEMPORARY)
if (addon.location.isTemporary)
return false;
return addon._installLocation.isSystem;
return addon.location.isSystem;
}
get isSystem() {
let addon = addonFor(this);
return addon._installLocation.isSystem;
return addon.location.isSystem;
}
// Returns true if Firefox Sync should sync this addon. Only addons
// in the profile install location are considered syncable.
get isSyncable() {
let addon = addonFor(this);
return (addon._installLocation.name == KEY_APP_PROFILE);
return (addon.location.name == KEY_APP_PROFILE);
}
get userPermissions() {
@ -991,12 +982,12 @@ AddonWrapper = class {
uninstall(alwaysAllowUndo) {
let addon = addonFor(this);
XPIProvider.uninstallAddon(addon, alwaysAllowUndo);
XPIInstall.uninstallAddon(addon, alwaysAllowUndo);
}
cancelUninstall() {
let addon = addonFor(this);
XPIProvider.cancelUninstallAddon(addon);
XPIInstall.cancelUninstallAddon(addon);
}
findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
@ -1285,6 +1276,9 @@ this.XPIDatabase = {
jsonFile: FileUtils.getFile(KEY_PROFILEDIR, [FILE_JSON_DB], true),
rebuildingDatabase: false,
syncLoadingDB: false,
// Add-ons from the database in locations which are no longer
// supported.
orphanedAddons: [],
_saveTask: null,
@ -1373,7 +1367,7 @@ this.XPIDatabase = {
let toSave = {
schemaVersion: DB_SCHEMA,
addons: Array.from(this.addonDB.values())
.filter(addon => addon.location != KEY_APP_TEMPORARY),
.filter(addon => !addon.location.isTemporary),
};
return toSave;
},
@ -1447,9 +1441,14 @@ this.XPIDatabase = {
// wrong OS
logger.warn("Could not find source bundle for add-on " + loadedAddon.id, e);
}
loadedAddon.location = XPIStates.getLocation(loadedAddon.location);
let newAddon = new AddonInternal(loadedAddon);
addonDB.set(newAddon._key, newAddon);
if (loadedAddon.location) {
addonDB.set(newAddon._key, newAddon);
} else {
this.orphanedAddons.push(newAddon);
}
});
parseTimer.done();
@ -1787,7 +1786,7 @@ this.XPIDatabase = {
* @returns {Promise<Array<AddonInternal>>}
*/
getAddonsInLocation(aLocation) {
return this.getAddonList(aAddon => aAddon._installLocation.name == aLocation);
return this.getAddonList(aAddon => aAddon.location.name == aLocation);
},
/**
@ -1911,12 +1910,12 @@ this.XPIDatabase = {
LEGACY_TYPES.has(addon.type) &&
// Legacy add-ons are allowed in the system location.
!addon._installLocation.isSystem &&
!addon.location.isSystem &&
// Legacy extensions may be installed temporarily in
// non-release builds.
!(AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS &&
addon._installLocation.name == KEY_APP_TEMPORARY) &&
addon.location.isTemporary) &&
// Properly signed legacy extensions are allowed.
addon.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED);
@ -2056,9 +2055,9 @@ this.XPIDatabase = {
},
updateXPIStates(addon) {
let xpiState = XPIStates.getAddon(addon.location, addon.id);
if (xpiState) {
xpiState.syncWithDB(addon);
let state = addon.location && addon.location.get(addon.id);
if (state) {
state.syncWithDB(addon);
XPIStates.save();
}
},
@ -2092,7 +2091,7 @@ this.XPIDatabase = {
*
* @param {string} aId
* The ID of the add-on to make visible
* @param {InstallLocation} aLocation
* @param {XPIStateLocation} aLocation
* The location in which to make the add-on visible.
* @returns {AddonInternal?}
* The add-on instance which was marked visible, if any.
@ -2276,15 +2275,7 @@ this.XPIDatabase = {
// Flag that active states in the database need to be updated on shutdown
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
// Sync with XPIStates.
let xpiState = XPIStates.getAddon(aAddon.location, aAddon.id);
if (xpiState) {
xpiState.syncWithDB(aAddon);
XPIStates.save();
} else {
// There should always be an xpiState
logger.warn("No XPIState for ${id} in ${location}", aAddon);
}
this.updateXPIStates(aAddon);
// Have we just gone back to the current state?
if (isDisabled != aAddon.active) {
@ -2312,11 +2303,7 @@ this.XPIDatabase = {
if (isTheme(aAddon.type)) {
if (!isDisabled) {
AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type);
if (xpiState) {
xpiState.syncWithDB(aAddon);
XPIStates.save();
}
this.updateXPIStates(aAddon);
} else if (isDisabled && !aBecauseSelecting) {
AddonManagerPrivate.notifyAddonChanged(null, "theme");
}
@ -2359,11 +2346,11 @@ this.XPIDatabaseReconcile = {
flattenByID(addonMap, hideLocation) {
let map = new Map();
for (let installLocation of XPIProvider.installLocations) {
if (installLocation.name == hideLocation)
for (let loc of XPIStates.locations()) {
if (loc.name == hideLocation)
continue;
let locationMap = addonMap.get(installLocation.name);
let locationMap = addonMap.get(loc.name);
if (!locationMap)
continue;
@ -2411,7 +2398,7 @@ this.XPIDatabaseReconcile = {
* has been upgraded or become corrupt and add-on data has to be reloaded
* into it.
*
* @param {InstallLocation} aInstallLocation
* @param {XPIStateLocation} aLocation
* The install location containing the add-on
* @param {string} aId
* The ID of the add-on
@ -2429,9 +2416,9 @@ this.XPIDatabaseReconcile = {
* A boolean indicating if flushing caches is required to complete
* changing this add-on
*/
addMetadata(aInstallLocation, aId, aAddonState, aNewAddon, aOldAppVersion,
addMetadata(aLocation, aId, aAddonState, aNewAddon, aOldAppVersion,
aOldPlatformVersion) {
logger.debug("New add-on " + aId + " installed in " + aInstallLocation.name);
logger.debug(`New add-on ${aId} installed in ${aLocation.name}`);
// We treat this is a new install if,
//
@ -2452,12 +2439,11 @@ this.XPIDatabaseReconcile = {
if (!aNewAddon) {
// Load the manifest from the add-on.
let file = new nsIFile(aAddonState.path);
aNewAddon = XPIInstall.syncLoadManifestFromFile(file, aInstallLocation);
aNewAddon = XPIInstall.syncLoadManifestFromFile(file, aLocation);
}
// The add-on in the manifest should match the add-on ID.
if (aNewAddon.id != aId) {
throw new Error("Invalid addon ID: expected addon ID " + aId +
", found " + aNewAddon.id + " in manifest");
throw new Error(`Invalid addon ID: expected addon ID ${aId}, found ${aNewAddon.id} in manifest`);
}
unsigned = XPIDatabase.mustSign(aNewAddon.type) && !aNewAddon.isCorrectlySigned;
@ -2465,18 +2451,18 @@ this.XPIDatabaseReconcile = {
throw Error(`Extension ${aNewAddon.id} is not correctly signed`);
}
} catch (e) {
logger.warn("addMetadata: Add-on " + aId + " is invalid", e);
logger.warn(`addMetadata: Add-on ${aId} is invalid`, e);
// Remove the invalid add-on from the install location if the install
// location isn't locked
if (aInstallLocation.isLinkedAddon(aId))
if (aLocation.isLinkedAddon(aId))
logger.warn("Not uninstalling invalid item because it is a proxy file");
else if (aInstallLocation.locked)
else if (aLocation.locked)
logger.warn("Could not uninstall invalid item from locked install location");
else if (unsigned && !isNewInstall)
logger.warn("Not uninstalling existing unsigned add-on");
else
aInstallLocation.uninstallAddon(aId);
aLocation.installer.uninstallAddon(aId);
return null;
}
@ -2486,9 +2472,7 @@ this.XPIDatabaseReconcile = {
// Assume that add-ons in the system add-ons install location aren't
// foreign and should default to enabled.
aNewAddon.foreignInstall = isDetectedInstall &&
aInstallLocation.name != KEY_APP_SYSTEM_ADDONS &&
aInstallLocation.name != KEY_APP_SYSTEM_DEFAULTS;
aNewAddon.foreignInstall = isDetectedInstall && !aLocation.isSystem;
// appDisabled depends on whether the add-on is a foreignInstall so update
aNewAddon.appDisabled = !XPIDatabase.isUsableAddon(aNewAddon);
@ -2497,9 +2481,8 @@ this.XPIDatabaseReconcile = {
// If the add-on is a foreign install and is in a scope where add-ons
// that were dropped in should default to disabled then disable it
let disablingScopes = Services.prefs.getIntPref(PREF_EM_AUTO_DISABLED_SCOPES, 0);
if (aInstallLocation.scope & disablingScopes) {
logger.warn("Disabling foreign installed add-on " + aNewAddon.id + " in "
+ aInstallLocation.name);
if (aLocation.scope & disablingScopes) {
logger.warn(`Disabling foreign installed add-on ${aNewAddon.id} in ${aLocation.name}`);
aNewAddon.userDisabled = true;
aNewAddon.seen = false;
}
@ -2517,7 +2500,7 @@ this.XPIDatabaseReconcile = {
*/
removeMetadata(aOldAddon) {
// This add-on has disappeared
logger.debug("Add-on " + aOldAddon.id + " removed from " + aOldAddon.location);
logger.debug("Add-on " + aOldAddon.id + " removed from " + aOldAddon.location.name);
XPIDatabase.removeAddonMetadata(aOldAddon);
},
@ -2525,7 +2508,7 @@ this.XPIDatabaseReconcile = {
* Updates an add-on's metadata and determines. This is called when either the
* add-on's install directory path or last modified time has changed.
*
* @param {InstallLocation} aInstallLocation
* @param {XPIStateLocation} aLocation
* The install location containing the add-on
* @param {AddonInternal} aOldAddon
* The AddonInternal as it appeared the last time the application
@ -2538,26 +2521,28 @@ this.XPIDatabaseReconcile = {
* A boolean indicating if flushing caches is required to complete
* changing this add-on
*/
updateMetadata(aInstallLocation, aOldAddon, aAddonState, aNewAddon) {
logger.debug("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
updateMetadata(aLocation, aOldAddon, aAddonState, aNewAddon) {
logger.debug(`Add-on ${aOldAddon.id} modified in ${aLocation.name}`);
try {
// If there isn't an updated install manifest for this add-on then load it.
if (!aNewAddon) {
let file = new nsIFile(aAddonState.path);
aNewAddon = XPIInstall.syncLoadManifestFromFile(file, aInstallLocation, aOldAddon);
aNewAddon = XPIInstall.syncLoadManifestFromFile(file, aLocation, aOldAddon);
}
// The ID in the manifest that was loaded must match the ID of the old
// add-on.
if (aNewAddon.id != aOldAddon.id)
throw new Error("Incorrect id in install manifest for existing add-on " + aOldAddon.id);
throw new Error(`Incorrect id in install manifest for existing add-on ${aOldAddon.id}`);
} catch (e) {
logger.warn("updateMetadata: Add-on " + aOldAddon.id + " is invalid", e);
logger.warn(`updateMetadata: Add-on ${aOldAddon.id} is invalid`, e);
XPIDatabase.removeAddonMetadata(aOldAddon);
XPIStates.removeAddon(aOldAddon.location, aOldAddon.id);
if (!aInstallLocation.locked)
aInstallLocation.uninstallAddon(aOldAddon.id);
aOldAddon.location.removeAddon(aOldAddon.id);
if (!aLocation.locked)
aLocation.installer.uninstallAddon(aOldAddon.id);
else
logger.warn("Could not uninstall invalid item from locked install location");
@ -2575,7 +2560,7 @@ this.XPIDatabaseReconcile = {
* Updates an add-on's path for when the add-on has moved in the
* filesystem but hasn't changed in any other way.
*
* @param {InstallLocation} aInstallLocation
* @param {XPIStateLocation} aLocation
* The install location containing the add-on
* @param {AddonInternal} aOldAddon
* The AddonInternal as it appeared the last time the application
@ -2584,8 +2569,8 @@ this.XPIDatabaseReconcile = {
* The new state of the add-on
* @returns {AddonInternal}
*/
updatePath(aInstallLocation, aOldAddon, aAddonState) {
logger.debug("Add-on " + aOldAddon.id + " moved to " + aAddonState.path);
updatePath(aLocation, aOldAddon, aAddonState) {
logger.debug(`Add-on ${aOldAddon.id} moved to ${aAddonState.path}`);
aOldAddon.path = aAddonState.path;
aOldAddon._sourceBundle = new nsIFile(aAddonState.path);
@ -2596,7 +2581,7 @@ this.XPIDatabaseReconcile = {
* Called when no change has been detected for an add-on's metadata but the
* application has changed so compatibility may have changed.
*
* @param {InstallLocation} aInstallLocation
* @param {XPIStateLocation} aLocation
* The install location containing the add-on
* @param {AddonInternal} aOldAddon
* The AddonInternal as it appeared the last time the application
@ -2609,8 +2594,8 @@ this.XPIDatabaseReconcile = {
* @returns {AddonInternal}
* The new addon.
*/
updateCompatibility(aInstallLocation, aOldAddon, aAddonState, aReloadMetadata) {
logger.debug("Updating compatibility for add-on " + aOldAddon.id + " in " + aInstallLocation.name);
updateCompatibility(aLocation, aOldAddon, aAddonState, aReloadMetadata) {
logger.debug(`Updating compatibility for add-on ${aOldAddon.id} in ${aLocation.name}`);
let checkSigning = (aOldAddon.signedState === undefined &&
AddonSettings.ADDON_SIGNING &&
@ -2620,7 +2605,7 @@ this.XPIDatabaseReconcile = {
if (checkSigning || aReloadMetadata) {
try {
let file = new nsIFile(aAddonState.path);
manifest = XPIInstall.syncLoadManifestFromFile(file, aInstallLocation);
manifest = XPIInstall.syncLoadManifestFromFile(file, aLocation);
} catch (err) {
// If we can no longer read the manifest, it is no longer compatible.
aOldAddon.brokenManifest = true;
@ -2659,7 +2644,7 @@ this.XPIDatabaseReconcile = {
* bundle. Add-ons in these locations are expected to change whenever
* the application updates.
*
* @param {InstallLocation} location
* @param {XPIStateLocation} location
* The install location to check.
* @returns {boolean}
* True if this location is part of the application bundle.
@ -2692,7 +2677,7 @@ this.XPIDatabaseReconcile = {
updateExistingAddon(oldAddon, xpiState, newAddon, aUpdateCompatibility, aSchemaChange) {
XPIDatabase.recordAddonTelemetry(oldAddon);
let installLocation = oldAddon._installLocation;
let installLocation = oldAddon.location;
if (xpiState.mtime < oldAddon.updateDate) {
XPIProvider.setTelemetry(oldAddon.id, "olderFile", {
@ -2750,9 +2735,9 @@ this.XPIDatabaseReconcile = {
*/
processFileChanges(aManifests, aUpdateCompatibility, aOldAppVersion, aOldPlatformVersion,
aSchemaChange) {
let findManifest = (aInstallLocation, aId) => {
return (aManifests[aInstallLocation.name] &&
aManifests[aInstallLocation.name][aId]) || null;
let findManifest = (loc, id) => {
return (aManifests[loc.name] &&
aManifests[loc.name][id]) || null;
};
let addonExists = addon => addon._sourceBundle.exists();
@ -2762,7 +2747,7 @@ this.XPIDatabaseReconcile = {
// Get the previous add-ons from the database and put them into maps by location
for (let addon of XPIDatabase.getAddons()) {
previousAddons.get(addon.location).set(addon.id, addon);
previousAddons.get(addon.location.name).set(addon.id, addon);
}
// Keep track of add-ons whose blocklist status may have changed. We'll check this
@ -2773,19 +2758,18 @@ this.XPIDatabaseReconcile = {
// present we re-use the add-on objects from the database and update their
// details directly
let addonStates = new Map();
for (let installLocation of XPIProvider.installLocations) {
let locationAddons = currentAddons.get(installLocation.name);
for (let location of XPIStates.locations()) {
let locationAddons = currentAddons.get(location.name);
// Get all the on-disk XPI states for this location, and keep track of which
// ones we see in the database.
let states = XPIStates.getLocation(installLocation.name) || new Map();
let dbAddons = previousAddons.get(installLocation.name) || new Map();
let dbAddons = previousAddons.get(location.name) || new Map();
for (let [id, oldAddon] of dbAddons) {
// Check if the add-on is still installed
let xpiState = states.get(id);
let xpiState = location.get(id);
if (xpiState) {
let newAddon = this.updateExistingAddon(oldAddon, xpiState,
findManifest(installLocation, id),
findManifest(location, id),
aUpdateCompatibility, aSchemaChange);
if (newAddon) {
locationAddons.set(newAddon.id, newAddon);
@ -2800,11 +2784,11 @@ this.XPIDatabaseReconcile = {
}
}
for (let [id, xpiState] of states) {
for (let [id, xpiState] of location) {
if (locationAddons.has(id))
continue;
let newAddon = findManifest(installLocation, id);
let addon = this.addMetadata(installLocation, id, xpiState, newAddon,
let newAddon = findManifest(location, id);
let addon = this.addMetadata(location, id, xpiState, newAddon,
aOldAppVersion, aOldPlatformVersion);
if (addon) {
locationAddons.set(addon.id, addon);
@ -2813,22 +2797,13 @@ this.XPIDatabaseReconcile = {
}
}
// Remove metadata for any add-ons in install locations that are no
// longer supported.
for (let [locationName, addons] of previousAddons) {
if (!currentAddons.has(locationName)) {
for (let oldAddon of addons.values())
this.removeMetadata(oldAddon);
}
}
// Validate the updated system add-ons
let hideLocation;
{
let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
let systemAddonLocation = XPIStates.getLocation(KEY_APP_SYSTEM_ADDONS);
let addons = currentAddons.get(systemAddonLocation.name);
if (!systemAddonLocation.isValid(addons)) {
if (!systemAddonLocation.installer.isValid(addons)) {
// Hide the system add-on updates if any are invalid.
logger.info("One or more updated system add-ons invalid, falling back to defaults.");
hideLocation = systemAddonLocation.name;
@ -2840,10 +2815,16 @@ this.XPIDatabaseReconcile = {
let previousVisible = this.getVisibleAddons(previousAddons);
let currentVisible = this.flattenByID(currentAddons, hideLocation);
for (let addon of XPIDatabase.orphanedAddons.splice(0)) {
if (addon.visible) {
previousVisible.set(addon.id, addon);
}
}
for (let [id, addon] of currentVisible) {
// If we have a stored manifest for the add-on, it came from the
// startup data cache, and supersedes any previous XPIStates entry.
let xpiState = (!findManifest(addon._installLocation, id) &&
let xpiState = (!findManifest(addon.location, id) &&
addonStates.get(addon));
this.applyStartupChange(addon, previousVisible.get(id), xpiState);
@ -2851,14 +2832,16 @@ this.XPIDatabaseReconcile = {
}
for (let [id, addon] of previousVisible) {
if (addonExists(addon)) {
XPIInternal.BootstrapScope.get(addon).uninstall();
if (addon.location) {
if (addonExists(addon)) {
XPIInternal.BootstrapScope.get(addon).uninstall();
}
addon.location.removeAddon(id);
addon.visible = false;
addon.active = false;
}
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED, id);
XPIStates.removeAddon(addon.location, id);
addon.visible = false;
addon.active = false;
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED, id);
}
if (previousVisible.size) {
XPIInstall.flushChromeCaches();
@ -2918,7 +2901,7 @@ this.XPIDatabaseReconcile = {
if (previousAddon !== currentAddon) {
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED, id);
if (previousAddon._installLocation &&
if (previousAddon.location &&
previousAddon._sourceBundle.exists() &&
!previousAddon._sourceBundle.equals(currentAddon._sourceBundle)) {
XPIInternal.BootstrapScope.get(previousAddon).update(

Просмотреть файл

@ -88,12 +88,11 @@ const PREF_XPI_DIRECT_WHITELISTED = "xpinstall.whitelist.directRequest";
const PREF_XPI_FILE_WHITELISTED = "xpinstall.whitelist.fileRequest";
const PREF_XPI_WHITELIST_REQUIRED = "xpinstall.whitelist.required";
/* globals BOOTSTRAP_REASONS, KEY_APP_SYSTEM_ADDONS, KEY_APP_SYSTEM_DEFAULTS, KEY_APP_TEMPORARY, PREF_BRANCH_INSTALLED_ADDON, PREF_SYSTEM_ADDON_SET, TEMPORARY_ADDON_SUFFIX, SIGNED_TYPES, TOOLKIT_ID, XPI_PERMISSION, XPIStates, getExternalType, isTheme, isWebExtension */
/* globals BOOTSTRAP_REASONS, KEY_APP_SYSTEM_ADDONS, KEY_APP_SYSTEM_DEFAULTS, PREF_BRANCH_INSTALLED_ADDON, PREF_SYSTEM_ADDON_SET, TEMPORARY_ADDON_SUFFIX, SIGNED_TYPES, TOOLKIT_ID, XPI_PERMISSION, XPIStates, getExternalType, isTheme, isWebExtension, iterDirectory */
const XPI_INTERNAL_SYMBOLS = [
"BOOTSTRAP_REASONS",
"KEY_APP_SYSTEM_ADDONS",
"KEY_APP_SYSTEM_DEFAULTS",
"KEY_APP_TEMPORARY",
"PREF_BRANCH_INSTALLED_ADDON",
"PREF_SYSTEM_ADDON_SET",
"SIGNED_TYPES",
@ -104,6 +103,7 @@ const XPI_INTERNAL_SYMBOLS = [
"getExternalType",
"isTheme",
"isWebExtension",
"iterDirectory",
];
for (let name of XPI_INTERNAL_SYMBOLS) {
@ -780,7 +780,7 @@ function generateTemporaryInstallID(aFile) {
return id;
}
var loadManifest = async function(aPackage, aInstallLocation, aOldAddon) {
var loadManifest = async function(aPackage, aLocation, aOldAddon) {
async function loadFromRDF(aUri) {
let manifest = await aPackage.readString("install.rdf");
let addon = await loadManifestFromRDF(aUri, manifest, aPackage);
@ -809,7 +809,7 @@ var loadManifest = async function(aPackage, aInstallLocation, aOldAddon) {
await loadFromRDF(aPackage.getURI("install.rdf"));
addon._sourceBundle = aPackage.file;
addon._installLocation = aInstallLocation;
addon.location = aLocation;
let {signedState, cert} = await aPackage.verifySignedState(addon);
addon.signedState = signedState;
@ -821,7 +821,7 @@ var loadManifest = async function(aPackage, aInstallLocation, aOldAddon) {
throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
}
}
if (!addon.id && aInstallLocation.name == KEY_APP_TEMPORARY) {
if (!addon.id && aLocation.isTemporary) {
addon.id = generateTemporaryInstallID(aPackage.file);
}
}
@ -839,7 +839,7 @@ var loadManifest = async function(aPackage, aInstallLocation, aOldAddon) {
*
* @param {nsIFile} aFile
* The file to load the manifest from.
* @param {InstallLocation} aInstallLocation
* @param {XPIStateLocation} aLocation
* The install location the add-on is installed in, or will be
* installed to.
* @param {AddonInternal?} aOldAddon
@ -849,10 +849,10 @@ var loadManifest = async function(aPackage, aInstallLocation, aOldAddon) {
* @returns {AddonInternal}
* The parsed Addon object for the file's manifest.
*/
var loadManifestFromFile = async function(aFile, aInstallLocation, aOldAddon) {
var loadManifestFromFile = async function(aFile, aLocation, aOldAddon) {
let pkg = Package.get(aFile);
try {
let addon = await loadManifest(pkg, aInstallLocation, aOldAddon);
let addon = await loadManifest(pkg, aLocation, aOldAddon);
return addon;
} finally {
pkg.close();
@ -863,8 +863,8 @@ var loadManifestFromFile = async function(aFile, aInstallLocation, aOldAddon) {
* A synchronous method for loading an add-on's manifest. Do not use
* this.
*/
function syncLoadManifestFromFile(aFile, aInstallLocation, aOldAddon) {
return XPIInternal.awaitPromise(loadManifestFromFile(aFile, aInstallLocation, aOldAddon));
function syncLoadManifestFromFile(aFile, aLocation, aOldAddon) {
return XPIInternal.awaitPromise(loadManifestFromFile(aFile, aLocation, aOldAddon));
}
function flushChromeCaches() {
@ -953,11 +953,11 @@ function getSignedStatus(aRv, aCert, aAddonID) {
function shouldVerifySignedState(aAddon) {
// Updated system add-ons should always have their signature checked
if (aAddon._installLocation.name == KEY_APP_SYSTEM_ADDONS)
if (aAddon.location.name == KEY_APP_SYSTEM_ADDONS)
return true;
// We don't care about signatures for default system add-ons
if (aAddon._installLocation.name == KEY_APP_SYSTEM_DEFAULTS)
if (aAddon.location.name == KEY_APP_SYSTEM_DEFAULTS)
return false;
// Otherwise only check signatures if signing is enabled and the add-on is one
@ -1096,7 +1096,7 @@ function recursiveRemove(aFile) {
// iterating over a directory while removing files from it (the YAFFS2
// embedded filesystem has this issue, see bug 772238), and to remove
// normal files before their resource forks on OSX (see bug 733436).
let entries = getDirectoryEntries(aFile, true);
let entries = Array.from(iterDirectory(aFile));
entries.forEach(recursiveRemove);
try {
@ -1272,44 +1272,6 @@ SafeInstallOperation.prototype = {
}
};
/**
* Gets a snapshot of directory entries.
*
* @param {nsIFile} aDir
* Directory to look at
* @param {boolean} aSortEntries
* True to sort entries by filename
* @returns {nsIFile[]}
* An files in the directory, or an empty array if aDir is not a
* readable directory.
*/
function getDirectoryEntries(aDir, aSortEntries) {
let dirEnum;
try {
dirEnum = aDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
let entries = [];
while (dirEnum.hasMoreElements())
entries.push(dirEnum.nextFile);
if (aSortEntries) {
entries.sort(function(a, b) {
return a.path > b.path ? -1 : 1;
});
}
return entries;
} catch (e) {
if (aDir.exists()) {
logger.warn("Can't iterate directory " + aDir.path, e);
}
return [];
} finally {
if (dirEnum) {
dirEnum.close();
}
}
}
function getHashStringForCrypto(aCrypto) {
// return the two-digit hexadecimal code for a byte
let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
@ -1328,7 +1290,7 @@ class AddonInstall {
/**
* Instantiates an AddonInstall.
*
* @param {InstallLocation} installLocation
* @param {XPIStateLocation} installLocation
* The install location the add-on will be installed into
* @param {nsIURL} url
* The nsIURL to get the add-on from. If this is an nsIFileURL then
@ -1352,7 +1314,7 @@ class AddonInstall {
*/
constructor(installLocation, url, options = {}) {
this.wrapper = new AddonInstallWrapper(this);
this.installLocation = installLocation;
this.location = installLocation;
this.sourceURI = url;
if (options.hash) {
@ -1464,9 +1426,9 @@ class AddonInstall {
break;
case AddonManager.STATE_INSTALLED:
logger.debug("Cancelling install of " + this.addon.id);
let xpi = getFile(`${this.addon.id}.xpi`, this.installLocation.getStagingDir());
let xpi = getFile(`${this.addon.id}.xpi`, this.location.installer.getStagingDir());
flushJarCache(xpi);
this.installLocation.cleanStagingDir([this.addon.id, this.addon.id + ".xpi"]);
this.location.installer.cleanStagingDir([this.addon.id, this.addon.id + ".xpi"]);
this.state = AddonManager.STATE_CANCELLED;
XPIInstall.installs.delete(this);
@ -1486,7 +1448,7 @@ class AddonInstall {
this._callInstallListeners("onInstallCancelled");
this.removeTemporaryFile();
let stagingDir = this.installLocation.getStagingDir();
let stagingDir = this.location.installer.getStagingDir();
let stagedAddon = stagingDir.clone();
this.unstageInstall(stagedAddon);
@ -1526,19 +1488,19 @@ class AddonInstall {
removeTemporaryFile() {
// Only proceed if this AddonInstall owns its XPI file
if (!this.ownsTempFile) {
this.logger.debug("removeTemporaryFile: " + this.sourceURI.spec + " does not own temp file");
this.logger.debug(`removeTemporaryFile: ${this.sourceURI.spec} does not own temp file`);
return;
}
try {
this.logger.debug("removeTemporaryFile: " + this.sourceURI.spec + " removing temp file " +
this.file.path);
this.logger.debug(`removeTemporaryFile: ${this.sourceURI.spec} removing temp file ` +
this.file.path);
this.file.remove(true);
this.ownsTempFile = false;
} catch (e) {
this.logger.warn("Failed to remove temporary file " + this.file.path + " for addon " +
this.sourceURI.spec,
e);
this.logger.warn(`Failed to remove temporary file ${this.file.path} for addon ` +
this.sourceURI.spec,
e);
}
}
@ -1570,7 +1532,7 @@ class AddonInstall {
try {
try {
this.addon = await loadManifest(pkg, this.installLocation, this.existingAddon);
this.addon = await loadManifest(pkg, this.location, this.existingAddon);
} catch (e) {
return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
}
@ -1727,7 +1689,7 @@ class AddonInstall {
// install location
for (let install of XPIInstall.installs) {
if (install.state == AddonManager.STATE_INSTALLED &&
install.installLocation == this.installLocation &&
install.location == this.location &&
install.addon.id == this.addon.id) {
logger.debug(`Cancelling previous pending install of ${install.addon.id}`);
install.cancel();
@ -1735,17 +1697,17 @@ class AddonInstall {
}
let isUpgrade = this.existingAddon &&
this.existingAddon._installLocation == this.installLocation;
this.existingAddon.location == this.location;
logger.debug("Starting install of " + this.addon.id + " from " + this.sourceURI.spec);
AddonManagerPrivate.callAddonListeners("onInstalling",
this.addon.wrapper,
false);
let stagedAddon = this.installLocation.getStagingDir();
let stagedAddon = this.location.installer.getStagingDir();
(async () => {
await this.installLocation.requestStagingDir();
await this.location.installer.requestStagingDir();
// remove any previously staged files
await this.unstageInstall(stagedAddon);
@ -1764,7 +1726,7 @@ class AddonInstall {
// Install the new add-on into its final location
let existingAddonID = this.existingAddon ? this.existingAddon.id : null;
let file = this.installLocation.installAddon({
let file = this.location.installer.installAddon({
id: this.addon.id,
source: stagedAddon,
existingAddonID
@ -1777,7 +1739,7 @@ class AddonInstall {
if (isUpgrade) {
this.addon = XPIDatabase.updateAddonMetadata(this.existingAddon, this.addon,
file.path);
let state = XPIStates.getAddon(this.installLocation.name, this.addon.id);
let state = this.location.get(this.addon.id);
if (state) {
state.syncWithDB(this.addon, true);
} else {
@ -1826,7 +1788,7 @@ class AddonInstall {
this._callInstallListeners("onInstallFailed");
}).then(() => {
this.removeTemporaryFile();
return this.installLocation.releaseStagingDir();
return this.location.installer.releaseStagingDir();
});
}
@ -1853,8 +1815,7 @@ class AddonInstall {
this.addon._sourceBundle = stagedAddon;
// Cache the AddonInternal as it may have updated compatibility info
XPIStates.getLocation(this.installLocation.name).stageAddon(this.addon.id,
this.addon.toJSON());
this.location.stageAddon(this.addon.id, this.addon.toJSON());
logger.debug(`Staged install of ${this.addon.id} from ${this.sourceURI.spec} ready; waiting for restart.`);
if (isUpgrade) {
@ -1871,7 +1832,7 @@ class AddonInstall {
* The staging directory from which to unstage the install.
*/
async unstageInstall(stagingDir) {
XPIStates.getLocation(this.installLocation.name).unstageAddon(this.addon.id);
this.location.unstageAddon(this.addon.id);
await removeAsync(getFile(this.addon.id, stagingDir));
@ -1887,9 +1848,9 @@ class AddonInstall {
async postpone(resumeFn) {
this.state = AddonManager.STATE_POSTPONED;
let stagingDir = this.installLocation.getStagingDir();
let stagingDir = this.location.installer.getStagingDir();
await this.installLocation.requestStagingDir();
await this.location.installer.requestStagingDir();
await this.unstageInstall(stagingDir);
let stagedAddon = getFile(`${this.addon.id}.xpi`, stagingDir);
@ -1921,7 +1882,7 @@ class AddonInstall {
// Release the staging directory lock, but since the staging dir is populated
// it will not be removed until resumed or installed by restart.
// See also cleanStagingDir()
this.installLocation.releaseStagingDir();
this.location.installer.releaseStagingDir();
}
_callInstallListeners(event, ...args) {
@ -2042,8 +2003,8 @@ var DownloadAddonInstall = class extends AddonInstall {
/**
* Instantiates a DownloadAddonInstall
*
* @param {InstallLocation} installLocation
* The InstallLocation the add-on will be installed into
* @param {XPIStateLocation} installLocation
* The XPIStateLocation the add-on will be installed into
* @param {nsIURL} url
* The nsIURL to get the add-on from
* @param {Object} [options = {}]
@ -2452,10 +2413,10 @@ function createUpdate(aCallback, aAddon, aUpdate) {
};
let install;
if (url instanceof Ci.nsIFileURL) {
install = new LocalAddonInstall(aAddon._installLocation, url, opts);
install = new LocalAddonInstall(aAddon.location, url, opts);
await install.init();
} else {
install = new DownloadAddonInstall(aAddon._installLocation, url, opts);
install = new DownloadAddonInstall(aAddon.location, url, opts);
}
try {
if (aUpdate.updateInfoURL)
@ -2688,7 +2649,7 @@ UpdateChecker.prototype = {
ignoreMaxVersion, ignoreStrictCompat, compatOverrides);
if (update && Services.vc.compare(this.addon.version, update.version) < 0
&& !this.addon._installLocation.locked) {
&& !this.addon.location.locked) {
for (let currentInstall of XPIInstall.installs) {
// Skip installs that don't match the available update
if (currentInstall.existingAddon != this.addon ||
@ -2746,14 +2707,14 @@ UpdateChecker.prototype = {
*
* @param {nsIFile} file
* The file to install
* @param {InstallLocation} location
* @param {XPIStateLocation} location
* The location to install to
* @returns {Promise<AddonInstall>}
* A Promise that resolves with the new install object.
*/
function createLocalInstall(file, location) {
if (!location) {
location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
location = XPIStates.getLocation(KEY_APP_PROFILE);
}
let url = Services.io.newFileURI(file);
@ -2767,13 +2728,26 @@ function createLocalInstall(file, location) {
}
}
// These are partial classes which contain the install logic for the
// homonymous classes in XPIProvider.jsm. Those classes forward calls to
// their install methods to these classes, with the `this` value set to
// an instance the class as defined in XPIProvider.
class DirectoryInstallLocation {}
class DirectoryInstaller {
constructor(location) {
this.location = location;
this._stagingDirLock = 0;
this._stagingDirPromise = null;
}
get name() {
return this.location.name;
}
get dir() {
return this.location.dir;
}
set dir(val) {
this.location.dir = val;
this.location.path = val.path;
}
class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
/**
* Gets the staging directory to put add-ons that are pending install and
* uninstall into.
@ -2781,7 +2755,7 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
* @returns {nsIFile}
*/
getStagingDir() {
return getFile(DIR_STAGE, this._directory);
return getFile(DIR_STAGE, this.dir);
}
requestStagingDir() {
@ -2790,8 +2764,8 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
if (this._stagingDirPromise)
return this._stagingDirPromise;
OS.File.makeDir(this._directory.path);
let stagepath = OS.Path.join(this._directory.path, DIR_STAGE);
OS.File.makeDir(this.dir.path);
let stagepath = OS.Path.join(this.dir.path, DIR_STAGE);
return this._stagingDirPromise = OS.File.makeDir(stagepath).catch((e) => {
if (e instanceof OS.File.Error && e.becauseExists)
return;
@ -2830,13 +2804,9 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
if (this._stagingDirLock > 0)
return;
let dirEntries = dir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
try {
if (dirEntries.nextFile)
return;
} finally {
dirEntries.close();
}
// eslint-disable-next-line no-unused-vars
for (let file of iterDirectory(dir))
return;
try {
setFilePermissions(dir, FileUtils.PERMS_DIRECTORY);
@ -2856,7 +2826,7 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
* @returns {nsIFile}
*/
getTrashDir() {
let trashDir = getFile(DIR_TRASH, this._directory);
let trashDir = getFile(DIR_TRASH, this.dir);
let trashDirExists = trashDir.exists();
try {
if (trashDirExists)
@ -2900,11 +2870,11 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
let transaction = new SafeInstallOperation();
let moveOldAddon = aId => {
let file = getFile(aId, this._directory);
let file = getFile(aId, this.dir);
if (file.exists())
transaction.moveUnder(file, trashDir);
file = getFile(`${aId}.xpi`, this._directory);
file = getFile(`${aId}.xpi`, this.dir);
if (file.exists()) {
flushJarCache(file);
transaction.moveUnder(file, trashDir);
@ -2943,10 +2913,10 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
}
if (action == "copy") {
transaction.copy(source, this._directory);
transaction.copy(source, this.dir);
} else if (action == "move") {
flushJarCache(source);
transaction.moveUnder(source, this._directory);
transaction.moveUnder(source, this.dir);
}
// Do nothing for the proxy file as we sideload an addon permanently
} finally {
@ -2955,11 +2925,11 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
try {
recursiveRemove(trashDir);
} catch (e) {
logger.warn("Failed to remove trash directory when installing " + id, e);
logger.warn(`Failed to remove trash directory when installing ${id}`, e);
}
}
let newFile = this._directory.clone();
let newFile = this.dir.clone();
if (action == "proxy") {
// When permanently installing sideloaded addon, we just put a proxy file
@ -2974,13 +2944,7 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
try {
newFile.lastModifiedTime = Date.now();
} catch (e) {
logger.warn("failed to set lastModifiedTime on " + newFile.path, e);
}
this._IDToFileMap[id] = newFile;
if (existingAddonID && existingAddonID != id &&
existingAddonID in this._IDToFileMap) {
delete this._IDToFileMap[existingAddonID];
logger.warn(`failed to set lastModifiedTime on ${newFile.path}`, e);
}
return newFile;
@ -2994,29 +2958,20 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
* @throws if the ID does not match any of the add-ons installed
*/
uninstallAddon(aId) {
let file = this._IDToFileMap[aId];
if (!file) {
logger.warn("Attempted to remove " + aId + " from " +
this._name + " but it was already gone");
return;
}
file = getFile(aId, this._directory);
let file = getFile(aId, this.dir);
if (!file.exists())
file.leafName += ".xpi";
if (!file.exists()) {
logger.warn("Attempted to remove " + aId + " from " +
this._name + " but it was already gone");
delete this._IDToFileMap[aId];
logger.warn(`Attempted to remove ${aId} from ${this.name} but it was already gone`);
this.location.delete(aId);
return;
}
let trashDir = this.getTrashDir();
if (file.leafName != aId) {
logger.debug("uninstallAddon: flushing jar cache " + file.path + " for addon " + aId);
logger.debug(`uninstallAddon: flushing jar cache ${file.path} for addon ${aId}`);
flushJarCache(file);
}
@ -3030,17 +2985,29 @@ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
try {
recursiveRemove(trashDir);
} catch (e) {
logger.warn("Failed to remove trash directory when uninstalling " + aId, e);
logger.warn(`Failed to remove trash directory when uninstalling ${aId}`, e);
}
}
XPIStates.removeAddon(this.name, aId);
delete this._IDToFileMap[aId];
this.location.removeAddon(aId);
}
}
class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
class SystemAddonInstaller extends DirectoryInstaller {
constructor(location) {
super(location);
this._baseDir = location._baseDir;
this._nextDir = null;
}
get _addonSet() {
return this.location._addonSet;
}
set _addonSet(val) {
this.location._addonSet = val;
}
/**
* Saves the current set of system add-ons
*
@ -3052,7 +3019,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
}
static _loadAddonSet() {
return XPIInternal.SystemAddonInstallLocation._loadAddonSet();
return XPIInternal.SystemAddonLocation._loadAddonSet();
}
/**
@ -3063,22 +3030,22 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
* Staging directory for system add-on upgrades.
*/
getStagingDir() {
this._addonSet = SystemAddonInstallLocation._loadAddonSet();
this._addonSet = SystemAddonInstaller._loadAddonSet();
let dir = null;
if (this._addonSet.directory) {
this._directory = getFile(this._addonSet.directory, this._baseDir);
dir = getFile(DIR_STAGE, this._directory);
this.dir = getFile(this._addonSet.directory, this._baseDir);
dir = getFile(DIR_STAGE, this.dir);
} else {
logger.info("SystemAddonInstallLocation directory is missing");
logger.info("SystemAddonInstaller directory is missing");
}
return dir;
}
requestStagingDir() {
this._addonSet = SystemAddonInstallLocation._loadAddonSet();
this._addonSet = SystemAddonInstaller._loadAddonSet();
if (this._addonSet.directory) {
this._directory = getFile(this._addonSet.directory, this._baseDir);
this.dir = getFile(this._addonSet.directory, this._baseDir);
}
return super.requestStagingDir();
}
@ -3130,7 +3097,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
// fails then at least they will not be re-activated on
// next restart.
this._addonSet = { schema: 1, addons: {} };
SystemAddonInstallLocation._saveAddonSet(this._addonSet);
SystemAddonInstaller._saveAddonSet(this._addonSet);
// If this is running at app startup, the pref being cleared
// will cause later stages of startup to notice that the
@ -3176,7 +3143,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
}
// Skip the directory currently in use
if (this._directory && this._directory.path == entry.path) {
if (this.dir && this.dir.path == entry.path) {
continue;
}
@ -3214,7 +3181,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
// Make sure the base dir exists
await OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
let addonSet = SystemAddonInstallLocation._loadAddonSet();
let addonSet = SystemAddonInstaller._loadAddonSet();
// Remove any add-ons that are no longer part of the set.
for (let addonID of Object.keys(addonSet.addons)) {
@ -3238,14 +3205,13 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
// Record the new upgrade directory.
let state = { schema: 1, directory: newDir.leafName, addons: {} };
SystemAddonInstallLocation._saveAddonSet(state);
SystemAddonInstaller._saveAddonSet(state);
this._nextDir = newDir;
let location = this;
let installs = [];
for (let addon of aAddons) {
let install = await createLocalInstall(addon._sourceBundle, location);
let install = await createLocalInstall(addon._sourceBundle, this.location);
installs.push(install);
}
@ -3261,7 +3227,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
logger.info(`system add-on ${install.addon.id} has an upgrade listener, postponing upgrade set until restart`);
resumeFn = () => {
logger.info(`${install.addon.id} has resumed a previously postponed addon set`);
install.installLocation.resumeAddonSet(installs);
install.location.installer.resumeAddonSet(installs);
};
}
await install.postpone(resumeFn);
@ -3278,8 +3244,8 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
};
}
previousState = SystemAddonInstallLocation._loadAddonSet();
SystemAddonInstallLocation._saveAddonSet(state);
previousState = SystemAddonInstaller._loadAddonSet();
SystemAddonInstaller._saveAddonSet(state);
let blockers = aAddons.filter(
addon => AddonManagerPrivate.hasUpgradeListener(addon.id)
@ -3293,7 +3259,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
} catch (e) {
// Roll back to previous upgrade set (if present) on restart.
if (previousState) {
SystemAddonInstallLocation._saveAddonSet(previousState);
SystemAddonInstaller._saveAddonSet(previousState);
}
// Otherwise, roll back to built-in set on restart.
// TODO try to do these restartlessly
@ -3317,7 +3283,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
async resumeAddonSet(installs) {
async function resumeAddon(install) {
install.state = AddonManager.STATE_DOWNLOADED;
install.installLocation.releaseStagingDir();
install.location.installer.releaseStagingDir();
install.install();
}
@ -3341,7 +3307,7 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
* @returns {nsIFile}
*/
getTrashDir() {
let trashDir = getFile(DIR_TRASH, this._directory);
let trashDir = getFile(DIR_TRASH, this.dir);
let trashDirExists = trashDir.exists();
try {
if (trashDirExists)
@ -3375,25 +3341,24 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
try {
flushJarCache(source);
transaction.moveUnder(source, this._directory);
transaction.moveUnder(source, this.dir);
} finally {
// It isn't ideal if this cleanup fails but it isn't worth rolling back
// the install because of it.
try {
recursiveRemove(trashDir);
} catch (e) {
logger.warn("Failed to remove trash directory when installing " + id, e);
logger.warn(`Failed to remove trash directory when installing ${id}`, e);
}
}
let newFile = getFile(source.leafName, this._directory);
let newFile = getFile(source.leafName, this.dir);
try {
newFile.lastModifiedTime = Date.now();
} catch (e) {
logger.warn("failed to set lastModifiedTime on " + newFile.path, e);
}
this._IDToFileMap[id] = newFile;
return newFile;
}
@ -3418,7 +3383,7 @@ var XPIInstall = {
* The expected ID of the add-on.
* @param {nsIFile} file
* The XPI file to install the add-on from.
* @param {InstallLocation} location
* @param {XPIStateLocation} location
* The install location to install the add-on to.
* @returns {AddonInternal}
* The installed Addon object, upon success.
@ -3430,29 +3395,25 @@ var XPIInstall = {
throw new Error(`File file ${file.path} contains an add-on with an incorrect ID`);
}
let existingEntry = null;
try {
existingEntry = location.getLocationForID(id);
} catch (e) {
}
let state = location.get(id);
if (existingEntry) {
if (state) {
try {
let existingAddon = await loadManifestFromFile(existingEntry, location);
let existingAddon = await loadManifestFromFile(state.file, location);
if (Services.vc.compare(addon.version, existingAddon.version) <= 0)
return null;
} catch (e) {
// Bad add-on in the profile so just proceed and install over the top
logger.warn("Profile contains an add-on with a bad or missing install " +
`manifest at ${existingEntry.path}, overwriting`, e);
`manifest at ${state.path}, overwriting`, e);
}
} else if (Services.prefs.getBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, false)) {
return null;
}
// Install the add-on
addon._sourceBundle = location.installAddon({ id, source: file, action: "copy" });
addon._sourceBundle = location.installer.installAddon({ id, source: file, action: "copy" });
if (Services.prefs.getBoolPref(PREF_DISTRO_ADDONS_PERMS, false)) {
addon.userDisabled = true;
if (!XPIProvider.newDistroAddons) {
@ -3462,7 +3423,7 @@ var XPIInstall = {
}
XPIStates.addAddon(addon);
logger.debug("Installed distribution add-on " + id);
logger.debug(`Installed distribution add-on ${id}`);
Services.prefs.setBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, true);
@ -3477,13 +3438,13 @@ var XPIInstall = {
* The expected ID of the add-on.
* @param {object} metadata
* The parsed metadata for the staged install.
* @param {InstallLocation} location
* @param {XPIStateLocation} location
* The install location to install the add-on to.
* @returns {AddonInternal}
* The installed Addon object, upon success.
*/
async installStagedAddon(id, metadata, location) {
let source = getFile(`${id}.xpi`, location.getStagingDir());
let source = getFile(`${id}.xpi`, location.installer.getStagingDir());
// Check that the directory's name is a valid ID.
if (!gIDTest.test(id) || !source.exists() || !source.isFile()) {
@ -3516,7 +3477,7 @@ var XPIInstall = {
}
try {
addon._sourceBundle = location.installAddon({
addon._sourceBundle = location.installer.installAddon({
id, source, existingAddonID: id,
});
XPIStates.addAddon(addon);
@ -3532,10 +3493,12 @@ var XPIInstall = {
},
async updateSystemAddons() {
let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
let systemAddonLocation = XPIStates.getLocation(KEY_APP_SYSTEM_ADDONS);
if (!systemAddonLocation)
return;
let installer = systemAddonLocation.installer;
// Don't do anything in safe mode
if (Services.appinfo.inSafeMode)
return;
@ -3543,7 +3506,7 @@ var XPIInstall = {
// Download the list of system add-ons
let url = Services.prefs.getStringPref(PREF_SYSTEM_ADDON_UPDATE_URL, null);
if (!url) {
await systemAddonLocation.cleanDirectories();
await installer.cleanDirectories();
return;
}
@ -3555,7 +3518,7 @@ var XPIInstall = {
// If there was no list then do nothing.
if (!res || !res.gmpAddons) {
logger.info("No system add-ons list was returned.");
await systemAddonLocation.cleanDirectories();
await installer.cleanDirectories();
return;
}
@ -3582,7 +3545,7 @@ var XPIInstall = {
let updatedAddons = addonMap(await XPIDatabase.getAddonsInLocation(KEY_APP_SYSTEM_ADDONS));
if (setMatches(addonList, updatedAddons)) {
logger.info("Retaining existing updated system add-ons.");
await systemAddonLocation.cleanDirectories();
await installer.cleanDirectories();
return;
}
@ -3591,8 +3554,8 @@ var XPIInstall = {
let defaultAddons = addonMap(await XPIDatabase.getAddonsInLocation(KEY_APP_SYSTEM_DEFAULTS));
if (setMatches(addonList, defaultAddons)) {
logger.info("Resetting system add-ons.");
systemAddonLocation.resetAddonSet();
await systemAddonLocation.cleanDirectories();
installer.resetAddonSet();
await installer.cleanDirectories();
return;
}
@ -3641,7 +3604,7 @@ var XPIInstall = {
return false;
}
if (!systemAddonLocation.isValidAddon(item.addon))
if (!installer.isValidAddon(item.addon))
return false;
return true;
@ -3654,7 +3617,7 @@ var XPIInstall = {
// Install into the install location
logger.info("Installing new system add-on set");
await systemAddonLocation.installAddonSet(Array.from(addonList.values())
await installer.installAddonSet(Array.from(addonList.values())
.map(a => a.addon));
},
@ -3752,7 +3715,7 @@ var XPIInstall = {
* @returns {AddonInstall}
*/
async getInstallForURL(aUrl, aHash, aName, aIcons, aVersion, aBrowser) {
let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
let location = XPIStates.getLocation(KEY_APP_PROFILE);
let url = Services.io.newURI(aUrl);
let options = {
@ -3825,7 +3788,7 @@ var XPIInstall = {
}
let addon = await loadManifestFromFile(aFile, installLocation);
installLocation.installAddon({ id: addon.id, source: aFile });
installLocation.installer.installAddon({ id: addon.id, source: aFile });
if (addon.appDisabled) {
let message = `Add-on ${addon.id} is not compatible with application version.`;
@ -3909,11 +3872,11 @@ var XPIInstall = {
*/
async uninstallAddon(aAddon, aForcePending) {
if (!(aAddon.inDatabase))
throw new Error("Cannot uninstall addon " + aAddon.id + " because it is not installed");
throw new Error(`Cannot uninstall addon ${aAddon.id} because it is not installed`);
if (aAddon._installLocation.locked)
throw new Error("Cannot uninstall addon " + aAddon.id
+ " from locked install location " + aAddon._installLocation.name);
if (aAddon.location.locked)
throw new Error(`Cannot uninstall addon ${aAddon.id} ` +
`from locked install location ${aAddon.location.name}`);
if (aForcePending && aAddon.pendingUninstall)
throw new Error("Add-on is already marked to be uninstalled");
@ -3921,7 +3884,7 @@ var XPIInstall = {
aAddon._hasResourceCache.clear();
if (aAddon._updateCheck) {
logger.debug("Cancel in-progress update check for " + aAddon.id);
logger.debug(`Cancel in-progress update check for ${aAddon.id}`);
aAddon._updateCheck.cancel();
}
@ -3932,8 +3895,8 @@ var XPIInstall = {
// that an uninstall is necessary on next startup. Temporary add-ons are
// automatically uninstalled on shutdown anyway so there is no need to
// do this for them.
if (aAddon._installLocation.name != KEY_APP_TEMPORARY) {
let stage = getFile(aAddon.id, aAddon._installLocation.getStagingDir());
if (!aAddon.location.isTemporary) {
let stage = getFile(aAddon.id, aAddon.location.installer.getStagingDir());
if (!stage.exists())
stage.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
}
@ -3942,7 +3905,7 @@ var XPIInstall = {
pendingUninstall: true
});
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
let xpiState = XPIStates.getAddon(aAddon.location, aAddon.id);
let xpiState = aAddon.location.get(aAddon.id);
if (xpiState) {
xpiState.enabled = false;
XPIStates.save();
@ -3963,8 +3926,8 @@ var XPIInstall = {
!!aForcePending);
}
let existingAddon = XPIStates.findAddon(aAddon.id, loc =>
loc.name != aAddon._installLocation.name);
let existingAddon = XPIStates.findAddon(aAddon.id,
loc => loc != aAddon.location);
let bootstrap = XPIInternal.BootstrapScope.get(aAddon);
if (!aForcePending) {
@ -3976,9 +3939,9 @@ var XPIInstall = {
let uninstall = () => {
XPIStates.disableAddon(aAddon.id);
aAddon._installLocation.uninstallAddon(aAddon.id);
aAddon.location.installer.uninstallAddon(aAddon.id);
XPIDatabase.removeAddonMetadata(aAddon);
XPIStates.removeAddon(aAddon.location, aAddon.id);
aAddon.location.removeAddon(aAddon.id);
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
if (existing) {
@ -3996,7 +3959,7 @@ var XPIInstall = {
AddonManagerPrivate.callAddonListeners("onInstalled", existing.wrapper);
} else {
XPIStates.removeAddon(aAddon.location, aAddon.id);
aAddon.location.removeAddon(aAddon.id);
bootstrap.uninstall();
uninstall();
}
@ -4023,8 +3986,8 @@ var XPIInstall = {
if (!aAddon.pendingUninstall)
throw new Error("Add-on is not marked to be uninstalled");
if (aAddon._installLocation.name != KEY_APP_TEMPORARY)
aAddon._installLocation.cleanStagingDir([aAddon.id]);
if (!aAddon.location.isTemporary)
aAddon.location.installer.cleanStagingDir([aAddon.id]);
XPIDatabase.setAddonProperties(aAddon, {
pendingUninstall: false
@ -4033,7 +3996,7 @@ var XPIInstall = {
if (!aAddon.visible)
return;
XPIStates.getAddon(aAddon.location, aAddon.id).syncWithDB(aAddon);
aAddon.location.get(aAddon.id).syncWithDB(aAddon);
XPIStates.save();
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
@ -4052,6 +4015,6 @@ var XPIInstall = {
AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type, false);
},
MutableDirectoryInstallLocation,
SystemAddonInstallLocation,
DirectoryInstaller,
SystemAddonInstaller,
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -18,7 +18,7 @@ async function test() {
let allCompatible = true;
for (let a of aAddons) {
// Ignore plugins.
if (a.type == "plugin")
if (a.type == "plugin" || a.id == "workerbootstrap-test@mozilla.org")
continue;
ok(a.isCompatible, a.type + " " + a.name + " " + a.version + " should be compatible");

Просмотреть файл

@ -13,8 +13,6 @@ Cu.importGlobalProperties(["TextEncoder"]);
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_COMPAT_OVERRIDES = "extensions.getAddons.compatOverides.url";
const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
@ -1127,10 +1125,6 @@ function promiseInstallWebExtension(aData) {
// 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);

Просмотреть файл

@ -65,7 +65,7 @@ function checkChange(XS, aPath, aChange) {
lastTimestamp += 10000;
info("Touching file " + aPath.path + " with " + lastTimestamp);
aPath.lastModifiedTime = lastTimestamp;
Assert.equal(XS.getInstallState(), aChange);
Assert.equal(XS.scanForChanges(), aChange);
// Save the pref so we don't detect this change again
XS.save();
}
@ -95,7 +95,7 @@ add_task(async function detect_touches() {
let XS = getXS();
// Should be no changes detected here, because everything should start out up-to-date.
Assert.ok(!XS.getInstallState());
Assert.ok(!XS.scanForChanges());
let states = XS.getLocation("app-profile");

Просмотреть файл

@ -8,8 +8,6 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
const EXPECTED_GLOBALS = [
["Worker", "function"],
["ChromeWorker", "function"],
["console", "object"]
];

Просмотреть файл

@ -16,7 +16,7 @@ if (AppConstants.platform == "win" && AppConstants.DEBUG) {
Services.prefs.setBoolPref("extensions.webextensions.remote", false);
}
PromiseTestUtils.expectUncaughtRejection(/Message manager disconnected/);
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
/* globals browser*/

Просмотреть файл

@ -25,7 +25,10 @@ const ADDONS = {
expected: {
strictCompatibility: false,
},
compatible: [true, true, true, true],
compatible: {
nonStrict: true,
strict: true,
},
},
// Incompatible in strict compatibility mode
@ -44,7 +47,10 @@ const ADDONS = {
expected: {
strictCompatibility: false,
},
compatible: [false, true, false, true],
compatible: {
nonStrict: true,
strict: false,
},
},
// Opt-in to strict compatibility - always incompatible
@ -64,7 +70,10 @@ const ADDONS = {
expected: {
strictCompatibility: true,
},
compatible: [false, false, false, false],
compatible: {
nonStrict: false,
strict: false,
},
},
// Addon from the future - would be marked as compatibile-by-default,
@ -84,7 +93,10 @@ const ADDONS = {
expected: {
strictCompatibility: false,
},
compatible: [false, false, false, false],
compatible: {
nonStrict: false,
strict: false,
},
},
// Extremely old addon - maxVersion is less than the minimum compat version
@ -104,7 +116,10 @@ const ADDONS = {
expected: {
strictCompatibility: false,
},
compatible: [false, true, false, false],
compatible: {
nonStrict: true,
strict: false,
},
},
// Dictionary - incompatible in strict compatibility mode
@ -123,7 +138,10 @@ const ADDONS = {
expected: {
strictCompatibility: false,
},
compatible: [false, true, false, true],
compatible: {
nonStrict: true,
strict: false,
},
},
};
@ -154,37 +172,23 @@ add_task(async function setup() {
await promiseWriteInstallRDFForExtension(addon["install.rdf"], profileDir);
}
Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.1");
await promiseStartupManager();
});
add_task(async function test_0() {
// Should default to enabling strict compat.
await checkCompatStatus(true, 0);
});
add_task(async function test_1() {
info("Test 1");
Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
await checkCompatStatus(false, 1);
await checkCompatStatus(false, "nonStrict");
await promiseRestartManager();
await checkCompatStatus(false, 1);
await checkCompatStatus(false, "nonStrict");
});
add_task(async function test_2() {
info("Test 2");
Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
await checkCompatStatus(true, 2);
await checkCompatStatus(true, "strict");
await promiseRestartManager();
await checkCompatStatus(true, 2);
});
add_task(async function test_3() {
info("Test 3");
Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.4");
await checkCompatStatus(false, 3);
await checkCompatStatus(true, "strict");
});
const CHECK_COMPAT_ADDONS = {

Просмотреть файл

@ -451,7 +451,7 @@ FileDescriptorFile::CreateUnique(uint32_t aType, uint32_t aAttributes)
}
NS_IMETHODIMP
FileDescriptorFile::GetDirectoryEntries(nsISimpleEnumerator** aEntries)
FileDescriptorFile::GetDirectoryEntriesImpl(nsIDirectoryEnumerator** aEntries)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

Просмотреть файл

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsISimpleEnumerator.idl"
interface nsIFile;
@ -14,7 +15,7 @@ interface nsIFile;
* enumeration is complete.
*/
[scriptable, uuid(31f7f4ae-6916-4f2d-a81e-926a4e3022ee)]
interface nsIDirectoryEnumerator : nsISupports
interface nsIDirectoryEnumerator : nsISimpleEnumerator
{
/**
* Retrieves the next file in the sequence. The "nextFile" element is the

Просмотреть файл

@ -4,12 +4,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIDirectoryEnumerator.idl"
%{C++
struct PRFileDesc;
struct PRLibrary;
#include <stdio.h>
#include "mozilla/Path.h"
#include "nsCOMPtr.h"
#include "nsStringFwd.h"
namespace mozilla {
using PathString = nsTString<filesystem::Path::value_type>;
@ -22,8 +24,6 @@ using PathSubstring = nsTSubstring<filesystem::Path::value_type>;
[ptr] native FILE(FILE);
native PathString(mozilla::PathString);
interface nsISimpleEnumerator;
/**
* An nsIFile is an abstract representation of a filename. It manages
* filename encoding issues, pathname component separators ('/' vs. '\\'
@ -346,7 +346,23 @@ interface nsIFile : nsISupports
* @throws NS_ERROR_FILE_NOT_DIRECTORY if the current nsIFile does
* not specify a directory.
*/
readonly attribute nsISimpleEnumerator directoryEntries;
[binaryname(DirectoryEntriesImpl)]
readonly attribute nsIDirectoryEnumerator directoryEntries;
%{C++
nsresult GetDirectoryEntries(nsISimpleEnumerator** aOut)
{
nsCOMPtr<nsIDirectoryEnumerator> dirEnum;
nsresult rv = GetDirectoryEntries(getter_AddRefs(dirEnum));
dirEnum.forget(aOut);
return rv;
};
nsresult GetDirectoryEntries(nsIDirectoryEnumerator** aOut)
{
return GetDirectoryEntriesImpl(aOut);
};
%}
/**
* initWith[Native]Path

Просмотреть файл

@ -88,8 +88,7 @@ using namespace mozilla;
/* directory enumerator */
class nsDirEnumeratorUnix final
: public nsISimpleEnumerator
, public nsIDirectoryEnumerator
: public nsIDirectoryEnumerator
{
public:
nsDirEnumeratorUnix();
@ -1850,7 +1849,7 @@ nsLocalFile::SetFollowLinks(bool aFollowLinks)
}
NS_IMETHODIMP
nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator** aEntries)
nsLocalFile::GetDirectoryEntriesImpl(nsIDirectoryEnumerator** aEntries)
{
RefPtr<nsDirEnumeratorUnix> dir = new nsDirEnumeratorUnix();

Просмотреть файл

@ -230,13 +230,35 @@ private:
nsString mResolvedPath;
};
class nsDriveEnumerator : public nsISimpleEnumerator
class nsDriveEnumerator : public nsIDirectoryEnumerator
{
public:
nsDriveEnumerator();
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
nsresult Init();
NS_IMETHOD GetNextFile(nsIFile** aResult) override
{
bool hasMore = false;
nsresult rv = HasMoreElements(&hasMore);
if (NS_FAILED(rv) || !hasMore) {
return rv;
}
nsCOMPtr<nsISupports> next;
rv = GetNext(getter_AddRefs(next));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> result = do_QueryInterface(next);
result.forget(aResult);
return NS_OK;
}
NS_IMETHOD Close() override
{
return NS_OK;
}
private:
virtual ~nsDriveEnumerator();
@ -658,8 +680,7 @@ CloseDir(nsDir*& aDir)
//-----------------------------------------------------------------------------
class nsDirEnumerator final
: public nsISimpleEnumerator
, public nsIDirectoryEnumerator
: public nsIDirectoryEnumerator
{
private:
~nsDirEnumerator()
@ -3074,7 +3095,7 @@ nsLocalFile::SetFollowLinks(bool aFollowLinks)
NS_IMETHODIMP
nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator** aEntries)
nsLocalFile::GetDirectoryEntriesImpl(nsIDirectoryEnumerator** aEntries)
{
nsresult rv;
@ -3511,7 +3532,7 @@ nsLocalFile::GetHashCode(uint32_t* aResult)
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsDriveEnumerator, nsISimpleEnumerator)
NS_IMPL_ISUPPORTS(nsDriveEnumerator, nsIDirectoryEnumerator, nsISimpleEnumerator)
nsDriveEnumerator::nsDriveEnumerator()
{