зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
8affe6e831
|
@ -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()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче