diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index c2083d00e3e3..094c7b1adaf9 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -69,6 +69,8 @@ pref("extensions.screenshots.system-disabled", true); // Disable add-ons that are not installed by the user in all scopes by default. // See the SCOPE constants in AddonManager.jsm for values to use here. pref("extensions.autoDisableScopes", 15); +// Scopes to scan for changes at startup. +pref("extensions.startupScanScopes", 0); // This is where the profiler WebExtension API will look for breakpad symbols. // NOTE: deliberately http right now since https://symbols.mozilla.org is not supported. diff --git a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm index 8852baa93bdd..a103a713b583 100644 --- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm +++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm @@ -243,6 +243,9 @@ var AddonTestUtils = { // By default don't disable add-ons from any scope Services.prefs.setIntPref("extensions.autoDisableScopes", 0); + // And scan for changes at startup + Services.prefs.setIntPref("extensions.startupScanScopes", 15); + // By default, don't cache add-ons in AddonRepository.jsm Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false); diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index c864958684b0..6a22f5b80819 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -117,6 +117,7 @@ const PREF_EM_UPDATE_BACKGROUND_URL = "extensions.update.background.url"; const PREF_EM_ENABLED_ADDONS = "extensions.enabledAddons"; const PREF_EM_EXTENSION_FORMAT = "extensions."; const PREF_EM_ENABLED_SCOPES = "extensions.enabledScopes"; +const PREF_EM_STARTUP_SCAN_SCOPES = "extensions.startupScanScopes"; const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI"; const PREF_XPI_ENABLED = "xpinstall.enabled"; const PREF_XPI_WHITELIST_REQUIRED = "xpinstall.whitelist.required"; @@ -327,6 +328,9 @@ const LAZY_OBJECTS = ["XPIDatabase", "XPIDatabaseReconcile"]; var gLazyObjectsLoaded = false; +XPCOMUtils.defineLazyPreferenceGetter(this, "gStartupScanScopes", + PREF_EM_STARTUP_SCAN_SCOPES, 0); + function loadLazyObjects() { let uri = "resource://gre/modules/addons/XPIProviderUtils.js"; let scope = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { @@ -2322,19 +2326,38 @@ this.XPIStates = { /** * Walk through all install locations, highest priority first, * comparing the on-disk state of extensions to what is stored in prefs. + * + * @param {bool} [ignoreSideloads = true] + * If true, ignore changes in scopes where we don't accept + * side-loads. + * * @return true if anything has changed. */ - getInstallState() { + getInstallState(ignoreSideloads = true) { let oldState = this.loadExtensionState(); let changed = false; this.db = new SerializableMap(); for (let location of XPIProvider.installLocations) { - // The list of add-on like file/directory names in the install location. - let addons = location.getAddonLocations(); // The results of scanning this location. let foundAddons = new SerializableMap(); + // Don't bother checking scopes where we don't accept side-loads. + if (ignoreSideloads && !(location.scope & gStartupScanScopes)) { + if (location.name in oldState) { + for (let [id, state] of Object.entries(oldState[location.name])) { + foundAddons.set(id, new XPIState(state)); + } + + this.db.set(location.name, foundAddons); + delete oldState[location.name]; + } + continue; + } + + // The list of add-on like file/directory names in the install location. + let addons = location.getAddonLocations(!ignoreSideloads); + // What our old state thinks should be in this location. let locState = {}; if (location.name in oldState) { @@ -3762,8 +3785,7 @@ this.XPIProvider = { * if it is a new profile or the version is unknown * @return true if a change requiring a restart was detected */ - checkForChanges(aAppChanged, aOldAppVersion, - aOldPlatformVersion) { + checkForChanges(aAppChanged, aOldAppVersion, aOldPlatformVersion) { logger.debug("checkForChanges"); // Keep track of whether and why we need to open and update the database at @@ -3810,7 +3832,7 @@ this.XPIProvider = { // Telemetry probe added around getInstallState() to check perf let telemetryCaptureTime = Cu.now(); - let installChanged = XPIStates.getInstallState(); + let installChanged = XPIStates.getInstallState(aAppChanged === false); let telemetry = Services.telemetry; telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Math.round(Cu.now() - telemetryCaptureTime)); if (installChanged) { @@ -8073,7 +8095,7 @@ class DirectoryInstallLocation { if (!aDirectory.isDirectory()) throw new Error("Location must be a directory."); - this._readAddons(); + this.initialized = false; } /** @@ -8137,7 +8159,12 @@ class DirectoryInstallLocation { /** * Finds all the add-ons installed in this location. */ - _readAddons() { + _readAddons(rescan = false) { + if ((this.initialized && !rescan) || !this._directory) { + return; + } + this.initialized = true; + // Use a snapshot of the directory contents to avoid possible issues with // iterating over a directory while removing files from it (the YAFFS2 // embedded filesystem has this issue, see bug 772238). @@ -8200,7 +8227,9 @@ class DirectoryInstallLocation { /** * Gets an array of nsIFiles for add-ons installed in this location. */ - getAddonLocations() { + getAddonLocations(rescan = false) { + this._readAddons(rescan); + let locations = new Map(); for (let id in this._IDToFileMap) { locations.set(id, this._IDToFileMap[id].clone()); @@ -8217,6 +8246,9 @@ class DirectoryInstallLocation { * @throws if the ID does not match any of the add-ons installed */ getLocationForID(aId) { + if (!(aId in this._IDToFileMap)) + this._readAddons(); + if (aId in this._IDToFileMap) return this._IDToFileMap[aId].clone(); throw new Error("Unknown add-on ID " + aId);