summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/internal
diff options
context:
space:
mode:
authorjanekptacijarabaci <janekptacijarabaci@seznam.cz>2018-07-06 15:53:52 +0200
committerjanekptacijarabaci <janekptacijarabaci@seznam.cz>2018-07-06 15:53:52 +0200
commit941e54654eabed0a3568f7fefe424a45aa02eddb (patch)
tree49aa02b174c428962d99142d8061267bfcd79e69 /toolkit/mozapps/extensions/internal
parentad9ee72dcd7981bc47b3844a224d69fadfdfd8ef (diff)
parent0daa12376295d5d796256a116eb2a348a3a9273f (diff)
downloadUXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar
UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.gz
UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.lz
UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.tar.xz
UXP-941e54654eabed0a3568f7fefe424a45aa02eddb.zip
Merge branch 'master' of https://github.com/MoonchildProductions/UXP into _testBranch_test_1
Diffstat (limited to 'toolkit/mozapps/extensions/internal')
-rw-r--r--toolkit/mozapps/extensions/internal/AddonLogging.jsm28
-rw-r--r--toolkit/mozapps/extensions/internal/AddonRepository.jsm4
-rw-r--r--toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm8
-rw-r--r--toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm407
-rw-r--r--toolkit/mozapps/extensions/internal/Content.js19
-rw-r--r--toolkit/mozapps/extensions/internal/GMPProvider.jsm15
-rw-r--r--toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm37
-rw-r--r--toolkit/mozapps/extensions/internal/PluginProvider.jsm2
-rw-r--r--toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm467
-rw-r--r--toolkit/mozapps/extensions/internal/XPIProvider.jsm168
-rw-r--r--toolkit/mozapps/extensions/internal/XPIProviderUtils.js16
-rw-r--r--toolkit/mozapps/extensions/internal/moz.build2
12 files changed, 952 insertions, 221 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/AddonRepository.jsm b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
index adcecbee7..76a7528c7 100644
--- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -72,7 +72,7 @@ const LOGGER_ID = "addons.repository";
// Create a new logger for use by the Addons Repository
// (Requires AddonManager.jsm)
-let logger = Log.repository.getLogger(LOGGER_ID);
+var logger = Log.repository.getLogger(LOGGER_ID);
// A map between XML keys to AddonSearchResult keys for string values
// that require no extra parsing from XML
@@ -101,7 +101,7 @@ const INTEGER_KEY_MAP = {
};
// Wrap the XHR factory so that tests can override with a mock
-let XHRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1",
+var XHRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1",
"nsIXMLHttpRequest");
function convertHTMLToPlainText(html) {
diff --git a/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm b/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
index 128146bbe..66147b9aa 100644
--- a/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository_SQLiteMigrator.jsm
@@ -30,7 +30,7 @@ const LOGGER_ID = "addons.repository.sqlmigrator";
// Create a new logger for use by the Addons Repository SQL Migrator
// (Requires AddonManager.jsm)
-let logger = Log.repository.getLogger(LOGGER_ID);
+var logger = Log.repository.getLogger(LOGGER_ID);
this.EXPORTED_SYMBOLS = ["AddonRepository_SQLiteMigrator"];
@@ -60,7 +60,11 @@ this.AddonRepository_SQLiteMigrator = {
this._retrieveStoredData((results) => {
this._closeConnection();
- let resultArray = [addon for ([,addon] of Iterator(results))];
+ // Tycho: let resultArray = [addon for ([,addon] of Iterator(results))];
+ let resultArray = [];
+ for (let [,addon] of Iterator(results)) {
+ resultArray.push(addon);
+ }
logger.debug(resultArray.length + " addons imported.")
aCallback(resultArray);
});
diff --git a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
index d68a0f175..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;
@@ -52,7 +58,7 @@ const LOGGER_ID = "addons.update-checker";
// Create a new logger for use by the Addons Update Checker
// (Requires AddonManager.jsm)
-let logger = Log.repository.getLogger(LOGGER_ID);
+var logger = Log.repository.getLogger(LOGGER_ID);
/**
* A serialisation method for RDF data that produces an identical string
@@ -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, "&amp;");
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);
- }
- }
- else {
- logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
+ logger.warn("onUpdateCheckComplete notification failed", e);
}
- 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,9 +975,18 @@ 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) {
- // Exclude default theme
- if (aId != "{972ce4c6-7e08-4474-a285-3208198ce6fd}")
+ 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/extensions/AddonUpdateChecker.jsm
+ let internalIDS = [
+ '{972ce4c6-7e08-4474-a285-3208198ce6fd}',
+ 'modern@themes.mozilla.org'
+ ];
+
+ // If the ID is not in the array then go ahead and query AUS
+ if (internalIDS.indexOf(aId) == -1) {
return new UpdateParser(aId, aUpdateKey, aUrl, aObserver);
+ }
}
};
diff --git a/toolkit/mozapps/extensions/internal/Content.js b/toolkit/mozapps/extensions/internal/Content.js
index 29c0ed8ce..9f366ba32 100644
--- a/toolkit/mozapps/extensions/internal/Content.js
+++ b/toolkit/mozapps/extensions/internal/Content.js
@@ -2,29 +2,36 @@
* 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() {
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+var {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-let nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
+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/GMPProvider.jsm b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
index a55457f6e..25651f1b8 100644
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -4,9 +4,9 @@
"use strict";
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
this.EXPORTED_SYMBOLS = [];
@@ -58,17 +58,18 @@ const GMP_PLUGINS = [
optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul",
isEME: true
}];
+XPCOMUtils.defineConstant(this, "GMP_PLUGINS", GMP_PLUGINS);
XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
() => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
XPCOMUtils.defineLazyGetter(this, "gmpService",
() => Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(Ci.mozIGeckoMediaPluginChromeService));
-let messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
+var messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
-let gLogger;
-let gLogAppenderDump = null;
+var gLogger;
+var gLogAppenderDump = null;
function configureLogging() {
if (!gLogger) {
@@ -443,7 +444,7 @@ GMPWrapper.prototype = {
},
};
-let GMPProvider = {
+var GMPProvider = {
get name() { return "GMPProvider"; },
_plugins: null,
diff --git a/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
index fccde9a81..a9201c3da 100644
--- a/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
+++ b/toolkit/mozapps/extensions/internal/LightweightThemeImageOptimizer.jsm
@@ -49,7 +49,7 @@ this.LightweightThemeImageOptimizer = {
Object.freeze(LightweightThemeImageOptimizer);
-let ImageCropper = {
+var ImageCropper = {
_inProgress: {},
getCroppedImageURL:
@@ -59,10 +59,14 @@ let ImageCropper = {
return aImageURL;
}
- if (Services.prefs.getBoolPref("lightweightThemes.animation.enabled")) {
- //Don't crop if animated
- return aImageURL;
- }
+ try {
+ if (Services.prefs.getBoolPref("lightweightThemes.animation.enabled")) {
+ //Don't crop if animated
+ return aImageURL;
+ }
+ } catch(e) {
+ // Continue of pref doesn't exist.
+ }
// Generate the cropped image's file name using its
// base name and the current screen size.
@@ -119,23 +123,20 @@ let ImageCropper = {
}
};
-let ImageFile = {
- read: function ImageFile_read(aURI, aCallback) {
- this._netUtil.asyncFetch2(
- aURI,
- function read_asyncFetch(aInputStream, aStatus, aRequest) {
+var ImageFile = {
+ read: function(aURI, aCallback) {
+ this._netUtil.asyncFetch({
+ uri: aURI,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
+ }, function(aInputStream, aStatus, aRequest) {
if (Components.isSuccessCode(aStatus) && aRequest instanceof Ci.nsIChannel) {
let channel = aRequest.QueryInterface(Ci.nsIChannel);
aCallback(aInputStream, channel.contentType);
} else {
aCallback();
}
- },
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_IMAGE);
+ });
},
write: function ImageFile_write(aFile, aInputStream, aCallback) {
@@ -158,7 +159,7 @@ let ImageFile = {
XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil",
"resource://gre/modules/NetUtil.jsm", "NetUtil");
-let ImageTools = {
+var ImageTools = {
decode: function ImageTools_decode(aInputStream, aContentType) {
let outParam = {value: null};
@@ -187,7 +188,7 @@ let ImageTools = {
XPCOMUtils.defineLazyServiceGetter(ImageTools, "_imgTools",
"@mozilla.org/image/tools;1", "imgITools");
-let Utils = {
+var Utils = {
createCopy: function Utils_createCopy(aData) {
let copy = {};
for (let [k, v] in Iterator(aData)) {
diff --git a/toolkit/mozapps/extensions/internal/PluginProvider.jsm b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
index 04a4f9d7c..cb07dcb12 100644
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -23,7 +23,7 @@ const LOGGER_ID = "addons.plugins";
// Create a new logger for use by the Addons Plugin Provider
// (Requires AddonManager.jsm)
-let logger = Log.repository.getLogger(LOGGER_ID);
+var logger = Log.repository.getLogger(LOGGER_ID);
function getIDHashForString(aStr) {
// return the two-digit hexadecimal code for a byte
diff --git a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
new file mode 100644
index 000000000..f98dd2a94
--- /dev/null
+++ b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
@@ -0,0 +1,467 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+const LOCAL_EME_SOURCES = [{
+ "id": "gmp-eme-adobe",
+ "src": "chrome://global/content/gmp-sources/eme-adobe.json"
+}, {
+ "id": "gmp-gmpopenh264",
+ "src": "chrome://global/content/gmp-sources/openh264.json"
+}, {
+ "id": "gmp-widevinecdm",
+ "src": "chrome://global/content/gmp-sources/widevinecdm.json"
+}];
+
+this.EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
+
+Cu.importGlobalProperties(["XMLHttpRequest"]);
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://gre/modules/CertUtils.jsm");
+/* globals checkCert, BadCertHandler*/
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
+
+/* globals GMPPrefs */
+XPCOMUtils.defineLazyModuleGetter(this, "GMPPrefs",
+ "resource://gre/modules/GMPUtils.jsm");
+
+/* globals OS */
+
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+ "resource://gre/modules/UpdateUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest",
+ "resource://gre/modules/ServiceRequest.jsm");
+
+// This exists so that tests can override the XHR behaviour for downloading
+// the addon update XML file.
+var CreateXHR = function() {
+ return Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+ createInstance(Ci.nsISupports);
+}
+
+var logger = Log.repository.getLogger("addons.productaddons");
+
+/**
+ * Number of milliseconds after which we need to cancel `downloadXML`.
+ *
+ * Bug 1087674 suggests that the XHR we use in `downloadXML` may
+ * never terminate in presence of network nuisances (e.g. strange
+ * antivirus behavior). This timeout is a defensive measure to ensure
+ * that we fail cleanly in such case.
+ */
+const TIMEOUT_DELAY_MS = 20000;
+// Chunk size for the incremental downloader
+const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
+// Incremental downloader interval
+const DOWNLOAD_INTERVAL = 0;
+// How much of a file to read into memory at a time for hashing
+const HASH_CHUNK_SIZE = 8192;
+
+/**
+ * Gets the status of an XMLHttpRequest either directly or from its underlying
+ * channel.
+ *
+ * @param request
+ * The XMLHttpRequest.
+ * @return an integer status value.
+ */
+function getRequestStatus(request) {
+ let status = null;
+ try {
+ status = request.status;
+ }
+ catch (e) {
+ }
+
+ if (status != null) {
+ return status;
+ }
+
+ return request.channel.QueryInterface(Ci.nsIRequest).status;
+}
+
+/**
+ * Downloads an XML document from a URL optionally testing the SSL certificate
+ * for certain attributes.
+ *
+ * @param url
+ * The url to download from.
+ * @param allowNonBuiltIn
+ * Whether to trust SSL certificates without a built-in CA issuer.
+ * @param allowedCerts
+ * The list of certificate attributes to match the SSL certificate
+ * against or null to skip checks.
+ * @return a promise that resolves to the DOM document downloaded or rejects
+ * with a JS exception in case of error.
+ */
+function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
+ return new Promise((resolve, reject) => {
+ let request = CreateXHR();
+ // This is here to let unit test code override XHR
+ if (request.wrappedJSObject) {
+ request = request.wrappedJSObject;
+ }
+ request.open("GET", url, true);
+ request.channel.notificationCallbacks = new BadCertHandler(allowNonBuiltIn);
+ // Prevent the request from reading from the cache.
+ request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ // Prevent the request from writing to the cache.
+ request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+ // Use conservative TLS settings. See bug 1325501.
+ // TODO move to ServiceRequest.
+ if (request.channel instanceof Ci.nsIHttpChannelInternal) {
+ request.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative = true;
+ }
+ request.timeout = TIMEOUT_DELAY_MS;
+
+ request.overrideMimeType("text/xml");
+ // The Cache-Control header is only interpreted by proxies and the
+ // final destination. It does not help if a resource is already
+ // cached locally.
+ request.setRequestHeader("Cache-Control", "no-cache");
+ // HTTP/1.0 servers might not implement Cache-Control and
+ // might only implement Pragma: no-cache
+ request.setRequestHeader("Pragma", "no-cache");
+
+ let fail = (event) => {
+ let request = event.target;
+ let status = getRequestStatus(request);
+ let message = "Failed downloading XML, status: " + status + ", reason: " + event.type;
+ logger.warn(message);
+ let ex = new Error(message);
+ ex.status = status;
+ reject(ex);
+ };
+
+ let success = (event) => {
+ logger.info("Completed downloading document");
+ let request = event.target;
+
+ try {
+ checkCert(request.channel, allowNonBuiltIn, allowedCerts);
+ } catch (ex) {
+ logger.error("Request failed certificate checks: " + ex);
+ ex.status = getRequestStatus(request);
+ reject(ex);
+ return;
+ }
+
+ resolve(request.responseXML);
+ };
+
+ request.addEventListener("error", fail, false);
+ request.addEventListener("abort", fail, false);
+ request.addEventListener("timeout", fail, false);
+ request.addEventListener("load", success, false);
+
+ logger.info("sending request to: " + url);
+ request.send(null);
+ });
+}
+
+function downloadJSON(uri) {
+ logger.info("fetching config from: " + uri);
+ return new Promise((resolve, reject) => {
+ let xmlHttp = new ServiceRequest({mozAnon: true});
+
+ xmlHttp.onload = function(aResponse) {
+ resolve(JSON.parse(this.responseText));
+ };
+
+ xmlHttp.onerror = function(e) {
+ reject("Fetching " + uri + " results in error code: " + e.target.status);
+ };
+
+ xmlHttp.open("GET", uri);
+ xmlHttp.overrideMimeType("application/json");
+ xmlHttp.send();
+ });
+}
+
+
+/**
+ * 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 object containing
+ * an array of the addons listed and a field notifying whether the
+ * fallback was used.
+ */
+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;
+ }
+
+ 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"]) {
+ if (addonElement.hasAttribute(name)) {
+ addon[name] = addonElement.getAttribute(name);
+ }
+ }
+ addon.size = Number(addon.size) || undefined;
+
+ results.push(addon);
+ }
+
+ return {
+ usedFallback: false,
+ gmpAddons: results
+ };
+}
+
+/**
+ * If downloading from the network fails (AUS server is down),
+ * load the sources from local build configuration.
+ */
+function downloadLocalConfig() {
+
+ if (!GMPPrefs.get(GMPPrefs.KEY_UPDATE_ENABLED, true)) {
+ logger.info("Updates are disabled via media.gmp-manager.updateEnabled");
+ return Promise.resolve({usedFallback: true, gmpAddons: []});
+ }
+
+ return Promise.all(LOCAL_EME_SOURCES.map(conf => {
+ return downloadJSON(conf.src).then(addons => {
+
+ let platforms = addons.vendors[conf.id].platforms;
+ let target = Services.appinfo.OS + "_" + UpdateUtils.ABI;
+ let details = null;
+
+ while (!details) {
+ if (!(target in platforms)) {
+ // There was no matching platform so return false, this addon
+ // will be filtered from the results below
+ logger.info("no details found for: " + target);
+ return false;
+ }
+ // Field either has the details of the binary or is an alias
+ // to another build target key that does
+ if (platforms[target].alias) {
+ target = platforms[target].alias;
+ } else {
+ details = platforms[target];
+ }
+ }
+
+ logger.info("found plugin: " + conf.id);
+ return {
+ "id": conf.id,
+ "URL": details.fileUrl,
+ "hashFunction": addons.hashFunction,
+ "hashValue": details.hashValue,
+ "version": addons.vendors[conf.id].version,
+ "size": details.filesize
+ };
+ });
+ })).then(addons => {
+
+ // Some filters may not match this platform so
+ // filter those out
+ addons = addons.filter(x => x !== false);
+
+ return {
+ usedFallback: true,
+ gmpAddons: addons
+ };
+ });
+}
+
+/**
+ * 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
+ * with a JS exception in case of error.
+ */
+function downloadFile(url) {
+ return new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ xhr.onload = function(response) {
+ logger.info("downloadXHR File download. status=" + xhr.status);
+ if (xhr.status != 200 && xhr.status != 206) {
+ reject(Components.Exception("File download failed", xhr.status));
+ return;
+ }
+ Task.spawn(function* () {
+ let f = yield OS.File.openUnique(OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon"));
+ let path = f.path;
+ logger.info(`Downloaded file will be saved to ${path}`);
+ yield f.file.close();
+ yield OS.File.writeAtomic(path, new Uint8Array(xhr.response));
+ return path;
+ }).then(resolve, reject);
+ };
+
+ let fail = (event) => {
+ let request = event.target;
+ let status = getRequestStatus(request);
+ let message = "Failed downloading via XHR, status: " + status + ", reason: " + event.type;
+ logger.warn(message);
+ let ex = new Error(message);
+ ex.status = status;
+ reject(ex);
+ };
+ xhr.addEventListener("error", fail);
+ xhr.addEventListener("abort", fail);
+
+ xhr.responseType = "arraybuffer";
+ try {
+ xhr.open("GET", url);
+ // Use conservative TLS settings. See bug 1325501.
+ // TODO move to ServiceRequest.
+ if (xhr.channel instanceof Ci.nsIHttpChannelInternal) {
+ xhr.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative = true;
+ }
+ xhr.send(null);
+ } catch (ex) {
+ reject(ex);
+ }
+ });
+}
+
+/**
+ * Convert a string containing binary values to hex.
+ */
+function binaryToHex(input) {
+ let result = "";
+ for (let i = 0; i < input.length; ++i) {
+ let hex = input.charCodeAt(i).toString(16);
+ if (hex.length == 1) {
+ hex = "0" + hex;
+ }
+ result += hex;
+ }
+ return result;
+}
+
+/**
+ * Calculates the hash of a file.
+ *
+ * @param hashFunction
+ * The type of hash function to use, must be supported by nsICryptoHash.
+ * @param path
+ * The path of the file to hash.
+ * @return a promise that resolves to hash of the file or rejects with a JS
+ * exception in case of error.
+ */
+var computeHash = Task.async(function*(hashFunction, path) {
+ let file = yield OS.File.open(path, { existing: true, read: true });
+ try {
+ let hasher = Cc["@mozilla.org/security/hash;1"].
+ createInstance(Ci.nsICryptoHash);
+ hasher.initWithString(hashFunction);
+
+ let bytes;
+ do {
+ bytes = yield file.read(HASH_CHUNK_SIZE);
+ hasher.update(bytes, bytes.length);
+ } while (bytes.length == HASH_CHUNK_SIZE);
+
+ return binaryToHex(hasher.finish(false));
+ }
+ finally {
+ yield file.close();
+ }
+});
+
+/**
+ * Verifies that a downloaded file matches what was expected.
+ *
+ * @param properties
+ * The properties to check, `size` and `hashFunction` with `hashValue`
+ * are supported. Any properties missing won't be checked.
+ * @param path
+ * The path of the file to check.
+ * @return a promise that resolves if the file matched or rejects with a JS
+ * exception in case of error.
+ */
+var verifyFile = Task.async(function*(properties, path) {
+ if (properties.size !== undefined) {
+ let stat = yield OS.File.stat(path);
+ if (stat.size != properties.size) {
+ throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
+ }
+ }
+
+ if (properties.hashFunction !== undefined) {
+ let expectedDigest = properties.hashValue.toLowerCase();
+ let digest = yield computeHash(properties.hashFunction, path);
+ if (digest != expectedDigest) {
+ throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest + "`.");
+ }
+ }
+});
+
+const ProductAddonChecker = {
+ /**
+ * Downloads a list of add-ons from a URL optionally testing the SSL
+ * certificate for certain attributes.
+ *
+ * @param url
+ * The url to download from.
+ * @param allowNonBuiltIn
+ * Whether to trust SSL certificates without a built-in CA issuer.
+ * @param allowedCerts
+ * The list of certificate attributes to match the SSL certificate
+ * against or null to skip checks.
+ * @return a promise that resolves to an object containing the list of add-ons
+ * and whether the local fallback was used, or rejects with a JS
+ * exception in case of error.
+ */
+ getProductAddonList: function(url, allowNonBuiltIn = false, allowedCerts = null) {
+ if (!GMPPrefs.get(GMPPrefs.KEY_UPDATE_ENABLED, true)) {
+ logger.info("Updates are disabled via media.gmp-manager.updateEnabled");
+ return Promise.resolve({usedFallback: true, gmpAddons: []});
+ }
+
+ return downloadXML(url, allowNonBuiltIn, allowedCerts)
+ .then(parseXML)
+ .catch(downloadLocalConfig);
+ },
+
+ /**
+ * Downloads an add-on to a local file and checks that it matches the expected
+ * file. The caller is responsible for deleting the temporary file returned.
+ *
+ * @param addon
+ * The addon to download.
+ * @return a promise that resolves to the temporary file downloaded or rejects
+ * with a JS exception in case of error.
+ */
+ downloadAddon: Task.async(function*(addon) {
+ let path = yield downloadFile(addon.URL);
+ try {
+ yield verifyFile(addon, path);
+ return path;
+ }
+ catch (e) {
+ yield OS.File.remove(path);
+ throw e;
+ }
+ })
+}
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
index d5f1ab5dd..9ea876f6c 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -36,8 +36,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
+#ifdef MOZ_DEVTOOLS
XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
"resource://devtools/client/framework/ToolboxProcess.jsm");
+#endif
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
"resource://gre/modules/Console.jsm");
@@ -52,6 +54,10 @@ XPCOMUtils.defineLazyServiceGetter(this,
"ResProtocolHandler",
"@mozilla.org/network/protocol;1?name=resource",
"nsIResProtocolHandler");
+XPCOMUtils.defineLazyServiceGetter(this,
+ "AddonPathService",
+ "@mozilla.org/addon-path-service;1",
+ "amIAddonPathService");
const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
"initWithPath");
@@ -90,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";
@@ -106,6 +110,10 @@ const DIR_TRASH = "trash";
const FILE_DATABASE = "extensions.json";
const FILE_OLD_CACHE = "extensions.cache";
const FILE_INSTALL_MANIFEST = "install.rdf";
+#ifndef MOZ_JETPACK
+const FILE_JETPACK_MANIFEST_1 = "harness-options.json";
+const FILE_JETPACK_MANIFEST_2 = "package.json";
+#endif
const FILE_WEBEXT_MANIFEST = "manifest.json";
const FILE_XPI_ADDONS_LIST = "extensions.ini";
@@ -129,12 +137,15 @@ const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
const TOOLKIT_ID = "toolkit@mozilla.org";
#ifdef MOZ_PHOENIX_EXTENSIONS
const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
-const FIREFOX_APPCOMPATVERSION = "27.9"
+const FIREFOX_APPCOMPATVERSION = "56.9"
#endif
// The value for this is in Makefile.in
#expand const DB_SCHEMA = __MOZ_EXTENSIONS_DB_SCHEMA__;
+XPCOMUtils.defineConstant(this, "DB_SCHEMA", DB_SCHEMA);
+#ifdef MOZ_DEVTOOLS
const NOTIFICATION_TOOLBOXPROCESS_LOADED = "ToolboxProcessLoaded";
+#endif
// Properties that exist in the install manifest
const PROP_METADATA = ["id", "version", "type", "internalName", "updateURL",
@@ -222,7 +233,7 @@ const LOGGER_ID = "addons.xpi";
// Create a new logger for use by all objects in this Addons XPI Provider module
// (Requires AddonManager.jsm)
-let logger = Log.repository.getLogger(LOGGER_ID);
+var logger = Log.repository.getLogger(LOGGER_ID);
const LAZY_OBJECTS = ["XPIDatabase"];
@@ -649,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;
@@ -1059,37 +1056,36 @@ function loadManifestFromDir(aDir) {
* @throws if the XPI file does not contain a valid install manifest.
* Throws with |webext:true| if a WebExtension manifest was found
* to distinguish between WebExtensions and corrupt files.
+ * Throws with |jetpacksdk:true| if a Jetpack files were found
+ * if Jetpack its self isn't built.
*/
function loadManifestFromZipReader(aZipReader) {
- let zis;
- try {
- zis = aZipReader.getInputStream(FILE_INSTALL_MANIFEST);
- } catch (e) {
- // We're going to throw here, but depending on whether we have a
- // WebExtension manifest in the XPI, we'll throw with the webext flag.
- try {
- let zws = aZipReader.getInputStream(FILE_WEBEXT_MANIFEST);
- zws.close();
- } catch(e2) {
- // We have neither an install manifest nor a WebExtension manifest;
- // this means the extension file has a structural problem.
- // Just pass the original error up the chain in that case.
+ // If WebExtension but not install.rdf throw an error
+ if (aZipReader.hasEntry(FILE_WEBEXT_MANIFEST)) {
+ if (!aZipReader.hasEntry(FILE_INSTALL_MANIFEST)) {
throw {
- name: e.name,
- message: e.message
+ name: "UnsupportedExtension",
+ message: Services.appinfo.name + " does not support WebExtensions",
+ webext: true
};
}
- // If we get here, we have a WebExtension manifest but no install
- // manifest. Pass the error up the chain with the webext flag.
+ }
+
+#ifndef MOZ_JETPACK
+ // If Jetpack is not built throw an error
+ if (aZipReader.hasEntry(FILE_JETPACK_MANIFEST_1) ||
+ aZipReader.hasEntry(FILE_JETPACK_MANIFEST_2)) {
throw {
- name: e.name,
- message: e.message,
- webext: true
+ name: "UnsupportedExtension",
+ message: Services.appinfo.name + " does not support Jetpack Extensions",
+ jetpacksdk: true
};
}
-
- // We found an install manifest, so it's either a regular or hybrid
- // extension. Continue processing.
+#endif
+
+ // Attempt to open install.rdf else throw normally
+ let zis = aZipReader.getInputStream(FILE_INSTALL_MANIFEST);
+ // Create a buffered input stream for install.rdf
let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
createInstance(Ci.nsIBufferedInputStream);
bis.init(zis, 4096);
@@ -1118,7 +1114,9 @@ function loadManifestFromZipReader(aZipReader) {
return addon;
}
finally {
+ // Close the buffered input stream
bis.close();
+ // Close the input stream to install.rdf
zis.close();
}
}
@@ -1842,8 +1840,10 @@ this.XPIProvider = {
_enabledExperiments: null,
// A Map from an add-on install to its ID
_addonFileMap: new Map(),
+#ifdef MOZ_DEVTOOLS
// Flag to know if ToolboxProcess.jsm has already been loaded by someone or not
_toolboxProcessLoaded: false,
+#endif
// Have we started shutting down bootstrap add-ons?
_closing: false,
@@ -1887,8 +1887,7 @@ this.XPIProvider = {
logger.info("Mapping " + aID + " to " + aFile.path);
this._addonFileMap.set(aID, aFile.path);
- let service = Cc["@mozilla.org/addon-path-service;1"].getService(Ci.amIAddonPathService);
- service.insertPath(aFile.path, aID);
+ AddonPathService.insertPath(aFile.path, aID);
},
/**
@@ -1931,12 +1930,10 @@ this.XPIProvider = {
let chan;
try {
- chan = Services.io.newChannelFromURI2(aURI,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ chan = NetUtil.newChannel({
+ uri: aURI,
+ loadUsingSystemPrincipal: true
+ });
}
catch (ex) {
return null;
@@ -2082,6 +2079,8 @@ this.XPIProvider = {
Services.prefs.addObserver(PREF_EM_MIN_COMPAT_APP_VERSION, this, false);
Services.prefs.addObserver(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, this, false);
Services.obs.addObserver(this, NOTIFICATION_FLUSH_PERMISSIONS, false);
+
+#ifdef MOZ_DEVTOOLS
if (Cu.isModuleLoaded("resource://devtools/client/framework/ToolboxProcess.jsm")) {
// If BrowserToolboxProcess is already loaded, set the boolean to true
// and do whatever is needed
@@ -2093,6 +2092,7 @@ this.XPIProvider = {
// Else, wait for it to load
Services.obs.addObserver(this, NOTIFICATION_TOOLBOXPROCESS_LOADED, false);
}
+#endif
let flushCaches = this.checkForChanges(aAppChanged, aOldAppVersion,
aOldPlatformVersion);
@@ -3918,16 +3918,8 @@ this.XPIProvider = {
* @see amIAddonManager.mapURIToAddonID
*/
mapURIToAddonID: function XPI_mapURIToAddonID(aURI) {
- let resolved = this._resolveURIToFile(aURI);
- if (!resolved || !(resolved instanceof Ci.nsIFileURL))
- return null;
-
- for (let [id, path] of this._addonFileMap) {
- if (resolved.file.path.startsWith(path))
- return id;
- }
-
- return null;
+ // Returns `null` instead of empty string if the URI can't be mapped.
+ return AddonPathService.mapURIToAddonId(aURI) || null;
},
/**
@@ -4095,12 +4087,14 @@ this.XPIProvider = {
}
return;
}
+#ifdef MOZ_DEVTOOLS
else if (aTopic == NOTIFICATION_TOOLBOXPROCESS_LOADED) {
Services.obs.removeObserver(this, NOTIFICATION_TOOLBOXPROCESS_LOADED, false);
this._toolboxProcessLoaded = true;
BrowserToolboxProcess.on("connectionchange",
this.onDebugConnectionChange.bind(this));
}
+#endif
if (aTopic == "nsPref:changed") {
switch (aData) {
@@ -4365,12 +4359,14 @@ this.XPIProvider = {
logger.warn("Error loading bootstrap.js for " + aId, e);
}
+#ifdef MOZ_DEVTOOLS
// Only access BrowserToolboxProcess if ToolboxProcess.jsm has been
// initialized as otherwise, when it will be initialized, all addons'
// globals will be added anyways
if (this._toolboxProcessLoaded) {
BrowserToolboxProcess.setAddonOptions(aId, { global: this.bootstrapScopes[aId] });
}
+#endif
},
/**
@@ -4390,11 +4386,13 @@ this.XPIProvider = {
this.persistBootstrappedAddons();
this.addAddonsToCrashReporter();
+#ifdef MOZ_DEVTOOLS
// Only access BrowserToolboxProcess if ToolboxProcess.jsm has been
// initialized as otherwise, there won't be any addon globals added to it
if (this._toolboxProcessLoaded) {
BrowserToolboxProcess.setAddonOptions(aId, { global: null });
}
+#endif
},
/**
@@ -4438,7 +4436,18 @@ this.XPIProvider = {
if (aAddon.type == "locale")
return;
- if (!(aMethod in this.bootstrapScopes[aAddon.id])) {
+ let method = undefined;
+ try {
+ method = Components.utils.evalInSandbox(`${aMethod};`,
+ this.bootstrapScopes[aAddon.id],
+ "ECMAv5");
+ }
+ catch (e) {
+ // An exception will be caught if the expected method is not defined.
+ // That will be logged below.
+ }
+
+ if (!method) {
logger.warn("Add-on " + aAddon.id + " is missing bootstrap method " + aMethod);
return;
}
@@ -4457,9 +4466,9 @@ this.XPIProvider = {
}
logger.debug("Calling bootstrap method " + aMethod + " on " + aAddon.id + " version " +
- aAddon.version);
+ aAddon.version);
try {
- this.bootstrapScopes[aAddon.id][aMethod](params, aReason);
+ method(params, aReason);
}
catch (e) {
logger.warn("Exception running bootstrap method " + aMethod + " on " + aAddon.id, e);
@@ -5001,6 +5010,11 @@ AddonInstall.prototype = {
if (e.webext) {
logger.warn("WebExtension XPI", e);
this.error = AddonManager.ERROR_WEBEXT_FILE;
+#ifndef MOZ_JETPACK
+ } else if (e.jetpacksdk) {
+ logger.warn("Jetpack XPI", e);
+ this.error = AddonManager.ERROR_JETPACKSDK_FILE;
+#endif
} else {
logger.warn("Invalid XPI", e);
this.error = AddonManager.ERROR_CORRUPT_FILE;
@@ -5456,21 +5470,17 @@ AddonInstall.prototype = {
let requireBuiltIn = Preferences.get(PREF_INSTALL_REQUIREBUILTINCERTS, true);
this.badCertHandler = new BadCertHandler(!requireBuiltIn);
- this.channel = NetUtil.newChannel2(this.sourceURI,
- null,
- null,
- null, // aLoadingNode
- Services.scriptSecurityManager.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_NORMAL,
- Ci.nsIContentPolicy.TYPE_OTHER);
+ this.channel = NetUtil.newChannel({
+ uri: this.sourceURI,
+ loadUsingSystemPrincipal: true
+ });
this.channel.notificationCallbacks = this;
if (this.channel instanceof Ci.nsIHttpChannel) {
this.channel.setRequestHeader("Moz-XPI-Update", "1", true);
if (this.channel instanceof Ci.nsIHttpChannelInternal)
this.channel.forceAllowThirdPartyCookie = true;
}
- this.channel.asyncOpen(listener, null);
+ this.channel.asyncOpen2(listener);
Services.obs.addObserver(this, "network:offline-about-to-go-offline", false);
}
@@ -5646,6 +5656,10 @@ AddonInstall.prototype = {
catch (e) {
if (e.webext) {
this.downloadFailed(AddonManager.ERROR_WEBEXT_FILE, e);
+#ifndef MOZ_JETPACK
+ } else if (e.jetpacksdk) {
+ this.downloadFailed(AddonManager.ERROR_JETPACKSDK_FILE, e);
+#endif
} else {
this.downloadFailed(AddonManager.ERROR_CORRUPT_FILE, e);
}
@@ -6442,21 +6456,23 @@ AddonInternal.prototype = {
if (!aPlatformVersion)
aPlatformVersion = Services.appinfo.platformVersion;
+#ifdef MOZ_PHOENIX_EXTENSIONS
this.native = false;
-
+#endif
+
let version;
if (app.id == Services.appinfo.ID) {
version = aAppVersion;
+#ifdef MOZ_PHOENIX_EXTENSIONS
this.native = true;
}
-#ifdef MOZ_PHOENIX_EXTENSIONS
else if (app.id == FIREFOX_ID) {
version = FIREFOX_APPCOMPATVERSION;
if (this.type == "locale")
//Never allow language packs in Firefox compatibility mode
return false;
- }
#endif
+ }
else if (app.id == TOOLKIT_ID)
version = aPlatformVersion
@@ -7822,7 +7838,7 @@ WinRegInstallLocation.prototype = {
};
#endif
-let addonTypes = [
+var addonTypes = [
new AddonManagerPrivate.AddonType("extension", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 4000,
diff --git a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
index 2cef907f1..f2420bd1a 100644
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -4,10 +4,10 @@
"use strict";
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@@ -29,7 +29,7 @@ const LOGGER_ID = "addons.xpi-utils";
// Create a new logger for use by the Addons XPI Provider Utils
// (Requires AddonManager.jsm)
-let logger = Log.repository.getLogger(LOGGER_ID);
+var logger = Log.repository.getLogger(LOGGER_ID);
const KEY_PROFILEDIR = "ProfD";
const FILE_DATABASE = "extensions.sqlite";
@@ -70,7 +70,11 @@ const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
"skinnable", "size", "sourceURI", "releaseNotesURI",
"softDisabled", "foreignInstall", "hasBinaryComponents",
"strictCompatibility", "locales", "targetApplications",
- "targetPlatforms", "multiprocessCompatible", "native"];
+ "targetPlatforms", "multiprocessCompatible",
+#ifdef MOZ_PHOENIX_EXTENSIONS
+ "native"
+#endif
+ ];
// Time to wait before async save of XPI JSON database, in milliseconds
const ASYNC_SAVE_DELAY_MS = 20;
diff --git a/toolkit/mozapps/extensions/internal/moz.build b/toolkit/mozapps/extensions/internal/moz.build
index efcd2af21..337df3104 100644
--- a/toolkit/mozapps/extensions/internal/moz.build
+++ b/toolkit/mozapps/extensions/internal/moz.build
@@ -5,12 +5,12 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES.addons += [
- '../../webextensions/internal/ProductAddonChecker.jsm',
'AddonLogging.jsm',
'AddonRepository_SQLiteMigrator.jsm',
'Content.js',
'GMPProvider.jsm',
'LightweightThemeImageOptimizer.jsm',
+ 'ProductAddonChecker.jsm',
'SpellCheckDictionaryBootstrap.js',
]