Bug 1363925: Part 6 - Move staged add-on install logic to XPIInstall. r?zombie
MozReview-Commit-ID: IDXsbKvl5U3
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -3650,16 +3650,89 @@ var XPIInstall = {
XPIStates.addAddon(addon);
logger.debug("Installed distribution add-on " + id);
Services.prefs.setBoolPref(PREF_BRANCH_INSTALLED_ADDON + id, true);
return addon;
},
+ /**
+ * Completes the install of an add-on which was staged during the last
+ * session.
+ *
+ * @param {string} id
+ * The expected ID of the add-on.
+ * @param {object} metadata
+ * The parsed metadata for the staged install.
+ * @param {InstallLocation} 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());
+
+ // Check that the directory's name is a valid ID.
+ if (!gIDTest.test(id) || !source.exists() || !source.isFile()) {
+ throw new Error(`Ignoring invalid staging directory entry: ${id}`);
+ }
+
+ let addon = await loadManifestFromFile(source, location);
+
+ if (mustSign(addon.type) &&
+ addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
+ throw new Error(`Refusing to install staged add-on ${id} with signed state ${addon.signedState}`);
+ }
+
+ addon.importMetadata(metadata);
+
+ var oldBootstrap = null;
+ logger.debug(`Processing install of ${id} in ${location.name}`);
+ let existingAddon = XPIStates.findAddon(id);
+ if (existingAddon && existingAddon.bootstrapped) {
+ try {
+ var file = existingAddon.file;
+ if (file.exists()) {
+ oldBootstrap = existingAddon;
+
+ // We'll be replacing a currently active bootstrapped add-on so
+ // call its uninstall method
+ let newVersion = addon.version;
+ let oldVersion = existingAddon;
+ let uninstallReason = newVersionReason(oldVersion, newVersion);
+
+ XPIProvider.callBootstrapMethod(existingAddon,
+ file, "uninstall", uninstallReason,
+ { newVersion });
+ XPIProvider.unloadBootstrapScope(id);
+ flushChromeCaches();
+ }
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+
+ try {
+ addon._sourceBundle = location.installAddon({
+ id, source, existingAddonID: id,
+ });
+ XPIStates.addAddon(addon);
+ } catch (e) {
+ if (oldBootstrap) {
+ // Re-install the old add-on
+ XPIProvider.callBootstrapMethod(oldBootstrap, existingAddon, "install",
+ BOOTSTRAP_REASONS.ADDON_INSTALL);
+ }
+ throw e;
+ }
+
+ return addon;
+ },
+
async updateSystemAddons() {
let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
if (!systemAddonLocation)
return;
// Don't do anything in safe mode
if (Services.appinfo.inSafeMode)
return;
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2038,93 +2038,38 @@ var XPIProvider = {
// We can't install or uninstall anything in locked locations
if (location.locked) {
continue;
}
let state = XPIStates.getLocation(location.name);
let cleanNames = [];
+ let promises = [];
for (let [id, metadata] of state.getStagedAddons()) {
state.unstageAddon(id);
- let source = getFile(`${id}.xpi`, location.getStagingDir());
-
- // Check that the directory's name is a valid ID.
- if (!gIDTest.test(id) || !source.exists() || !source.isFile()) {
- logger.warn("Ignoring invalid staging directory entry: ${id}", {id});
- cleanNames.push(source.leafName);
- continue;
- }
-
+ aManifests[location.name][id] = null;
+ promises.push(
+ XPIInstall.installStagedAddon(id, metadata, location).then(
+ addon => {
+ aManifests[location.name][id] = addon;
+ },
+ error => {
+ delete aManifests[location.name][id];
+ cleanNames.push(`${id}.xpi`);
+
+ logger.error(`Failed to install staged add-on ${id} in ${location.name}`,
+ error);
+ }));
+ }
+
+ if (promises.length) {
changed = true;
- aManifests[location.name][id] = null;
-
- let addon;
- try {
- addon = XPIInstall.syncLoadManifestFromFile(source, location);
- } catch (e) {
- logger.error(`Unable to read add-on manifest from ${source.path}`, e);
- cleanNames.push(source.leafName);
- continue;
- }
-
- if (mustSign(addon.type) &&
- addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
- logger.warn(`Refusing to install staged add-on ${id} with signed state ${addon.signedState}`);
- cleanNames.push(source.leafName);
- continue;
- }
-
- addon.importMetadata(metadata);
- aManifests[location.name][id] = addon;
-
- var oldBootstrap = null;
- logger.debug(`Processing install of ${id} in ${location.name}`);
- let existingAddon = XPIStates.findAddon(id);
- if (existingAddon && existingAddon.bootstrapped) {
- try {
- var file = existingAddon.file;
- if (file.exists()) {
- oldBootstrap = existingAddon;
-
- // We'll be replacing a currently active bootstrapped add-on so
- // call its uninstall method
- let newVersion = addon.version;
- let oldVersion = existingAddon;
- let uninstallReason = XPIInstall.newVersionReason(oldVersion, newVersion);
-
- this.callBootstrapMethod(existingAddon,
- file, "uninstall", uninstallReason,
- { newVersion });
- this.unloadBootstrapScope(id);
- XPIInstall.flushChromeCaches();
- }
- } catch (e) {
- Cu.reportError(e);
- }
- }
-
- try {
- addon._sourceBundle = location.installAddon({
- id, source, existingAddonID: id,
- });
- XPIStates.addAddon(addon);
- } catch (e) {
- logger.error("Failed to install staged add-on " + id + " in " + location.name,
- e);
-
- delete aManifests[location.name][id];
-
- if (oldBootstrap) {
- // Re-install the old add-on
- this.callBootstrapMethod(oldBootstrap, existingAddon, "install",
- BOOTSTRAP_REASONS.ADDON_INSTALL);
- }
- }
+ awaitPromise(Promise.all(promises));
}
try {
if (cleanNames.length) {
location.cleanStagingDir(cleanNames);
}
} catch (e) {
// Non-critical, just saves some perf on startup if we clean this up.