Bug 1380278 - UpdateUtils.getLocale to Fetch API for async I/O. r?florian, r?whimboo
The NetUtils sync I/O shows up in the profile logs, so we want to switch the
function that loads `update.locale` file to be async.
MozReview-Commit-ID: AEYKiivsNl0
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
@@ -371,20 +371,27 @@ class SoftwareUpdate(BaseLib):
def get_formatted_update_url(self, force=False):
"""Retrieve the formatted AUS update URL the update snippet is retrieved from.
:param force: Boolean flag to force an update check
:returns: The URL of the update snippet
"""
- # Format the URL by replacing placeholders
- url = self.marionette.execute_script("""
- Components.utils.import("resource://gre/modules/UpdateUtils.jsm")
- return UpdateUtils.formatUpdateURL(arguments[0]);
+ url = self.marionette.execute_async_script("""
+ Components.utils.import("resource://gre/modules/UpdateUtils.jsm");
+ let res = UpdateUtils.formatUpdateURL(arguments[0]);
+ // Format the URL by replacing placeholders
+ // In 56 we switched the method to be async.
+ // For now, support both approaches.
+ if (res.then) {
+ res.then(marionetteScriptFinished);
+ } else {
+ marionetteScriptFinished(res);
+ }
""", script_args=[self.update_url])
if force:
if '?' in url:
url += '&'
else:
url += '?'
url += 'force=1'
--- a/toolkit/modules/GMPInstallManager.jsm
+++ b/toolkit/modules/GMPInstallManager.jsm
@@ -54,79 +54,79 @@ function GMPInstallManager() {
}
/**
* Temp file name used for downloading
*/
GMPInstallManager.prototype = {
/**
* Obtains a URL with replacement of vars
*/
- _getURL() {
+ async _getURL() {
let log = getScopedLogger("GMPInstallManager._getURL");
// Use the override URL if it is specified. The override URL is just like
// the normal URL but it does not check the cert.
let url = GMPPrefs.getString(GMPPrefs.KEY_URL_OVERRIDE, "");
if (url) {
log.info("Using override url: " + url);
} else {
url = GMPPrefs.getString(GMPPrefs.KEY_URL);
log.info("Using url: " + url);
}
- url = UpdateUtils.formatUpdateURL(url);
+ url = await UpdateUtils.formatUpdateURL(url);
log.info("Using url (with replacement): " + url);
return url;
},
/**
* Performs an addon check.
* @return a promise which will be resolved or rejected.
* The promise is resolved with an object with properties:
* gmpAddons: array of GMPAddons
* usedFallback: whether the data was collected from online or
* from fallback data within the build
* The promise is rejected with an object with properties:
* target: The XHR request object
* status: The HTTP status code
* type: Sometimes specifies type of rejection
*/
- checkForAddons() {
+ async checkForAddons() {
let log = getScopedLogger("GMPInstallManager.checkForAddons");
if (this._deferred) {
log.error("checkForAddons already called");
return Promise.reject({type: "alreadycalled"});
}
this._deferred = PromiseUtils.defer();
- let url = this._getURL();
let allowNonBuiltIn = true;
let certs = null;
if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE)) {
allowNonBuiltIn = !GMPPrefs.getString(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true);
if (GMPPrefs.getBool(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
}
}
+ let url = await this._getURL();
+
let addonPromise = ProductAddonChecker
- .getProductAddonList(url, allowNonBuiltIn, certs);
+ .getProductAddonList(url, allowNonBuiltIn, certs);
addonPromise.then(res => {
if (!res || !res.gmpAddons) {
this._deferred.resolve({gmpAddons: []});
} else {
res.gmpAddons = res.gmpAddons.map(a => new GMPAddon(a));
this._deferred.resolve(res);
}
delete this._deferred;
}, (ex) => {
this._deferred.reject(ex);
delete this._deferred;
});
-
return this._deferred.promise;
},
/**
* Installs the specified addon and calls a callback when done.
* @param gmpAddon The GMPAddon object to install
* @return a promise which will be resolved or rejected
* The promise will resolve with an array of paths that were extracted
* The promise will reject with an error object:
--- a/toolkit/modules/UpdateUtils.jsm
+++ b/toolkit/modules/UpdateUtils.jsm
@@ -4,26 +4,28 @@
this.EXPORTED_SYMBOLS = ["UpdateUtils"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.importGlobalProperties(["fetch"]); /* globals fetch */
const FILE_UPDATE_LOCALE = "update.locale";
const PREF_APP_DISTRIBUTION = "distribution.id";
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
const PREF_APP_UPDATE_CUSTOM = "app.update.custom";
this.UpdateUtils = {
+ _locale: undefined,
+
/**
* Read the update channel from defaults only. We do this to ensure that
* the channel is tightly coupled with the application and does not apply
* to other instances of the application that may use the same profile.
*
* @param [optional] aIncludePartners
* Whether or not to include the partner bits. Default: true.
*/
@@ -56,83 +58,87 @@ this.UpdateUtils = {
/**
* Formats a URL by replacing %...% values with OS, build and locale specific
* values.
*
* @param url
* The URL to format.
* @return The formatted URL.
*/
- formatUpdateURL(url) {
+ async formatUpdateURL(url) {
+ const locale = await this.getLocale();
+
return url.replace(/%(\w+)%/g, (match, name) => {
switch (name) {
case "PRODUCT":
return Services.appinfo.name;
case "VERSION":
return Services.appinfo.version;
case "BUILD_ID":
return Services.appinfo.appBuildID;
case "BUILD_TARGET":
return Services.appinfo.OS + "_" + this.ABI;
case "OS_VERSION":
return this.OSVersion;
case "LOCALE":
- return this.Locale;
+ return locale;
case "CHANNEL":
return this.UpdateChannel;
case "PLATFORM_VERSION":
return Services.appinfo.platformVersion;
case "SYSTEM_CAPABILITIES":
return getSystemCapabilities();
case "CUSTOM":
return Services.prefs.getStringPref(PREF_APP_UPDATE_CUSTOM, "");
case "DISTRIBUTION":
return getDistributionPrefValue(PREF_APP_DISTRIBUTION);
case "DISTRIBUTION_VERSION":
return getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION);
}
return match;
}).replace(/\+/g, "%2B");
+ },
+
+ /**
+ * Gets the locale from the update.locale file for replacing %LOCALE% in the
+ * update url. The update.locale file can be located in the application
+ * directory or the GRE directory with preference given to it being located in
+ * the application directory.
+ */
+ async getLocale() {
+ if (this._locale !== undefined) {
+ return this._locale;
+ }
+
+ for (let res of ["app", "gre"]) {
+ const url = "resource://" + res + "/" + FILE_UPDATE_LOCALE;
+ let data;
+ try {
+ data = await fetch(url);
+ } catch (e) {
+ continue;
+ }
+ const locale = await data.text();
+ if (locale) {
+ return this._locale = locale.trim();
+ }
+ }
+
+ Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " +
+ "application or GRE directories");
+
+ return this._locale = null;
}
};
/* Get the distribution pref values, from defaults only */
function getDistributionPrefValue(aPrefName) {
return Services.prefs.getDefaultBranch(null).getCharPref(aPrefName, "default");
}
-/**
- * Gets the locale from the update.locale file for replacing %LOCALE% in the
- * update url. The update.locale file can be located in the application
- * directory or the GRE directory with preference given to it being located in
- * the application directory.
- */
-XPCOMUtils.defineLazyGetter(UpdateUtils, "Locale", function() {
- let channel;
- let locale;
- for (let res of ["app", "gre"]) {
- channel = NetUtil.newChannel({
- uri: "resource://" + res + "/" + FILE_UPDATE_LOCALE,
- contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST,
- loadUsingSystemPrincipal: true
- });
- try {
- let inputStream = channel.open2();
- locale = NetUtil.readInputStreamToString(inputStream, inputStream.available());
- } catch (e) {}
- if (locale)
- return locale.trim();
- }
-
- Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " +
- "application or GRE directories");
-
- return null;
-});
-
function getSystemCapabilities() {
return gInstructionSet + "," + getMemoryMB();
}
/**
* Gets the RAM size in megabytes. This will round the value because sysinfo
* doesn't always provide RAM in multiples of 1024.
*/
--- a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
+++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
@@ -2,16 +2,17 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} = Components;
const URL_HOST = "http://localhost";
var GMPScope = Cu.import("resource://gre/modules/GMPInstallManager.jsm", {});
var GMPInstallManager = GMPScope.GMPInstallManager;
+Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Preferences.jsm")
Cu.import("resource://gre/modules/UpdateUtils.jsm");
var ProductAddonCheckerScope = Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm", {});
@@ -141,17 +142,26 @@ add_test(function test_checkForAddons_40
/**
* Tests that a xhr abort() works as expected
*/
add_test(function test_checkForAddons_abort() {
let overriddenXhr = overrideXHR(200, "", { dropRequest: true} );
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
- overriddenXhr.abort();
+
+ // Since the XHR is created in checkForAddons asynchronously,
+ // we need to delay aborting till the XHR is running.
+ // Since checkForAddons returns a Promise already only after
+ // the abort is triggered, we can't use that, and instead
+ // we'll use a fake timer.
+ setTimeout(() => {
+ overriddenXhr.abort();
+ }, 100);
+
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
--- a/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js
+++ b/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js
@@ -177,39 +177,39 @@ function getMemoryMB() {
} catch (e) {
do_throw("Error getting system info memsize property. Exception: " + e);
}
return memoryMB;
}
// Helper function for formatting a url and getting the result we're
// interested in
-function getResult(url) {
- url = UpdateUtils.formatUpdateURL(url);
+async function getResult(url) {
+ url = await UpdateUtils.formatUpdateURL(url);
return url.substr(URL_PREFIX.length).split("/")[0];
}
// url constructed with %PRODUCT%
add_task(async function test_product() {
let url = URL_PREFIX + "%PRODUCT%/";
- Assert.equal(getResult(url), gAppInfo.name,
+ Assert.equal(await getResult(url), gAppInfo.name,
"the url param for %PRODUCT%" + MSG_SHOULD_EQUAL);
});
// url constructed with %VERSION%
add_task(async function test_version() {
let url = URL_PREFIX + "%VERSION%/";
- Assert.equal(getResult(url), gAppInfo.version,
+ Assert.equal(await getResult(url), gAppInfo.version,
"the url param for %VERSION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %BUILD_ID%
add_task(async function test_build_id() {
let url = URL_PREFIX + "%BUILD_ID%/";
- Assert.equal(getResult(url), gAppInfo.appBuildID,
+ Assert.equal(await getResult(url), gAppInfo.appBuildID,
"the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL);
});
// url constructed with %BUILD_TARGET%
// XXX TODO - it might be nice if we tested the actual ABI
add_task(async function test_build_target() {
let url = URL_PREFIX + "%BUILD_TARGET%/";
@@ -230,57 +230,57 @@ add_task(async function test_build_targe
if (macutils.isUniversalBinary) {
abi += "-u-" + macutils.architecturesInBinary;
}
} else if (AppConstants.platform == "win") {
// Windows build should report the CPU architecture that it's running on.
abi += "-" + getProcArchitecture();
}
- Assert.equal(getResult(url), gAppInfo.OS + "_" + abi,
+ Assert.equal(await getResult(url), gAppInfo.OS + "_" + abi,
"the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL);
});
// url constructed with %LOCALE%
// Bug 488936 added the update.locale file that stores the update locale
add_task(async function test_locale() {
// The code that gets the locale accesses the profile which is only available
// after calling do_get_profile in xpcshell tests. This prevents an error from
// being logged.
do_get_profile();
let url = URL_PREFIX + "%LOCALE%/";
- Assert.equal(getResult(url), AppConstants.INSTALL_LOCALE,
+ Assert.equal(await getResult(url), AppConstants.INSTALL_LOCALE,
"the url param for %LOCALE%" + MSG_SHOULD_EQUAL);
});
// url constructed with %CHANNEL%
add_task(async function test_channel() {
let url = URL_PREFIX + "%CHANNEL%/";
setUpdateChannel("test_channel");
- Assert.equal(getResult(url), "test_channel",
+ Assert.equal(await getResult(url), "test_channel",
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
});
// url constructed with %CHANNEL% with distribution partners
add_task(async function test_channel_distribution() {
let url = URL_PREFIX + "%CHANNEL%/";
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner1",
"test_partner1");
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner2",
"test_partner2");
- Assert.equal(getResult(url),
+ Assert.equal(await getResult(url),
"test_channel-cck-test_partner1-test_partner2",
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
});
// url constructed with %PLATFORM_VERSION%
add_task(async function test_platform_version() {
let url = URL_PREFIX + "%PLATFORM_VERSION%/";
- Assert.equal(getResult(url), gAppInfo.platformVersion,
+ Assert.equal(await getResult(url), gAppInfo.platformVersion,
"the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %OS_VERSION%
add_task(async function test_os_version() {
let url = URL_PREFIX + "%OS_VERSION%/";
let osVersion;
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
@@ -310,42 +310,42 @@ add_task(async function test_os_version(
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
} catch (e) {
// Not all platforms have a secondary widget library, so an error is
// nothing to worry about.
}
osVersion = encodeURIComponent(osVersion);
}
- Assert.equal(getResult(url), osVersion,
+ Assert.equal(await getResult(url), osVersion,
"the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %DISTRIBUTION%
add_task(async function test_distribution() {
let url = URL_PREFIX + "%DISTRIBUTION%/";
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
- Assert.equal(getResult(url), "test_distro",
+ Assert.equal(await getResult(url), "test_distro",
"the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %DISTRIBUTION_VERSION%
add_task(async function test_distribution_version() {
let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/";
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
- Assert.equal(getResult(url), "test_distro_version",
+ Assert.equal(await getResult(url), "test_distro_version",
"the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL);
});
add_task(async function test_custom() {
Services.prefs.setCharPref("app.update.custom", "custom");
let url = URL_PREFIX + "%CUSTOM%/";
- Assert.equal(getResult(url), "custom",
+ Assert.equal(await getResult(url), "custom",
"the url query string for %CUSTOM%" + MSG_SHOULD_EQUAL);
});
// url constructed with %SYSTEM_CAPABILITIES%
add_task(async function test_systemCapabilities() {
let url = URL_PREFIX + "%SYSTEM_CAPABILITIES%/";
let systemCapabilities = getInstructionSet() + "," + getMemoryMB();
- Assert.equal(getResult(url), systemCapabilities,
+ Assert.equal(await getResult(url), systemCapabilities,
"the url param for %SYSTEM_CAPABILITIES%" + MSG_SHOULD_EQUAL);
});
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2498,17 +2498,17 @@ this.XPIProvider = {
// Download the list of system add-ons
let url = Preferences.get(PREF_SYSTEM_ADDON_UPDATE_URL, null);
if (!url) {
await systemAddonLocation.cleanDirectories();
return;
}
- url = UpdateUtils.formatUpdateURL(url);
+ url = await UpdateUtils.formatUpdateURL(url);
logger.info(`Starting system add-on update check from ${url}.`);
let res = await ProductAddonChecker.getProductAddonList(url);
// If there was no list then do nothing.
if (!res || !res.gmpAddons) {
logger.info("No system add-ons list was returned.");
await systemAddonLocation.cleanDirectories();
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -2026,41 +2026,40 @@ UpdateService.prototype = {
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_IS_DOWNLOADED);
} else {
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_IS_STAGED);
}
return;
}
let validUpdateURL = true;
- try {
- this.backgroundChecker.getUpdateURL(false);
- } catch (e) {
+ this.backgroundChecker.getUpdateURL(false).catch(e => {
validUpdateURL = false;
- }
- // The following checks are done here so they can be differentiated from
- // foreground checks.
- if (!UpdateUtils.OSVersion) {
- AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_VERSION);
- } else if (!UpdateUtils.ABI) {
- AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_ABI);
- } else if (!validUpdateURL) {
- AUSTLMY.pingCheckCode(this._pingSuffix,
- AUSTLMY.CHK_INVALID_DEFAULT_URL);
- } else if (!getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true)) {
- AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_PREF_DISABLED);
- } else if (!hasUpdateMutex()) {
- AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_MUTEX);
- } else if (!gCanCheckForUpdates) {
- AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNABLE_TO_CHECK);
- } else if (!this.backgroundChecker._enabled) {
- AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_DISABLED_FOR_SESSION);
- }
-
- this.backgroundChecker.checkForUpdates(this, false);
+ }).then(() => {
+ // The following checks are done here so they can be differentiated from
+ // foreground checks.
+ if (!UpdateUtils.OSVersion) {
+ AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_VERSION);
+ } else if (!UpdateUtils.ABI) {
+ AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_ABI);
+ } else if (!validUpdateURL) {
+ AUSTLMY.pingCheckCode(this._pingSuffix,
+ AUSTLMY.CHK_INVALID_DEFAULT_URL);
+ } else if (!getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true)) {
+ AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_PREF_DISABLED);
+ } else if (!hasUpdateMutex()) {
+ AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_MUTEX);
+ } else if (!gCanCheckForUpdates) {
+ AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_UNABLE_TO_CHECK);
+ } else if (!this.backgroundChecker._enabled) {
+ AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_DISABLED_FOR_SESSION);
+ }
+
+ this.backgroundChecker.checkForUpdates(this, false);
+ });
},
/**
* Determine the update from the specified updates that should be offered.
* If both valid major and minor updates are available the minor update will
* be offered.
* @param updates
* An array of available nsIUpdate items
@@ -2813,76 +2812,83 @@ Checker.prototype = {
* The nsIUpdateCheckListener callback
*/
_callback: null,
/**
* The URL of the update service XML file to connect to that contains details
* about available updates.
*/
- getUpdateURL: function UC_getUpdateURL(force) {
+ getUpdateURL: async function UC_getUpdateURL(force) {
this._forced = force;
let url = Services.prefs.getDefaultBranch(null).
getCharPref(PREF_APP_UPDATE_URL, "");
if (!url) {
LOG("Checker:getUpdateURL - update URL not defined");
return null;
}
- url = UpdateUtils.formatUpdateURL(url);
+ url = await UpdateUtils.formatUpdateURL(url);
if (force) {
url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1";
}
LOG("Checker:getUpdateURL - update URL: " + url);
return url;
},
/**
* See nsIUpdateService.idl
*/
checkForUpdates: function UC_checkForUpdates(listener, force) {
LOG("Checker: checkForUpdates, force: " + force);
- if (!listener)
+ if (!listener) {
throw Cr.NS_ERROR_NULL_POINTER;
-
- var url = this.getUpdateURL(force);
- if (!url || (!this.enabled && !force))
+ }
+
+ if (!this.enabled && !force) {
return;
-
- this._request = new XMLHttpRequest();
- this._request.open("GET", url, true);
- this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(false);
- // Prevent the request from reading from the cache.
- this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
- // Prevent the request from writing to the cache.
- this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
- // Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
- this._request.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative = true;
-
- this._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.
- this._request.setRequestHeader("Cache-Control", "no-cache");
- // HTTP/1.0 servers might not implement Cache-Control and
- // might only implement Pragma: no-cache
- this._request.setRequestHeader("Pragma", "no-cache");
-
- var self = this;
- this._request.addEventListener("error", function(event) { self.onError(event); });
- this._request.addEventListener("load", function(event) { self.onLoad(event); });
-
- LOG("Checker:checkForUpdates - sending request to: " + url);
- this._request.send(null);
-
- this._callback = listener;
+ }
+
+ this.getUpdateURL(force).then(url => {
+ if (!url) {
+ return;
+ }
+
+ this._request = new XMLHttpRequest();
+ this._request.open("GET", url, true);
+ this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(false);
+ // Prevent the request from reading from the cache.
+ this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ // Prevent the request from writing to the cache.
+ this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+ // Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
+ this._request.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative = true;
+
+ this._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.
+ this._request.setRequestHeader("Cache-Control", "no-cache");
+ // HTTP/1.0 servers might not implement Cache-Control and
+ // might only implement Pragma: no-cache
+ this._request.setRequestHeader("Pragma", "no-cache");
+
+ var self = this;
+ this._request.addEventListener("error", function(event) { self.onError(event); });
+ this._request.addEventListener("load", function(event) { self.onLoad(event); });
+
+ LOG("Checker:checkForUpdates - sending request to: " + url);
+ this._request.send(null);
+
+ this._callback = listener;
+ });
},
/**
* Returns an array of nsIUpdate objects discovered by the update check.
* @throws if the XML document element node name is not updates.
*/
get _updates() {
var updatesElement = this._request.responseXML.documentElement;
@@ -2908,17 +2914,17 @@ Checker.prototype = {
updateElement.QueryInterface(Ci.nsIDOMElement);
let update;
try {
update = new Update(updateElement);
} catch (e) {
LOG("Checker:_updates get - invalid <update/>, ignoring...");
continue;
}
- update.serviceURL = this.getUpdateURL(this._forced);
+ update.serviceURL = this._request.responseURL;
update.channel = UpdateUtils.UpdateChannel;
updates.push(update);
}
return updates;
},
/**