Forked from rhelmer/gist:fd0aa0e66e8be1eff2bdcb05413b1ac9
Created
August 4, 2016 16:44
-
-
Save Osmose/88eb585a302299394a076df11be52fd5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/toolkit/modules/GMPInstallManager.jsm b/toolkit/modules/GMPInstallManager.jsm | |
--- a/toolkit/modules/GMPInstallManager.jsm | |
+++ b/toolkit/modules/GMPInstallManager.jsm | |
@@ -99,17 +99,17 @@ GMPInstallManager.prototype = { | |
let certs = null; | |
if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE)) { | |
allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true); | |
if (GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) { | |
certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH); | |
} | |
} | |
- ProductAddonChecker.getProductAddonList(url, allowNonBuiltIn, certs).then((addons) => { | |
+ ProductAddonChecker.getProductAddonList(url, allowNonBuiltIn, certs).then((addons, revision) => { | |
if (!addons) { | |
this._deferred.resolve([]); | |
} | |
else { | |
this._deferred.resolve(addons.map(a => new GMPAddon(a))); | |
} | |
delete this._deferred; | |
}, (ex) => { | |
diff --git a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm | |
--- a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm | |
+++ b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm | |
@@ -139,30 +139,31 @@ function downloadXML(url, allowNonBuiltI | |
}); | |
} | |
/** | |
* Parses a list of add-ons from a DOM document. | |
* | |
* @param document | |
* The DOM document to parse. | |
- * @return null if there is no <addons> element otherwise an array of the addons | |
- * listed. | |
+ * @return a tuple of {null, null} if there is no <addons> element, | |
+ * {array of the addons listed, -1} if there is no revision number, or | |
+ * {array of the addons listed, revision number of the addon set}. | |
*/ | |
function parseXML(document) { | |
// Check that the root element is correct | |
if (document.documentElement.localName != "updates") { | |
throw new Error("got node name: " + document.documentElement.localName + | |
", expected: updates"); | |
} | |
// Check if there are any addons elements in the updates element | |
let addons = document.querySelector("updates:root > addons"); | |
if (!addons) { | |
- return null; | |
+ return [null, null]; | |
} | |
let results = []; | |
let addonList = document.querySelectorAll("updates:root > addons > addon"); | |
for (let addonElement of addonList) { | |
let addon = {}; | |
for (let name of ["id", "URL", "hashFunction", "hashValue", "version", "size"]) { | |
@@ -170,17 +171,25 @@ function parseXML(document) { | |
addon[name] = addonElement.getAttribute(name); | |
} | |
} | |
addon.size = Number(addon.size) || undefined; | |
results.push(addon); | |
} | |
- return results; | |
+ // Record the revision number for this set, if available. | |
+ let revision = -1; | |
+ let addonData = document.querySelector("updates:root > addons") | |
+ let revisionAttribute = "revision"; | |
+ if (addonData.hasAttribute(revisionAttribute)) { | |
+ revision = addonData.getAttribute(revisionAttribute); | |
+ } | |
+ | |
+ return [results, revision]; | |
} | |
/** | |
* Downloads file from a URL using XHR. | |
* | |
* @param url | |
* The url to download from. | |
* @return a promise that resolves to the path of a temporary file or rejects | |
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm | |
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm | |
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm | |
@@ -170,16 +170,24 @@ const RDFURI_INSTALL_MANIFEST_ROOT = | |
const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#"; | |
const TOOLKIT_ID = "[email protected]"; | |
const XPI_SIGNATURE_CHECK_PERIOD = 24 * 60 * 60; | |
XPCOMUtils.defineConstant(this, "DB_SCHEMA", 17); | |
+const SYSTEM_ADDON_SCHEMA_VERSION = 2; | |
+const EMPTY_SYSTEM_ADDON_SET = { | |
+ schema: SYSTEM_ADDON_SCHEMA_VERSION, | |
+ revision: -1, | |
+ addons: {} | |
+}; | |
+ | |
+ | |
const NOTIFICATION_TOOLBOXPROCESS_LOADED = "ToolboxProcessLoaded"; | |
// Properties that exist in the install manifest | |
const PROP_METADATA = ["id", "version", "type", "internalName", "updateURL", | |
"updateKey", "optionsURL", "optionsType", "aboutURL", | |
"iconURL", "icon64URL"]; | |
const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"]; | |
const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"]; | |
@@ -3007,25 +3015,36 @@ this.XPIProvider = { | |
if (!url) { | |
yield systemAddonLocation.cleanDirectories(); | |
return; | |
} | |
url = UpdateUtils.formatUpdateURL(url); | |
logger.info(`Starting system add-on update check from ${url}.`); | |
- let addonList = yield ProductAddonChecker.getProductAddonList(url); | |
+ let [addonList, revision] = yield ProductAddonChecker.getProductAddonList(url); | |
// If there was no list then do nothing. | |
if (!addonList) { | |
logger.info("No system add-ons list was returned."); | |
yield systemAddonLocation.cleanDirectories(); | |
return; | |
} | |
+ // If there is a revision set and it is older than the recorded revision, | |
+ // do nothing. | |
+ let oldRevision = systemAddonLocation.getRevision(); | |
+ if (revision && oldRevision) { | |
+ if (revision >= 0 && revision < oldRevision) { | |
+ logger.debug(`System add-on server revision too old, server has ${revision} and client has ${oldRevision}`); | |
+ return; | |
+ } | |
+ } | |
+ systemAddonLocation.setRevision(revision); | |
+ | |
addonList = new Map( | |
addonList.map(spec => [spec.id, { spec, path: null, addon: null }])); | |
let getAddonsInLocation = (location) => { | |
return new Promise(resolve => { | |
XPIDatabase.getAddonsInLocation(location, resolve); | |
}); | |
}; | |
@@ -3122,17 +3141,17 @@ this.XPIProvider = { | |
if (!Array.from(addonList.values()).every(item => item.path && item.addon && validateAddon(item))) { | |
throw new Error("Rejecting updated system add-on set that either could not " + | |
"be downloaded or contained unusable add-ons."); | |
} | |
// Install into the install location | |
logger.info("Installing new system add-on set"); | |
yield systemAddonLocation.installAddonSet(Array.from(addonList.values()) | |
- .map(a => a.addon)); | |
+ .map(a => a.addon), revision); | |
// Bug 1204156: Switch to the new system add-ons without requiring a restart | |
} | |
finally { | |
// Delete the temporary files | |
logger.info("Deleting temporary files"); | |
for (let item of addonList.values()) { | |
// If this item downloaded delete the temporary file. | |
@@ -8275,39 +8294,50 @@ function SystemAddonInstallLocation(aNam | |
this._directory = aDirectory.clone(); | |
this._directory.append(this._addonSet.directory); | |
logger.info("SystemAddonInstallLocation scanning directory " + this._directory.path); | |
} | |
else { | |
logger.info("SystemAddonInstallLocation directory is missing"); | |
} | |
+ if (this._addonSet.revision) { | |
+ this._revision = this._addonSet.revision; | |
+ logger.info(`System add-on update revision is: ${this._revision}`); | |
+ } else { | |
+ logger.info("No System add-on update revision specified on server, setting to -1."); | |
+ this._revision = -1; | |
+ } | |
+ | |
DirectoryInstallLocation.call(this, aName, this._directory, aScope); | |
this.locked = true; | |
} | |
SystemAddonInstallLocation.prototype = Object.create(DirectoryInstallLocation.prototype); | |
Object.assign(SystemAddonInstallLocation.prototype, { | |
/** | |
* Reads the current set of system add-ons | |
*/ | |
_loadAddonSet: function() { | |
try { | |
let setStr = Preferences.get(PREF_SYSTEM_ADDON_SET, null); | |
if (setStr) { | |
let addonSet = JSON.parse(setStr); | |
- if ((typeof addonSet == "object") && addonSet.schema == 1) | |
- return addonSet; | |
+ if (typeof addonSet == "object") { | |
+ if (addonSet.schema == 1 || addonSet.schema == 2) { | |
+ return addonSet; | |
+ } | |
+ } | |
} | |
} | |
catch (e) { | |
logger.error("Malformed system add-on set, resetting."); | |
} | |
- return { schema: 1, addons: {} }; | |
+ return EMPTY_SYSTEM_ADDON_SET; | |
}, | |
/** | |
* Saves the current set of system add-ons | |
*/ | |
_saveAddonSet: function(aAddonSet) { | |
Preferences.set(PREF_SYSTEM_ADDON_SET, JSON.stringify(aAddonSet)); | |
}, | |
@@ -8323,16 +8353,25 @@ Object.assign(SystemAddonInstallLocation | |
for (let id of addons.keys()) { | |
if (!(id in this._addonSet.addons)) | |
addons.delete(id); | |
} | |
return addons; | |
}, | |
+ getRevision: function() { | |
+ return this._revision; | |
+ }, | |
+ | |
+ setRevision: function(revision) { | |
+ // TODO validate that it's a Number etc. | |
+ this._revision = revision; | |
+ }, | |
+ | |
/** | |
* Tests whether updated system add-ons are expected. | |
*/ | |
isActive: function() { | |
return this._directory != null; | |
}, | |
isValidAddon: function(aAddon) { | |
@@ -8376,17 +8415,17 @@ Object.assign(SystemAddonInstallLocation | |
return true; | |
}, | |
/** | |
* Resets the add-on set so on the next startup the default set will be used. | |
*/ | |
resetAddonSet: function() { | |
- this._saveAddonSet({ schema: 1, addons: {} }); | |
+ this._saveAddonSet(EMPTY_SYSTEM_ADDON_SET); | |
}, | |
/** | |
* Removes any directories not currently in use or pending use after a | |
* restart. Any errors that happen here don't really matter as we'll attempt | |
* to cleanup again next time. | |
*/ | |
cleanDirectories: Task.async(function*() { | |
@@ -8441,17 +8480,17 @@ Object.assign(SystemAddonInstallLocation | |
iterator.close(); | |
} | |
}), | |
/** | |
* Installs a new set of system add-ons into the location and updates the | |
* add-on set in prefs. We wait to switch state until a restart. | |
*/ | |
- installAddonSet: Task.async(function*(aAddons) { | |
+ installAddonSet: Task.async(function*(aAddons, revision) { | |
// Make sure the base dir exists | |
yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true }); | |
let newDir = this._baseDir.clone(); | |
let uuidGen = Cc["@mozilla.org/uuid-generator;1"]. | |
getService(Ci.nsIUUIDGenerator); | |
newDir.append("blank"); | |
@@ -8490,17 +8529,22 @@ Object.assign(SystemAddonInstallLocation | |
} | |
catch (e) { | |
logger.warn(`Failed to remove new system add-on directory ${newDir.path}.`, e); | |
} | |
throw e; | |
} | |
// All add-ons in position, create the new state and store it in prefs | |
- let state = { schema: 1, directory: newDir.leafName, addons: {} }; | |
+ let state = { | |
+ schema: SYSTEM_ADDON_SCHEMA_VERSION, | |
+ revision: revision, | |
+ directory: newDir.leafName, | |
+ addons: {} | |
+ }; | |
for (let addon of aAddons) { | |
state.addons[addon.id] = { | |
version: addon.version | |
} | |
} | |
this._saveAddonSet(state); | |
this._nextDir = newDir; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment