Bug 1356826: Part 2 - Don't scan directories for changes at startup without pref. r?rhelmer
MozReview-Commit-ID: KdzmJeHpdmU
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -64,16 +64,18 @@ pref("extensions.hotfix.certs.2.sha1Fing
pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/SystemAddons/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
// Disable screenshots for now, Shield will enable this.
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);
// Add-on content security policies.
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");
// Require signed add-ons by default
pref("xpinstall.signatures.required", true);
pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -238,16 +238,19 @@ var AddonTestUtils = {
Services.prefs.setBoolPref("extensions.logging.enabled", true);
// By default only load extensions from the profile install location
Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_PROFILE);
// 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);
// Disable the compatibility updates window by default
Services.prefs.setBoolPref("extensions.showMismatchUI", false);
// Point update checks to the local machine for fast failures
Services.prefs.setCharPref("extensions.update.url", "http://127.0.0.1/updateURL");
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -113,16 +113,17 @@ const PREF_PENDING_OPERATIONS =
const PREF_SKIN_SWITCHPENDING = "extensions.dss.switchPending";
const PREF_SKIN_TO_SELECT = "extensions.lastSelectedSkin";
const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
const PREF_EM_UPDATE_URL = "extensions.update.url";
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";
const PREF_XPI_DIRECT_WHITELISTED = "xpinstall.whitelist.directRequest";
const PREF_XPI_FILE_WHITELISTED = "xpinstall.whitelist.fileRequest";
// xpinstall.signatures.required only supported in dev builds
const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
const PREF_XPI_SIGNATURES_DEV_ROOT = "xpinstall.signatures.dev-root";
@@ -320,16 +321,19 @@ const LOGGER_ID = "addons.xpi";
// (Requires AddonManager.jsm)
var logger = Log.repository.getLogger(LOGGER_ID);
const LAZY_OBJECTS = ["XPIDatabase", "XPIDatabaseReconcile"];
/* globals 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(), {
sandboxName: uri,
wantGlobalProperties: ["TextDecoder"],
});
let shared = {
@@ -2304,29 +2308,48 @@ this.XPIStates = {
}
logger.debug("Loaded add-on state from prefs: ${}", state);
return state;
},
/**
* 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) {
locState = oldState[location.name];
// We've seen this location.
delete oldState[location.name];
}
@@ -3743,18 +3766,17 @@ this.XPIProvider = {
* @param aOldAppVersion
* The version of the application last run with this profile or null
* if it is a new profile or the version is unknown
* @param aOldPlatformVersion
* The version of the platform last run with this profile or null
* 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
// startup time.
let updateReasons = [];
if (aAppChanged) {
updateReasons.push("appChanged");
}
@@ -3791,17 +3813,17 @@ this.XPIProvider = {
updated = this.installDistributionAddons(manifests, aAppChanged);
if (updated) {
updateReasons.push("installDistributionAddons");
}
}
// 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) {
updateReasons.push("directoryState");
}
let haveAnyAddons = (XPIStates.size > 0);
@@ -8051,17 +8073,17 @@ class DirectoryInstallLocation {
this._IDToFileMap = {};
this._linkedAddons = [];
if (!aDirectory || !aDirectory.exists())
return;
if (!aDirectory.isDirectory())
throw new Error("Location must be a directory.");
- this._readAddons();
+ this.initialized = false;
}
/**
* Reads a directory linked to in a file.
*
* @param file
* The file containing the directory path
* @return An nsIFile object representing the linked directory.
@@ -8115,17 +8137,22 @@ class DirectoryInstallLocation {
logger.warn("File pointer " + aFile.path + " does not contain a path");
return null;
}
/**
* 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).
let entries = getDirectoryEntries(this._directory);
for (let entry of entries) {
let id = entry.leafName;
if (id == DIR_STAGE || id == DIR_TRASH)
@@ -8178,33 +8205,38 @@ class DirectoryInstallLocation {
*/
get scope() {
return this._scope;
}
/**
* 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());
}
return locations;
}
/**
* Gets the directory that the add-on with the given ID is installed in.
*
* @param aId
* The ID of the add-on
* @return The nsIFile
* @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);
}
/**
* Returns true if the given addon was installed in this location by a text
* file pointing to its real path.