From 294399b3fbbeb72ec048b478ec5142ce60859dbd Mon Sep 17 00:00:00 2001 From: Kris Maglione Date: Sun, 22 Apr 2018 15:00:08 -0700 Subject: [PATCH] Bug 1363925: Part 8d - Move updateAddonDisabledState to XPIDatabase. r=aswan This code is large and complex, and can only be called when we have an AddonInternal object from XPIDatabase.jsm. It should live with that code. MozReview-Commit-ID: 3ssV5aH9NUJ --HG-- extra : rebase_source : d54474f67213420678706f04291b246ceee154de extra : histedit_source : d1a3ca6fd05856a6675380717351e2e1d1568cf1 --- .../extensions/internal/XPIDatabase.jsm | 157 +++++++++++++++++- .../extensions/internal/XPIInstall.jsm | 7 + .../extensions/internal/XPIProvider.jsm | 154 ++--------------- 3 files changed, 170 insertions(+), 148 deletions(-) diff --git a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm index 9e51d29f80b1..3b7c262b340a 100644 --- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm +++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm @@ -4,6 +4,14 @@ "use strict"; +/** + * This file contains most of the logic required to maintain the + * extensions database, including querying and modifying extension + * metadata. In general, we try to avoid loading it during startup when + * at all possible. Please keep that in mind when deciding whether to + * add code here or elsewhere. + */ + /* eslint "valid-jsdoc": [2, {requireReturn: false, requireReturnDescription: false, prefer: {return: "returns"}}] */ var EXPORTED_SYMBOLS = ["AddonInternal", "XPIDatabase", "XPIDatabaseReconcile"]; @@ -472,7 +480,7 @@ AddonInternal.prototype = { } if (this.inDatabase && updateDatabase) { - XPIProvider.updateAddonDisabledState(this, userDisabled, softDisabled); + XPIDatabase.updateAddonDisabledState(this, userDisabled, softDisabled); XPIDatabase.saveChanges(); } else { this.appDisabled = !XPIDatabase.isUsableAddon(this); @@ -888,7 +896,7 @@ AddonWrapper.prototype = { if (this.hidden) { throw new Error(`Cannot disable hidden add-on ${addon.id}`); } - XPIProvider.updateAddonDisabledState(addon, val); + XPIDatabase.updateAddonDisabledState(addon, val); } else { addon.userDisabled = val; // When enabling remove the softDisabled flag @@ -908,9 +916,9 @@ AddonWrapper.prototype = { // When softDisabling a theme just enable the active theme if (isTheme(addon.type) && val && !addon.userDisabled) { if (isWebExtension(addon.type)) - XPIProvider.updateAddonDisabledState(addon, undefined, val); + XPIDatabase.updateAddonDisabledState(addon, undefined, val); } else { - XPIProvider.updateAddonDisabledState(addon, undefined, val); + XPIDatabase.updateAddonDisabledState(addon, undefined, val); } } else if (!addon.userDisabled) { // Only set softDisabled if not already disabled @@ -1028,9 +1036,9 @@ AddonWrapper.prototype = { if (!this.temporarilyInstalled) { let addonFile = addon.getResourceURI; - XPIProvider.updateAddonDisabledState(addon, true); + XPIDatabase.updateAddonDisabledState(addon, true); Services.obs.notifyObservers(addonFile, "flush-cache-entry"); - XPIProvider.updateAddonDisabledState(addon, false); + XPIDatabase.updateAddonDisabledState(addon, false); resolve(); } else { // This function supports re-installing an existing add-on. @@ -1253,7 +1261,7 @@ Object.assign(DBAddonInternal.prototype, { }); if (wasCompatible != this.isCompatible) - XPIProvider.updateAddonDisabledState(this); + XPIDatabase.updateAddonDisabledState(this); }, toJSON() { @@ -2211,6 +2219,141 @@ this.XPIDatabase = { } }, + /** + * Updates the disabled state for an add-on. Its appDisabled property will be + * calculated and if the add-on is changed the database will be saved and + * appropriate notifications will be sent out to the registered AddonListeners. + * + * @param {DBAddonInternal} aAddon + * The DBAddonInternal to update + * @param {boolean?} [aUserDisabled] + * Value for the userDisabled property. If undefined the value will + * not change + * @param {boolean?} [aSoftDisabled] + * Value for the softDisabled property. If undefined the value will + * not change. If true this will force userDisabled to be true + * @param {boolean?} [aBecauseSelecting] + * True if we're disabling this add-on because we're selecting + * another. + * @returns {boolean?} + * A tri-state indicating the action taken for the add-on: + * - undefined: The add-on did not change state + * - true: The add-on because disabled + * - false: The add-on became enabled + * @throws if addon is not a DBAddonInternal + */ + updateAddonDisabledState(aAddon, aUserDisabled, aSoftDisabled, aBecauseSelecting) { + if (!(aAddon.inDatabase)) + throw new Error("Can only update addon states for installed addons."); + if (aUserDisabled !== undefined && aSoftDisabled !== undefined) { + throw new Error("Cannot change userDisabled and softDisabled at the " + + "same time"); + } + + if (aUserDisabled === undefined) { + aUserDisabled = aAddon.userDisabled; + } else if (!aUserDisabled) { + // If enabling the add-on then remove softDisabled + aSoftDisabled = false; + } + + // If not changing softDisabled or the add-on is already userDisabled then + // use the existing value for softDisabled + if (aSoftDisabled === undefined || aUserDisabled) + aSoftDisabled = aAddon.softDisabled; + + let appDisabled = !this.isUsableAddon(aAddon); + // No change means nothing to do here + if (aAddon.userDisabled == aUserDisabled && + aAddon.appDisabled == appDisabled && + aAddon.softDisabled == aSoftDisabled) + return undefined; + + let wasDisabled = aAddon.disabled; + let isDisabled = aUserDisabled || aSoftDisabled || appDisabled; + + // If appDisabled changes but addon.disabled doesn't, + // no onDisabling/onEnabling is sent - so send a onPropertyChanged. + let appDisabledChanged = aAddon.appDisabled != appDisabled; + + // Update the properties in the database. + this.setAddonProperties(aAddon, { + userDisabled: aUserDisabled, + appDisabled, + softDisabled: aSoftDisabled + }); + + let wrapper = aAddon.wrapper; + + if (appDisabledChanged) { + AddonManagerPrivate.callAddonListeners("onPropertyChanged", + wrapper, + ["appDisabled"]); + } + + // If the add-on is not visible or the add-on is not changing state then + // there is no need to do anything else + if (!aAddon.visible || (wasDisabled == isDisabled)) + return undefined; + + // 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); + } + + // Have we just gone back to the current state? + if (isDisabled != aAddon.active) { + AddonManagerPrivate.callAddonListeners("onOperationCancelled", wrapper); + } else { + if (isDisabled) { + AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, false); + } else { + AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, false); + } + + this.updateAddonActive(aAddon, !isDisabled); + + if (isDisabled) { + if (aAddon.bootstrap && XPIProvider.activeAddons.has(aAddon.id)) { + XPIProvider.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown", + BOOTSTRAP_REASONS.ADDON_DISABLE); + XPIProvider.unloadBootstrapScope(aAddon.id); + } + AddonManagerPrivate.callAddonListeners("onDisabled", wrapper); + } else { + if (aAddon.bootstrap) { + XPIProvider.callBootstrapMethod(aAddon, aAddon._sourceBundle, "startup", + BOOTSTRAP_REASONS.ADDON_ENABLE); + } + AddonManagerPrivate.callAddonListeners("onEnabled", wrapper); + } + } + + // Notify any other providers that a new theme has been enabled + if (isTheme(aAddon.type)) { + if (!isDisabled) { + AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type); + + if (xpiState) { + xpiState.syncWithDB(aAddon); + XPIStates.save(); + } + } else if (isDisabled && !aBecauseSelecting) { + AddonManagerPrivate.notifyAddonChanged(null, "theme"); + } + } + + return isDisabled; + }, + /** * Record a bit of per-addon telemetry. * diff --git a/toolkit/mozapps/extensions/internal/XPIInstall.jsm b/toolkit/mozapps/extensions/internal/XPIInstall.jsm index 661206ffa8f2..944bafd82dfe 100644 --- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm +++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm @@ -4,6 +4,13 @@ "use strict"; +/** + * This file contains most of the logic required to install extensions. + * In general, we try to avoid loading it until extension installation + * or update is required. Please keep that in mind when deciding whether + * to add code here or elsewhere. + */ + /* eslint "valid-jsdoc": [2, {requireReturn: false, requireReturnDescription: false, prefer: {return: "returns"}}] */ var EXPORTED_SYMBOLS = [ diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 9a1495b4abba..db365a6cbf24 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -4,6 +4,13 @@ "use strict"; +/** + * This file contains most of the logic required to load and run + * extensions at startup. Anything which is not required immediately at + * startup should go in XPIInstall.jsm or XPIDatabase.jsm if at all + * possible, in order to minimize the impact on startup performance. + */ + /* eslint "valid-jsdoc": [2, {requireReturn: false, requireReturnDescription: false, prefer: {return: "returns"}}] */ var EXPORTED_SYMBOLS = ["XPIProvider", "XPIInternal"]; @@ -1977,7 +1984,7 @@ var XPIProvider = { ["signedState"]); } - let disabled = XPIProvider.updateAddonDisabledState(addon); + let disabled = XPIDatabase.updateAddonDisabledState(addon); if (disabled !== undefined) changes[disabled ? "disabled" : "enabled"].push(addon.id); } @@ -2567,7 +2574,7 @@ var XPIProvider = { let addons = XPIDatabase.getAddonsByType("webextension-theme"); for (let theme of addons) { if (theme.visible && theme.id != aId) - this.updateAddonDisabledState(theme, true, undefined, true); + XPIDatabase.updateAddonDisabledState(theme, true, undefined, true); } if (!aId && (!LightweightThemeManager.currentTheme || @@ -2587,7 +2594,7 @@ var XPIProvider = { updateAddonAppDisabledStates() { let addons = XPIDatabase.getAddons(); for (let addon of addons) { - this.updateAddonDisabledState(addon); + XPIDatabase.updateAddonDisabledState(addon); } }, @@ -2603,7 +2610,7 @@ var XPIProvider = { if (aRepoAddon || AddonRepository.getCompatibilityOverridesSync(addon.id)) { logger.debug("updateAddonRepositoryData got info for " + addon.id); addon._repositoryAddon = aRepoAddon; - this.updateAddonDisabledState(addon); + XPIDatabase.updateAddonDisabledState(addon); } }))); }, @@ -2828,7 +2835,7 @@ var XPIProvider = { activeAddon.disable = true; for (let addon of this.getDependentAddons(aAddon)) { if (addon.active) - this.updateAddonDisabledState(addon); + XPIDatabase.updateAddonDisabledState(addon); } } } @@ -2890,7 +2897,7 @@ var XPIProvider = { // Extensions are automatically initialized in the correct order at startup. if (aMethod == "startup" && aReason != BOOTSTRAP_REASONS.APP_STARTUP) { for (let addon of this.getDependentAddons(aAddon)) - this.updateAddonDisabledState(addon); + XPIDatabase.updateAddonDisabledState(addon); } if (CHROME_TYPES.has(aAddon.type) && aMethod == "shutdown" && aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) { @@ -2900,141 +2907,6 @@ var XPIProvider = { this.setTelemetry(aAddon.id, aMethod + "_MS", new Date() - timeStart); } }, - - /** - * Updates the disabled state for an add-on. Its appDisabled property will be - * calculated and if the add-on is changed the database will be saved and - * appropriate notifications will be sent out to the registered AddonListeners. - * - * @param {DBAddonInternal} aAddon - * The DBAddonInternal to update - * @param {boolean?} [aUserDisabled] - * Value for the userDisabled property. If undefined the value will - * not change - * @param {boolean?} [aSoftDisabled] - * Value for the softDisabled property. If undefined the value will - * not change. If true this will force userDisabled to be true - * @param {boolean?} [aBecauseSelecting] - * True if we're disabling this add-on because we're selecting - * another. - * @returns {boolean?} - * A tri-state indicating the action taken for the add-on: - * - undefined: The add-on did not change state - * - true: The add-on because disabled - * - false: The add-on became enabled - * @throws if addon is not a DBAddonInternal - */ - updateAddonDisabledState(aAddon, aUserDisabled, aSoftDisabled, aBecauseSelecting) { - if (!(aAddon.inDatabase)) - throw new Error("Can only update addon states for installed addons."); - if (aUserDisabled !== undefined && aSoftDisabled !== undefined) { - throw new Error("Cannot change userDisabled and softDisabled at the " + - "same time"); - } - - if (aUserDisabled === undefined) { - aUserDisabled = aAddon.userDisabled; - } else if (!aUserDisabled) { - // If enabling the add-on then remove softDisabled - aSoftDisabled = false; - } - - // If not changing softDisabled or the add-on is already userDisabled then - // use the existing value for softDisabled - if (aSoftDisabled === undefined || aUserDisabled) - aSoftDisabled = aAddon.softDisabled; - - let appDisabled = !XPIDatabase.isUsableAddon(aAddon); - // No change means nothing to do here - if (aAddon.userDisabled == aUserDisabled && - aAddon.appDisabled == appDisabled && - aAddon.softDisabled == aSoftDisabled) - return undefined; - - let wasDisabled = aAddon.disabled; - let isDisabled = aUserDisabled || aSoftDisabled || appDisabled; - - // If appDisabled changes but addon.disabled doesn't, - // no onDisabling/onEnabling is sent - so send a onPropertyChanged. - let appDisabledChanged = aAddon.appDisabled != appDisabled; - - // Update the properties in the database. - XPIDatabase.setAddonProperties(aAddon, { - userDisabled: aUserDisabled, - appDisabled, - softDisabled: aSoftDisabled - }); - - let wrapper = aAddon.wrapper; - - if (appDisabledChanged) { - AddonManagerPrivate.callAddonListeners("onPropertyChanged", - wrapper, - ["appDisabled"]); - } - - // If the add-on is not visible or the add-on is not changing state then - // there is no need to do anything else - if (!aAddon.visible || (wasDisabled == isDisabled)) - return undefined; - - // 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); - } - - // Have we just gone back to the current state? - if (isDisabled != aAddon.active) { - AddonManagerPrivate.callAddonListeners("onOperationCancelled", wrapper); - } else { - if (isDisabled) { - AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, false); - } else { - AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, false); - } - - XPIDatabase.updateAddonActive(aAddon, !isDisabled); - - if (isDisabled) { - if (aAddon.bootstrap && this.activeAddons.has(aAddon.id)) { - this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown", - BOOTSTRAP_REASONS.ADDON_DISABLE); - this.unloadBootstrapScope(aAddon.id); - } - AddonManagerPrivate.callAddonListeners("onDisabled", wrapper); - } else { - if (aAddon.bootstrap) { - this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "startup", - BOOTSTRAP_REASONS.ADDON_ENABLE); - } - AddonManagerPrivate.callAddonListeners("onEnabled", wrapper); - } - } - - // Notify any other providers that a new theme has been enabled - if (isTheme(aAddon.type)) { - if (!isDisabled) { - AddonManagerPrivate.notifyAddonChanged(aAddon.id, aAddon.type); - - if (xpiState) { - xpiState.syncWithDB(aAddon); - XPIStates.save(); - } - } else if (isDisabled && !aBecauseSelecting) { - AddonManagerPrivate.notifyAddonChanged(null, "theme"); - } - } - - return isDisabled; - }, }; for (let meth of ["cancelUninstallAddon", "getInstallForFile",