summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/internal
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/internal')
-rw-r--r--toolkit/mozapps/extensions/internal/AddonLogging.jsm28
-rw-r--r--toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm394
-rw-r--r--toolkit/mozapps/extensions/internal/Content.js13
-rw-r--r--toolkit/mozapps/extensions/internal/XPIProvider.jsm18
4 files changed, 333 insertions, 120 deletions
diff --git a/toolkit/mozapps/extensions/internal/AddonLogging.jsm b/toolkit/mozapps/extensions/internal/AddonLogging.jsm
index 362439bae..f05a6fe6c 100644
--- a/toolkit/mozapps/extensions/internal/AddonLogging.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonLogging.jsm
@@ -81,7 +81,7 @@ function AddonLogger(aName) {
AddonLogger.prototype = {
name: null,
- error: function AddonLogger_error(aStr, aException) {
+ error: function(aStr, aException) {
let message = formatLogMessage("error", this.name, aStr, aException);
let stack = getStackDetails(aException);
@@ -95,6 +95,18 @@ AddonLogger.prototype = {
// Always dump errors, in case the Console Service isn't listening yet
dump("*** " + message + "\n");
+ function formatTimestamp(date) {
+ // Format timestamp as: "%Y-%m-%d %H:%M:%S"
+ let year = String(date.getFullYear());
+ let month = String(date.getMonth() + 1).padStart(2, "0");
+ let day = String(date.getDate()).padStart(2, "0");
+ let hours = String(date.getHours()).padStart(2, "0");
+ let minutes = String(date.getMinutes()).padStart(2, "0");
+ let seconds = String(date.getSeconds()).padStart(2, "0");
+
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+ }
+
try {
var tstamp = new Date();
var logfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_LOG]);
@@ -104,7 +116,7 @@ AddonLogger.prototype = {
var writer = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
writer.init(stream, "UTF-8", 0, 0x0000);
- writer.writeString(tstamp.toLocaleFormat("%Y-%m-%d %H:%M:%S ") +
+ writer.writeString(formatTimestamp(tstamp) + " " +
message + " at " + stack.sourceName + ":" +
stack.lineNumber + "\n");
writer.close();
@@ -112,7 +124,7 @@ AddonLogger.prototype = {
catch (e) { }
},
- warn: function AddonLogger_warn(aStr, aException) {
+ warn: function(aStr, aException) {
let message = formatLogMessage("warn", this.name, aStr, aException);
let stack = getStackDetails(aException);
@@ -127,7 +139,7 @@ AddonLogger.prototype = {
dump("*** " + message + "\n");
},
- log: function AddonLogger_log(aStr, aException) {
+ log: function(aStr, aException) {
if (gDebugLogEnabled) {
let message = formatLogMessage("log", this.name, aStr, aException);
dump("*** " + message + "\n");
@@ -137,14 +149,14 @@ AddonLogger.prototype = {
};
this.LogManager = {
- getLogger: function LogManager_getLogger(aName, aTarget) {
+ getLogger: function(aName, aTarget) {
let logger = new AddonLogger(aName);
if (aTarget) {
["error", "warn", "log"].forEach(function(name) {
let fname = name.toUpperCase();
delete aTarget[fname];
- aTarget[fname] = function LogManager_targetName(aStr, aException) {
+ aTarget[fname] = function(aStr, aException) {
logger[name](aStr, aException);
};
});
@@ -155,13 +167,13 @@ this.LogManager = {
};
var PrefObserver = {
- init: function PrefObserver_init() {
+ init: function() {
Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
Services.obs.addObserver(this, "xpcom-shutdown", false);
this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
},
- observe: function PrefObserver_observe(aSubject, aTopic, aData) {
+ observe: function(aSubject, aTopic, aData) {
if (aTopic == "xpcom-shutdown") {
Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
Services.obs.removeObserver(this, "xpcom-shutdown");
diff --git a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
index 8d742ea42..f927bc745 100644
--- a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
@@ -15,30 +15,36 @@ const Cu = Components.utils;
this.EXPORTED_SYMBOLS = [ "AddonUpdateChecker" ];
-const TIMEOUT = 60 * 1000;
-const PREFIX_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
-const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
-const PREFIX_ITEM = "urn:mozilla:item:";
-const PREFIX_EXTENSION = "urn:mozilla:extension:";
-const PREFIX_THEME = "urn:mozilla:theme:";
-const TOOLKIT_ID = "toolkit@mozilla.org"
-#ifdef MOZ_PHOENIX_EXTENSIONS
-const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
-#endif
-const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
+const TIMEOUT = 60 * 1000;
+const PREFIX_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
+const PREFIX_ITEM = "urn:mozilla:item:";
+const PREFIX_EXTENSION = "urn:mozilla:extension:";
+const PREFIX_THEME = "urn:mozilla:theme:";
+const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
+
+const TOOLKIT_ID = "toolkit@mozilla.org";
+const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
+const FIREFOX_APPCOMPATVERSION = "56.9"
const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
+const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
+ "resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
"resource://gre/modules/addons/AddonRepository.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest",
+ "resource://gre/modules/ServiceRequest.jsm");
+
// Shared code for suppressing bad cert dialogs.
-XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
+XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
let certUtils = {};
Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
return certUtils;
@@ -80,7 +86,7 @@ RDFSerializer.prototype = {
* @return a string with all characters invalid in XML character data
* converted to entity references.
*/
- escapeEntities: function RDFS_escapeEntities(aString) {
+ escapeEntities: function(aString) {
aString = aString.replace(/&/g, "&");
aString = aString.replace(/</g, "&lt;");
aString = aString.replace(/>/g, "&gt;");
@@ -98,8 +104,7 @@ RDFSerializer.prototype = {
* The current level of indent for pretty-printing
* @return a string containing the serialized elements.
*/
- serializeContainerItems: function RDFS_serializeContainerItems(aDs, aContainer,
- aIndent) {
+ serializeContainerItems: function(aDs, aContainer, aIndent) {
var result = "";
var items = aContainer.GetElements();
while (items.hasMoreElements()) {
@@ -125,9 +130,7 @@ RDFSerializer.prototype = {
* @return a string containing the serialized properties.
* @throws if the resource contains a property that cannot be serialized
*/
- serializeResourceProperties: function RDFS_serializeResourceProperties(aDs,
- aResource,
- aIndent) {
+ serializeResourceProperties: function(aDs, aResource, aIndent) {
var result = "";
var items = [];
var arcs = aDs.ArcLabelsOut(aResource);
@@ -181,7 +184,7 @@ RDFSerializer.prototype = {
* @return a string containing the serialized resource.
* @throws if the RDF data contains multiple references to the same resource.
*/
- serializeResource: function RDFS_serializeResource(aDs, aResource, aIndent) {
+ serializeResource: function(aDs, aResource, aIndent) {
if (this.resources.indexOf(aResource) != -1 ) {
// We cannot output multiple references to the same resource.
throw Components.Exception("Cannot serialize multiple references to " + aResource.Value);
@@ -221,6 +224,48 @@ RDFSerializer.prototype = {
}
/**
+ * Sanitizes the update URL in an update item, as returned by
+ * parseRDFManifest and parseJSONManifest. Ensures that:
+ *
+ * - The URL is secure, or secured by a strong enough hash.
+ * - The security principal of the update manifest has permission to
+ * load the URL.
+ *
+ * @param aUpdate
+ * The update item to sanitize.
+ * @param aRequest
+ * The XMLHttpRequest used to load the manifest.
+ * @param aHashPattern
+ * The regular expression used to validate the update hash.
+ * @param aHashString
+ * The human-readable string specifying which hash functions
+ * are accepted.
+ */
+function sanitizeUpdateURL(aUpdate, aRequest, aHashPattern, aHashString) {
+ if (aUpdate.updateURL) {
+ let scriptSecurity = Services.scriptSecurityManager;
+ let principal = scriptSecurity.getChannelURIPrincipal(aRequest.channel);
+ try {
+ // This logs an error on failure, so no need to log it a second time
+ scriptSecurity.checkLoadURIStrWithPrincipal(principal, aUpdate.updateURL,
+ scriptSecurity.DISALLOW_SCRIPT);
+ } catch (e) {
+ delete aUpdate.updateURL;
+ return;
+ }
+
+ if (AddonManager.checkUpdateSecurity &&
+ !aUpdate.updateURL.startsWith("https:") &&
+ !aHashPattern.test(aUpdate.updateHash)) {
+ logger.warn(`Update link ${aUpdate.updateURL} is not secure and is not verified ` +
+ `by a strong enough hash (needs to be ${aHashString}).`);
+ delete aUpdate.updateURL;
+ delete aUpdate.updateHash;
+ }
+ }
+}
+
+/**
* Parses an RDF style update manifest into an array of update objects.
*
* @param aId
@@ -229,10 +274,17 @@ RDFSerializer.prototype = {
* An optional update key for the add-on
* @param aRequest
* The XMLHttpRequest that has retrieved the update manifest
+ * @param aManifestData
+ * The pre-parsed manifest, as a bare XML DOM document
* @return an array of update objects
* @throws if the update manifest is invalid in any way
*/
-function parseRDFManifest(aId, aUpdateKey, aRequest) {
+function parseRDFManifest(aId, aUpdateKey, aRequest, aManifestData) {
+ if (aManifestData.documentElement.namespaceURI != PREFIX_NS_RDF) {
+ throw Components.Exception("update.rdf: Update manifest had an unrecognised namespace: " +
+ aManifestData.documentElement.namespaceURI);
+ }
+
function EM_R(aProp) {
return gRDF.GetResource(PREFIX_NS_EM + aProp);
}
@@ -261,7 +313,7 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
function getRequiredProperty(aDs, aSource, aProperty) {
let value = getProperty(aDs, aSource, aProperty);
if (!value)
- throw Components.Exception("Update manifest is missing a required " + aProperty + " property.");
+ throw Components.Exception("update.rdf: Update manifest is missing a required " + aProperty + " property.");
return value;
}
@@ -275,15 +327,19 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
let extensionRes = gRDF.GetResource(PREFIX_EXTENSION + aId);
let themeRes = gRDF.GetResource(PREFIX_THEME + aId);
let itemRes = gRDF.GetResource(PREFIX_ITEM + aId);
- let addonRes = ds.ArcLabelsOut(extensionRes).hasMoreElements() ? extensionRes
- : ds.ArcLabelsOut(themeRes).hasMoreElements() ? themeRes
- : itemRes;
+ let addonRes;
+ if (ds.ArcLabelsOut(extensionRes).hasMoreElements())
+ addonRes = extensionRes;
+ else if (ds.ArcLabelsOut(themeRes).hasMoreElements())
+ addonRes = themeRes;
+ else
+ addonRes = itemRes;
// If we have an update key then the update manifest must be signed
if (aUpdateKey) {
let signature = getProperty(ds, addonRes, "signature");
if (!signature)
- throw Components.Exception("Update manifest for " + aId + " does not contain a required signature");
+ throw Components.Exception("update.rdf: Update manifest for " + aId + " does not contain a required signature");
let serializer = new RDFSerializer();
let updateString = null;
@@ -291,7 +347,7 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
updateString = serializer.serializeResource(ds, addonRes);
}
catch (e) {
- throw Components.Exception("Failed to generate signed string for " + aId + ". Serializer threw " + e,
+ throw Components.Exception("update.rdf: Failed to generate signed string for " + aId + ". Serializer threw " + e,
e.result);
}
@@ -303,7 +359,7 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
result = verifier.verifyData(updateString, signature, aUpdateKey);
}
catch (e) {
- throw Components.Exception("The signature or updateKey for " + aId + " is malformed." +
+ throw Components.Exception("update.rdf: The signature or updateKey for " + aId + " is malformed." +
"Verifier threw " + e, e.result);
}
@@ -316,7 +372,7 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
// A missing updates property doesn't count as a failure, just as no avialable
// update information
if (!updates) {
- logger.warn("Update manifest for " + aId + " did not contain an updates property");
+ logger.warn("update.rdf: Update manifest for " + aId + " did not contain an updates property");
return [];
}
@@ -326,7 +382,7 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
let cu = Cc["@mozilla.org/rdf/container-utils;1"].
getService(Ci.nsIRDFContainerUtils);
if (!cu.IsContainer(ds, updates))
- throw Components.Exception("Updates property was not an RDF container");
+ throw Components.Exception("update.rdf: Updates property was not an RDF container");
let results = [];
let ctr = Cc["@mozilla.org/rdf/container;1"].
@@ -337,11 +393,11 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
let item = items.getNext().QueryInterface(Ci.nsIRDFResource);
let version = getProperty(ds, item, "version");
if (!version) {
- logger.warn("Update manifest is missing a required version property.");
+ logger.warn("update.rdf: Update manifest is missing a required version property.");
continue;
}
- logger.debug("Found an update entry for " + aId + " version " + version);
+ logger.debug("update.rdf: Found an update entry for " + aId + " version " + version);
let targetApps = ds.GetTargets(item, EM_R("targetApplication"), true);
while (targetApps.hasMoreElements()) {
@@ -369,14 +425,10 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
targetApplications: [appEntry]
};
- if (result.updateURL && AddonManager.checkUpdateSecurity &&
- result.updateURL.substring(0, 6) != "https:" &&
- (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) {
- logger.warn("updateLink " + result.updateURL + " is not secure and is not verified" +
- " by a strong enough hash (needs to be sha1 or stronger).");
- delete result.updateURL;
- delete result.updateHash;
- }
+ // The JSON update protocol requires an SHA-2 hash. RDF still
+ // supports SHA-1, for compatibility reasons.
+ sanitizeUpdateURL(result, aRequest, /^sha/, "sha1 or stronger");
+
results.push(result);
}
}
@@ -384,6 +436,160 @@ function parseRDFManifest(aId, aUpdateKey, aRequest) {
}
/**
+ * Parses an JSON update manifest into an array of update objects.
+ *
+ * @param aId
+ * The ID of the add-on being checked for updates
+ * @param aUpdateKey
+ * An optional update key for the add-on
+ * @param aRequest
+ * The XMLHttpRequest that has retrieved the update manifest
+ * @param aManifestData
+ * The pre-parsed manifest, as a JSON object tree
+ * @return an array of update objects
+ * @throws if the update manifest is invalid in any way
+ */
+function parseJSONManifest(aId, aUpdateKey, aRequest, aManifestData) {
+ if (aUpdateKey)
+ throw Components.Exception("update.json: Update keys are not supported for JSON update manifests");
+
+ let TYPE_CHECK = {
+ "array": val => Array.isArray(val),
+ "object": val => val && typeof val == "object" && !Array.isArray(val),
+ };
+
+ function getProperty(aObj, aProperty, aType, aDefault = undefined) {
+ if (!(aProperty in aObj))
+ return aDefault;
+
+ let value = aObj[aProperty];
+
+ let matchesType = aType in TYPE_CHECK ? TYPE_CHECK[aType](value) : typeof value == aType;
+ if (!matchesType)
+ throw Components.Exception(`update.json: Update manifest property '${aProperty}' has incorrect type (expected ${aType})`);
+
+ return value;
+ }
+
+ function getRequiredProperty(aObj, aProperty, aType) {
+ let value = getProperty(aObj, aProperty, aType);
+ if (value === undefined)
+ throw Components.Exception(`update.json: Update manifest is missing a required ${aProperty} property.`);
+ return value;
+ }
+
+ let manifest = aManifestData;
+
+ if (!TYPE_CHECK["object"](manifest))
+ throw Components.Exception("update.json: Root element of update manifest must be a JSON object literal");
+
+ // The set of add-ons this manifest has updates for
+ let addons = getRequiredProperty(manifest, "addons", "object");
+
+ // The entry for this particular add-on
+ let addon = getProperty(addons, aId, "object");
+
+ // A missing entry doesn't count as a failure, just as no avialable update
+ // information
+ if (!addon) {
+ logger.warn("update.json: Update manifest did not contain an entry for " + aId);
+ return [];
+ }
+
+ let appID = Services.appinfo.ID;
+ let platformVersion = Services.appinfo.platformVersion;
+
+ // The list of available updates
+ let updates = getProperty(addon, "updates", "array", []);
+
+ let results = [];
+
+ for (let update of updates) {
+ let version = getRequiredProperty(update, "version", "string");
+ logger.debug(`update.json: Found an update entry for ${aId} version ${version}`);
+
+ let applications = getRequiredProperty(update, "applications", "object");
+
+ let app;
+ let appEntry;
+
+ if (appID in applications) {
+ logger.debug("update.json: Native targetApplication");
+ app = getProperty(applications, appID, "object");
+
+ appEntry = {
+ id: appID,
+ minVersion: getRequiredProperty(app, "min_version", "string"),
+ maxVersion: getRequiredProperty(app, "max_version", "string"),
+ }
+ }
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ else if (FIREFOX_ID in applications) {
+ logger.debug("update.json: Dual-GUID targetApplication");
+ app = getProperty(applications, FIREFOX_ID, "object");
+
+ appEntry = {
+ id: FIREFOX_ID,
+ minVersion: getRequiredProperty(app, "min_version", "string"),
+ maxVersion: getRequiredProperty(app, "max_version", "string"),
+ }
+ }
+#endif
+ else if (TOOLKIT_ID in applications) {
+ logger.debug("update.json: Toolkit targetApplication");
+ app = getProperty(applications, TOOLKIT_ID, "object");
+
+ appEntry = {
+ id: TOOLKIT_ID,
+ minVersion: getRequiredProperty(app, "min_version", "string"),
+ maxVersion: getRequiredProperty(app, "max_version", "string"),
+ }
+ }
+ else if ("gecko" in applications) {
+ logger.debug("update.json: Mozilla Compatiblity Mode");
+ app = getProperty(applications, "gecko", "object");
+
+ appEntry = {
+#ifdef MOZ_PHOENIX
+ id: FIREFOX_ID,
+ minVersion: getProperty(app, "strict_min_version", "string",
+ Services.prefs.getCharPref(PREF_EM_MIN_COMPAT_APP_VERSION)),
+#else
+ id: TOOLKIT_ID,
+ minVersion: platformVersion,
+#endif
+#if defined(MOZ_PHOENIX) && defined(MOZ_PHOENIX_EXTENSIONS)
+ maxVersion: FIREFOX_APPCOMPATVERSION,
+#else
+ maxVersion: '*',
+#endif
+ };
+ }
+ else {
+ continue;
+ }
+
+ let result = {
+ id: aId,
+ version: version,
+ multiprocessCompatible: getProperty(update, "multiprocess_compatible", "boolean", false),
+ updateURL: getProperty(update, "update_link", "string"),
+ updateHash: getProperty(update, "update_hash", "string"),
+ updateInfoURL: getProperty(update, "update_info_url", "string"),
+ strictCompatibility: getProperty(app, "strict_compatibility", "boolean", false),
+ targetApplications: [appEntry],
+ };
+
+ // The JSON update protocol requires an SHA-2 hash. RDF still
+ // supports SHA-1, for compatibility reasons.
+ sanitizeUpdateURL(result, aRequest, /^sha(256|512):/, "sha256 or sha512");
+
+ results.push(result);
+ }
+ return results;
+}
+
+/**
* Starts downloading an update manifest and then passes it to an appropriate
* parser to convert to an array of update objects
*
@@ -411,20 +617,18 @@ function UpdateParser(aId, aUpdateKey, aUrl, aObserver) {
logger.debug("Requesting " + aUrl);
try {
- this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
- createInstance(Ci.nsIXMLHttpRequest);
+ this.request = new ServiceRequest();
this.request.open("GET", this.url, true);
this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
// Prevent the request from writing to cache.
this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
- this.request.overrideMimeType("text/xml");
+ this.request.overrideMimeType("text/plain");
this.request.setRequestHeader("Moz-XPI-Update", "1", true);
this.request.timeout = TIMEOUT;
- var self = this;
- this.request.addEventListener("load", function loadEventListener(event) { self.onLoad() }, false);
- this.request.addEventListener("error", function errorEventListener(event) { self.onError() }, false);
- this.request.addEventListener("timeout", function timeoutEventListener(event) { self.onTimeout() }, false);
+ this.request.addEventListener("load", () => this.onLoad(), false);
+ this.request.addEventListener("error", () => this.onError(), false);
+ this.request.addEventListener("timeout", () => this.onTimeout(), false);
this.request.send(null);
}
catch (e) {
@@ -442,7 +646,7 @@ UpdateParser.prototype = {
/**
* Called when the manifest has been successfully loaded.
*/
- onLoad: function UP_onLoad() {
+ onLoad: function() {
let request = this.request;
this.request = null;
this._doneAt = new Error("place holder");
@@ -458,6 +662,7 @@ UpdateParser.prototype = {
CertUtils.checkCert(request.channel, !requireBuiltIn);
}
catch (e) {
+ logger.warn("Request failed: " + this.url + " - " + e);
this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
return;
}
@@ -476,41 +681,52 @@ UpdateParser.prototype = {
return;
}
- let xml = request.responseXML;
- if (!xml || xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR) {
- logger.warn("Update manifest was not valid XML");
- this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
+ // Detect the manifest type by first attempting to parse it as
+ // JSON, and falling back to parsing it as XML if that fails.
+ let parser;
+ try {
+ try {
+ let json = JSON.parse(request.responseText);
+
+ parser = () => parseJSONManifest(this.id, this.updateKey, request, json);
+ } catch (e) {
+ if (!(e instanceof SyntaxError))
+ throw e;
+ let domParser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
+ let xml = domParser.parseFromString(request.responseText, "text/xml");
+
+ if (xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR)
+ throw new Error("Update manifest was not valid XML or JSON");
+
+ parser = () => parseRDFManifest(this.id, this.updateKey, request, xml);
+ }
+ } catch (e) {
+ logger.warn("onUpdateCheckComplete failed to determine manifest type");
+ this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT);
return;
}
- // We currently only know about RDF update manifests
- if (xml.documentElement.namespaceURI == PREFIX_NS_RDF) {
- let results = null;
+ let results;
+ try {
+ results = parser();
+ }
+ catch (e) {
+ logger.warn("onUpdateCheckComplete failed to parse update manifest", e);
+ this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
+ return;
+ }
+ if ("onUpdateCheckComplete" in this.observer) {
try {
- results = parseRDFManifest(this.id, this.updateKey, request);
+ this.observer.onUpdateCheckComplete(results);
}
catch (e) {
- logger.warn("onUpdateCheckComplete failed to parse RDF manifest", e);
- this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
- return;
- }
- if ("onUpdateCheckComplete" in this.observer) {
- try {
- this.observer.onUpdateCheckComplete(results);
- }
- catch (e) {
- logger.warn("onUpdateCheckComplete notification failed", e);
- }
+ logger.warn("onUpdateCheckComplete notification failed", e);
}
- else {
- logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
- }
- return;
}
-
- logger.warn("Update manifest had an unrecognised namespace: " + xml.documentElement.namespaceURI);
- this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT);
+ else {
+ logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
+ }
},
/**
@@ -526,7 +742,7 @@ UpdateParser.prototype = {
/**
* Called when the manifest failed to load.
*/
- onError: function UP_onError() {
+ onError: function() {
if (!Components.isSuccessCode(this.request.status)) {
logger.warn("Request failed: " + this.url + " - " + this.request.status);
}
@@ -555,7 +771,7 @@ UpdateParser.prototype = {
/**
* Helper method to notify the observer that an error occured.
*/
- notifyError: function UP_notifyError(aStatus) {
+ notifyError: function(aStatus) {
if ("onUpdateCheckError" in this.observer) {
try {
this.observer.onUpdateCheckError(aStatus);
@@ -569,7 +785,7 @@ UpdateParser.prototype = {
/**
* Called to cancel an in-progress update check.
*/
- cancel: function UP_cancel() {
+ cancel: function() {
if (!this.request) {
logger.error("Trying to cancel already-complete request", this._doneAt);
return;
@@ -669,12 +885,9 @@ this.AddonUpdateChecker = {
* Ignore strictCompatibility when testing if an update matches. Optional.
* @return an update object if one matches or null if not
*/
- getCompatibilityUpdate: function AUC_getCompatibilityUpdate(aUpdates, aVersion,
- aIgnoreCompatibility,
- aAppVersion,
- aPlatformVersion,
- aIgnoreMaxVersion,
- aIgnoreStrictCompat) {
+ getCompatibilityUpdate: function(aUpdates, aVersion, aIgnoreCompatibility,
+ aAppVersion, aPlatformVersion,
+ aIgnoreMaxVersion, aIgnoreStrictCompat) {
if (!aAppVersion)
aAppVersion = Services.appinfo.version;
if (!aPlatformVersion)
@@ -686,8 +899,8 @@ this.AddonUpdateChecker = {
for (let targetApp of update.targetApplications) {
let id = targetApp.id;
#ifdef MOZ_PHOENIX_EXTENSIONS
- if (id == Services.appinfo.ID || id == FIREFOX_ID ||
- id == TOOLKIT_ID)
+ if (id == Services.appinfo.ID || id == FIREFOX_ID ||
+ id == TOOLKIT_ID)
#else
if (id == Services.appinfo.ID || id == TOOLKIT_ID)
#endif
@@ -720,12 +933,9 @@ this.AddonUpdateChecker = {
* Array of AddonCompatibilityOverride to take into account. Optional.
* @return an update object if one matches or null if not
*/
- getNewestCompatibleUpdate: function AUC_getNewestCompatibleUpdate(aUpdates,
- aAppVersion,
- aPlatformVersion,
- aIgnoreMaxVersion,
- aIgnoreStrictCompat,
- aCompatOverrides) {
+ getNewestCompatibleUpdate: function(aUpdates, aAppVersion, aPlatformVersion,
+ aIgnoreMaxVersion, aIgnoreStrictCompat,
+ aCompatOverrides) {
if (!aAppVersion)
aAppVersion = Services.appinfo.version;
if (!aPlatformVersion)
@@ -765,10 +975,10 @@ this.AddonUpdateChecker = {
* @return UpdateParser so that the caller can use UpdateParser.cancel() to shut
* down in-progress update requests
*/
- checkForUpdates: function AUC_checkForUpdates(aId, aUpdateKey, aUrl, aObserver) {
+ checkForUpdates: function(aId, aUpdateKey, aUrl, aObserver) {
// Define an array of internally used IDs to NOT send to AUS such as the
// Default Theme. Please keep this list in sync with:
- // toolkit/mozapps/webextensions/AddonUpdateChecker.jsm
+ // toolkit/mozapps/extensions/AddonUpdateChecker.jsm
let internalIDS = [
'{972ce4c6-7e08-4474-a285-3208198ce6fd}',
'modern@themes.mozilla.org'
diff --git a/toolkit/mozapps/extensions/internal/Content.js b/toolkit/mozapps/extensions/internal/Content.js
index 9ab3b9ad6..9f366ba32 100644
--- a/toolkit/mozapps/extensions/internal/Content.js
+++ b/toolkit/mozapps/extensions/internal/Content.js
@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* globals addMessageListener*/
+
"use strict";
(function() {
@@ -14,17 +16,22 @@ var nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
"initWithPath");
const MSG_JAR_FLUSH = "AddonJarFlush";
+const MSG_MESSAGE_MANAGER_CACHES_FLUSH = "AddonMessageManagerCachesFlush";
try {
if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
- // Propagate JAR cache flush notifications across process boundaries.
- addMessageListener(MSG_JAR_FLUSH, function jar_flushMessageListener(message) {
+ // Propagate JAR cache flush notifications across process boundaries.
+ addMessageListener(MSG_JAR_FLUSH, function(message) {
let file = new nsIFile(message.data);
Services.obs.notifyObservers(file, "flush-cache-entry", null);
});
+ // Propagate message manager caches flush notifications across processes.
+ addMessageListener(MSG_MESSAGE_MANAGER_CACHES_FLUSH, function() {
+ Services.obs.notifyObservers(null, "message-manager-flush-caches", null);
+ });
}
-} catch(e) {
+} catch (e) {
Cu.reportError(e);
}
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
index b522bd3ae..9ea876f6c 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -96,8 +96,6 @@ const PREF_INTERPOSITION_ENABLED = "extensions.interposition.enabled";
const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
-const PREF_CHECKCOMAT_THEMEOVERRIDE = "extensions.checkCompatibility.temporaryThemeOverride_minAppVersion";
-
const URI_EXTENSION_SELECT_DIALOG = "chrome://mozapps/content/extensions/selectAddons.xul";
const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
@@ -662,22 +660,8 @@ function isUsableAddon(aAddon) {
return false;
}
else {
- let app = aAddon.matchingTargetApplication;
- if (!app)
+ if (!aAddon.matchingTargetApplication)
return false;
-
- // XXX Temporary solution to let applications opt-in to make themes safer
- // following significant UI changes even if checkCompatibility=false has
- // been set, until we get bug 962001.
- if (aAddon.type == "theme" && app.id == Services.appinfo.ID) {
- try {
- let minCompatVersion = Services.prefs.getCharPref(PREF_CHECKCOMAT_THEMEOVERRIDE);
- if (minCompatVersion &&
- Services.vc.compare(minCompatVersion, app.maxVersion) > 0) {
- return false;
- }
- } catch (e) {}
- }
}
return true;