2015-09-14 21:04:19 +03:00
|
|
|
/* 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/. */
|
2021-03-04 20:15:57 +03:00
|
|
|
"use strict";
|
2015-09-14 21:04:19 +03:00
|
|
|
|
|
|
|
var EXPORTED_SYMBOLS = ["UpdateUtils"];
|
|
|
|
|
2019-01-17 21:18:31 +03:00
|
|
|
const { AppConstants } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/AppConstants.jsm"
|
|
|
|
);
|
|
|
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
const { XPCOMUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/XPCOMUtils.jsm"
|
|
|
|
);
|
|
|
|
const { FileUtils } = ChromeUtils.import(
|
|
|
|
"resource://gre/modules/FileUtils.jsm"
|
2019-07-05 12:15:43 +03:00
|
|
|
);
|
2019-01-17 21:18:31 +03:00
|
|
|
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
|
|
|
const { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm");
|
2020-04-13 22:09:32 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"WindowsVersionInfo",
|
|
|
|
"resource://gre/modules/components-utils/WindowsVersionInfo.jsm"
|
|
|
|
);
|
2018-05-26 03:02:29 +03:00
|
|
|
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]); /* globals fetch */
|
2015-09-14 21:04:19 +03:00
|
|
|
|
2018-01-30 08:17:48 +03:00
|
|
|
ChromeUtils.defineModuleGetter(
|
|
|
|
this,
|
|
|
|
"WindowsRegistry",
|
|
|
|
"resource://gre/modules/WindowsRegistry.jsm"
|
|
|
|
);
|
2017-10-06 20:55:20 +03:00
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
const PER_INSTALLATION_PREFS_PLATFORMS = ["win"];
|
|
|
|
|
2018-11-29 00:47:03 +03:00
|
|
|
// The file that stores Application Update configuration settings. The file is
|
|
|
|
// located in the update directory which makes it a common setting across all
|
|
|
|
// application profiles and allows the Background Update Agent to read it.
|
|
|
|
const FILE_UPDATE_CONFIG_JSON = "update-config.json";
|
2015-09-14 21:04:19 +03:00
|
|
|
const FILE_UPDATE_LOCALE = "update.locale";
|
|
|
|
const PREF_APP_DISTRIBUTION = "distribution.id";
|
|
|
|
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
|
|
|
|
|
|
|
|
var UpdateUtils = {
|
2017-07-12 20:34:23 +03:00
|
|
|
_locale: undefined,
|
|
|
|
|
2015-09-14 21:04:19 +03:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
getUpdateChannel(aIncludePartners = true) {
|
|
|
|
let defaults = Services.prefs.getDefaultBranch(null);
|
2017-03-07 17:29:48 +03:00
|
|
|
let channel = defaults.getCharPref(
|
|
|
|
"app.update.channel",
|
|
|
|
AppConstants.MOZ_UPDATE_CHANNEL
|
|
|
|
);
|
2015-09-14 21:04:19 +03:00
|
|
|
|
|
|
|
if (aIncludePartners) {
|
|
|
|
try {
|
|
|
|
let partners = Services.prefs.getChildList("app.partner.").sort();
|
|
|
|
if (partners.length) {
|
|
|
|
channel += "-cck";
|
2016-11-12 02:22:34 +03:00
|
|
|
partners.forEach(function(prefName) {
|
2015-09-14 21:04:19 +03:00
|
|
|
channel += "-" + Services.prefs.getCharPref(prefName);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return channel;
|
|
|
|
},
|
|
|
|
|
|
|
|
get UpdateChannel() {
|
|
|
|
return this.getUpdateChannel();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Formats a URL by replacing %...% values with OS, build and locale specific
|
|
|
|
* values.
|
|
|
|
*
|
|
|
|
* @param url
|
|
|
|
* The URL to format.
|
|
|
|
* @return The formatted URL.
|
|
|
|
*/
|
2017-07-12 20:34:23 +03:00
|
|
|
async formatUpdateURL(url) {
|
|
|
|
const locale = await this.getLocale();
|
|
|
|
|
2017-07-05 16:06:29 +03:00
|
|
|
return url
|
|
|
|
.replace(/%(\w+)%/g, (match, name) => {
|
|
|
|
switch (name) {
|
|
|
|
case "PRODUCT":
|
2020-03-05 01:00:48 +03:00
|
|
|
return Services.appinfo.name;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "VERSION":
|
2020-03-05 01:00:48 +03:00
|
|
|
return Services.appinfo.version;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "BUILD_ID":
|
2020-03-05 01:00:48 +03:00
|
|
|
return Services.appinfo.appBuildID;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "BUILD_TARGET":
|
2020-03-05 01:00:48 +03:00
|
|
|
return Services.appinfo.OS + "_" + this.ABI;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "OS_VERSION":
|
2020-03-05 01:00:48 +03:00
|
|
|
return this.OSVersion;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "LOCALE":
|
2020-03-05 01:00:48 +03:00
|
|
|
return locale;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "CHANNEL":
|
2020-03-05 01:00:48 +03:00
|
|
|
return this.UpdateChannel;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "PLATFORM_VERSION":
|
2020-03-05 01:00:48 +03:00
|
|
|
return Services.appinfo.platformVersion;
|
2017-07-05 16:06:29 +03:00
|
|
|
case "SYSTEM_CAPABILITIES":
|
2020-03-05 01:00:48 +03:00
|
|
|
return getSystemCapabilities();
|
2017-07-05 16:06:29 +03:00
|
|
|
case "DISTRIBUTION":
|
2020-03-05 01:00:48 +03:00
|
|
|
return getDistributionPrefValue(PREF_APP_DISTRIBUTION);
|
2017-07-05 16:06:29 +03:00
|
|
|
case "DISTRIBUTION_VERSION":
|
2020-03-05 01:00:48 +03:00
|
|
|
return getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION);
|
2017-07-05 16:06:29 +03:00
|
|
|
}
|
2020-03-05 01:00:48 +03:00
|
|
|
return match;
|
2017-07-05 16:06:29 +03:00
|
|
|
})
|
|
|
|
.replace(/\+/g, "%2B");
|
2017-07-12 20:34:23 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2015-09-14 21:04:19 +03:00
|
|
|
},
|
2018-11-29 00:47:03 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines whether or not the Application Update Service automatically
|
|
|
|
* downloads and installs updates. This corresponds to whether or not the user
|
|
|
|
* has selected "Automatically install updates" in about:preferences.
|
|
|
|
*
|
|
|
|
* On Windows, this setting is shared across all profiles for the installation
|
2020-03-03 21:12:14 +03:00
|
|
|
* and is read asynchronously from the file. On other operating systems, this
|
2018-11-29 00:47:03 +03:00
|
|
|
* setting is stored in a pref and is thus a per-profile setting.
|
|
|
|
*
|
|
|
|
* @return A Promise that resolves with a boolean.
|
|
|
|
*/
|
2021-03-04 20:15:57 +03:00
|
|
|
async getAppUpdateAutoEnabled() {
|
|
|
|
return this.readUpdateConfigSetting("app.update.auto");
|
2018-11-29 00:47:03 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggles whether the Update Service automatically downloads and installs
|
|
|
|
* updates. This effectively selects between the "Automatically install
|
|
|
|
* updates" and "Check for updates but let you choose to install them" options
|
|
|
|
* in about:preferences.
|
|
|
|
*
|
|
|
|
* On Windows, this setting is shared across all profiles for the installation
|
2020-03-03 21:12:14 +03:00
|
|
|
* and is written asynchronously to the file. On other operating systems, this
|
2018-11-29 00:47:03 +03:00
|
|
|
* setting is stored in a pref and is thus a per-profile setting.
|
|
|
|
*
|
2020-03-03 21:12:14 +03:00
|
|
|
* If this method is called when the setting is locked, the returned promise
|
|
|
|
* will reject. The lock status can be determined with
|
|
|
|
* UpdateUtils.appUpdateAutoSettingIsLocked()
|
|
|
|
*
|
2018-11-29 00:47:03 +03:00
|
|
|
* @param enabled If set to true, automatic download and installation of
|
|
|
|
* updates will be enabled. If set to false, this will be
|
|
|
|
* disabled.
|
|
|
|
* @return A Promise that, once the setting has been saved, resolves with the
|
|
|
|
* boolean value that was saved. If the setting could not be
|
|
|
|
* successfully saved, the Promise will reject.
|
|
|
|
* On Windows, where this setting is stored in a file, this Promise
|
|
|
|
* may reject with an I/O error.
|
|
|
|
* On other operating systems, this promise should not reject as
|
|
|
|
* this operation simply sets a pref.
|
|
|
|
*/
|
2021-03-04 20:15:57 +03:00
|
|
|
async setAppUpdateAutoEnabled(enabledValue) {
|
|
|
|
return this.writeUpdateConfigSetting("app.update.auto", !!enabledValue);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function should be used to determine if the automatic application
|
|
|
|
* update setting is locked by an enterprise policy
|
|
|
|
*
|
|
|
|
* @return true if the automatic update setting is currently locked.
|
|
|
|
* Otherwise, false.
|
|
|
|
*/
|
|
|
|
appUpdateAutoSettingIsLocked() {
|
2021-03-11 01:12:24 +03:00
|
|
|
return this.appUpdateSettingIsLocked("app.update.auto");
|
2021-03-04 20:15:57 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates whether or not per-installation prefs are supported on this
|
|
|
|
* platform.
|
|
|
|
*/
|
|
|
|
PER_INSTALLATION_PREFS_SUPPORTED: PER_INSTALLATION_PREFS_PLATFORMS.includes(
|
|
|
|
AppConstants.platform
|
|
|
|
),
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Possible per-installation pref types.
|
|
|
|
*/
|
|
|
|
PER_INSTALLATION_PREF_TYPE_BOOL: "boolean",
|
|
|
|
PER_INSTALLATION_PREF_TYPE_ASCII_STRING: "ascii",
|
|
|
|
PER_INSTALLATION_PREF_TYPE_INT: "integer",
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We want the preference definitions to be part of UpdateUtils for a couple
|
|
|
|
* of reasons. It's a clean way for consumers to look up things like observer
|
|
|
|
* topic names. It also allows us to manipulate the supported prefs during
|
|
|
|
* testing. However, we want to use values out of UpdateUtils (like pref
|
|
|
|
* types) to construct this object. Therefore, this will initially be a
|
|
|
|
* placeholder, which we will properly define after the UpdateUtils object
|
|
|
|
* definition.
|
|
|
|
*/
|
|
|
|
PER_INSTALLATION_PREFS: null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function initializes per-installation prefs. Note that it does not
|
|
|
|
* need to be called manually; it is already called within the file.
|
|
|
|
*
|
|
|
|
* This function is called on startup, so it does not read or write to disk.
|
|
|
|
*/
|
|
|
|
initPerInstallPrefs() {
|
|
|
|
// If we don't have per-installation prefs, we store the update config in
|
|
|
|
// preferences. In that case, the best way to notify observers of this
|
|
|
|
// setting is just to propagate it from a pref observer. This ensures that
|
|
|
|
// the expected observers still get notified, even if a user manually
|
|
|
|
// changes the pref value.
|
|
|
|
if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) {
|
|
|
|
let initialConfig = {};
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
|
|
|
|
|
|
|
|
try {
|
|
|
|
let initialValue = prefTypeFns.getProfilePref(prefName);
|
|
|
|
initialConfig[prefName] = initialValue;
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
|
|
Services.prefs.addObserver(prefName, async (subject, topic, data) => {
|
|
|
|
let config = { ...gUpdateConfigCache };
|
|
|
|
config[prefName] = await UpdateUtils.readUpdateConfigSetting(
|
|
|
|
prefName
|
|
|
|
);
|
|
|
|
maybeUpdateConfigChanged(config);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// On the first call to maybeUpdateConfigChanged, it has nothing to
|
|
|
|
// compare its input to, so it just populates the cache and doesn't notify
|
|
|
|
// any observers. This makes sense during normal usage, because the first
|
|
|
|
// call will be on the first config file read, and we don't want to notify
|
|
|
|
// observers of changes on the first read. But that means that when
|
|
|
|
// propagating pref observers, we need to make one initial call to
|
|
|
|
// simulate that initial read so that the cache will be populated when the
|
|
|
|
// first pref observer fires.
|
|
|
|
maybeUpdateConfigChanged(initialConfig);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads an installation-specific configuration setting from the update config
|
|
|
|
* JSON file. This function is guaranteed not to throw. If there are problems
|
|
|
|
* reading the file, the default value will be returned so that update can
|
|
|
|
* proceed. This is particularly important since the configuration file is
|
|
|
|
* writable by anyone and we don't want an unprivileged user to be able to
|
|
|
|
* break update for other users.
|
|
|
|
*
|
|
|
|
* If relevant policies are active, this function will read the policy value
|
|
|
|
* rather than the stored value.
|
|
|
|
*
|
|
|
|
* @param prefName
|
|
|
|
* The preference to read. Must be a key of the
|
|
|
|
* PER_INSTALLATION_PREFS object.
|
|
|
|
* @return A Promise that resolves with the pref's value.
|
|
|
|
*/
|
|
|
|
readUpdateConfigSetting(prefName) {
|
|
|
|
if (!(prefName in this.PER_INSTALLATION_PREFS)) {
|
2020-03-03 21:12:14 +03:00
|
|
|
return Promise.reject(
|
2021-04-09 01:55:21 +03:00
|
|
|
new Error(
|
|
|
|
`UpdateUtils.readUpdateConfigSetting: Unknown per-installation ` +
|
|
|
|
`pref '${prefName}'`
|
|
|
|
)
|
2020-03-03 21:12:14 +03:00
|
|
|
);
|
|
|
|
}
|
2021-03-04 20:15:57 +03:00
|
|
|
|
|
|
|
const pref = this.PER_INSTALLATION_PREFS[prefName];
|
|
|
|
const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
|
|
|
|
|
|
|
|
if (Services.policies && "policyFn" in pref) {
|
|
|
|
let policyValue = pref.policyFn();
|
|
|
|
if (policyValue !== null) {
|
|
|
|
return Promise.resolve(policyValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
|
|
|
|
// If we don't have per-installation prefs, we use regular preferences.
|
|
|
|
let prefValue = prefTypeFns.getProfilePref(prefName, pref.defaultValue);
|
2018-11-29 00:47:03 +03:00
|
|
|
return Promise.resolve(prefValue);
|
|
|
|
}
|
2021-03-04 20:15:57 +03:00
|
|
|
|
|
|
|
let readPromise = updateConfigIOPromise
|
|
|
|
// All promises returned by (read|write)UpdateConfigSetting are part of a
|
|
|
|
// single promise chain in order to serialize disk operations. But we
|
|
|
|
// don't want the entire promise chain to reject when one operation fails.
|
|
|
|
// So we are going to silently clear any rejections the promise chain
|
|
|
|
// might contain.
|
|
|
|
//
|
|
|
|
// We will also pass an empty function for the first then() argument as
|
|
|
|
// well, just to make sure we are starting fresh rather than potentially
|
|
|
|
// propagating some stale value.
|
|
|
|
.then(
|
|
|
|
() => {},
|
|
|
|
() => {}
|
|
|
|
)
|
|
|
|
.then(readUpdateConfig)
|
|
|
|
.then(maybeUpdateConfigChanged)
|
|
|
|
.then(config => {
|
|
|
|
return readEffectiveValue(config, prefName);
|
|
|
|
});
|
|
|
|
updateConfigIOPromise = readPromise;
|
|
|
|
return readPromise;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes an installation-specific configuration setting by writing it to
|
|
|
|
* the update config JSON file.
|
|
|
|
*
|
|
|
|
* If this method is called on a prefName that is locked, the returned promise
|
|
|
|
* will reject. The lock status can be determined with
|
|
|
|
* appUpdateSettingIsLocked().
|
|
|
|
*
|
|
|
|
* @param prefName
|
|
|
|
* The preference to change. This must be a key of the
|
|
|
|
* PER_INSTALLATION_PREFS object.
|
|
|
|
* @param value
|
|
|
|
* The value to be written. Its type must match
|
|
|
|
* PER_INSTALLATION_PREFS[prefName].type
|
|
|
|
* @param options
|
|
|
|
* Optional. An object containing any of the following keys:
|
|
|
|
* setDefaultOnly
|
|
|
|
* If set to true, the default branch value will be set rather
|
|
|
|
* than user value. If a user value is set for this pref, this
|
|
|
|
* will have no effect on the pref's effective value.
|
|
|
|
* NOTE - The behavior of the default pref branch currently
|
|
|
|
* differs depending on whether the current platform
|
|
|
|
* supports per-installation prefs. If they are
|
|
|
|
* supported, default branch values persist across
|
|
|
|
* Firefox sessions. If they aren't supported, default
|
|
|
|
* branch values reset when Firefox shuts down.
|
|
|
|
* @return A Promise that, once the setting has been saved, resolves with the
|
|
|
|
* value that was saved.
|
|
|
|
* @throw If there is an I/O error when attempting to write to the config
|
|
|
|
* file, the returned Promise will reject with an OS.File.Error
|
|
|
|
* exception.
|
|
|
|
*/
|
|
|
|
writeUpdateConfigSetting(prefName, value, options) {
|
|
|
|
if (!(prefName in this.PER_INSTALLATION_PREFS)) {
|
|
|
|
return Promise.reject(
|
2021-04-09 01:55:21 +03:00
|
|
|
new Error(
|
|
|
|
`UpdateUtils.writeUpdateConfigSetting: Unknown per-installation ` +
|
|
|
|
`pref '${prefName}'`
|
|
|
|
)
|
2021-03-04 20:15:57 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-11 01:12:24 +03:00
|
|
|
if (this.appUpdateSettingIsLocked(prefName)) {
|
2021-03-04 20:15:57 +03:00
|
|
|
return Promise.reject(
|
2021-04-09 01:55:21 +03:00
|
|
|
new Error(
|
|
|
|
`UpdateUtils.writeUpdateConfigSetting: Unable to change value of ` +
|
|
|
|
`setting '${prefName}' because it is locked by policy`
|
|
|
|
)
|
2021-03-04 20:15:57 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!options) {
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
const pref = this.PER_INSTALLATION_PREFS[prefName];
|
|
|
|
const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
|
|
|
|
|
|
|
|
if (!prefTypeFns.isValid(value)) {
|
|
|
|
return Promise.reject(
|
2021-04-09 01:55:21 +03:00
|
|
|
new Error(
|
|
|
|
`UpdateUtils.writeUpdateConfigSetting: Attempted to change pref ` +
|
|
|
|
`'${prefName} to invalid value: ${JSON.stringify(value)}`
|
|
|
|
)
|
2021-03-04 20:15:57 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
|
|
|
|
// If we don't have per-installation prefs, we use regular preferences.
|
|
|
|
if (options.setDefaultOnly) {
|
|
|
|
prefTypeFns.setProfileDefaultPref(prefName, value);
|
|
|
|
} else {
|
|
|
|
prefTypeFns.setProfilePref(prefName, value);
|
|
|
|
}
|
|
|
|
// Rather than call maybeUpdateConfigChanged, a pref observer has
|
|
|
|
// been connected to the relevant pref. This allows us to catch direct
|
|
|
|
// changes to prefs (which Firefox shouldn't be doing, but the user
|
|
|
|
// might do in about:config).
|
|
|
|
return Promise.resolve(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
let writePromise = updateConfigIOPromise
|
|
|
|
// All promises returned by (read|write)UpdateConfigSetting are part of a
|
|
|
|
// single promise chain in order to serialize disk operations. But we
|
|
|
|
// don't want the entire promise chain to reject when one operation fails.
|
|
|
|
// So we are going to silently clear any rejections the promise chain
|
|
|
|
// might contain.
|
|
|
|
//
|
|
|
|
// We will also pass an empty function for the first then() argument as
|
|
|
|
// well, just to make sure we are starting fresh rather than potentially
|
|
|
|
// propagating some stale value.
|
|
|
|
.then(
|
|
|
|
() => {},
|
|
|
|
() => {}
|
|
|
|
)
|
|
|
|
// We always re-read the update config before writing, rather than using a
|
|
|
|
// cached version. Otherwise, two simultaneous instances may overwrite
|
|
|
|
// each other's changes.
|
|
|
|
.then(readUpdateConfig)
|
|
|
|
.then(async config => {
|
|
|
|
setConfigValue(config, prefName, value, {
|
|
|
|
setDefaultOnly: !!options.setDefaultOnly,
|
|
|
|
});
|
|
|
|
|
2018-11-29 00:47:03 +03:00
|
|
|
try {
|
2021-03-04 20:15:57 +03:00
|
|
|
await writeUpdateConfig(config);
|
|
|
|
return config;
|
2018-11-29 00:47:03 +03:00
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError(
|
2021-03-04 20:15:57 +03:00
|
|
|
"UpdateUtils.writeUpdateConfigSetting: App update configuration " +
|
|
|
|
"file write failed. Exception: " +
|
2018-11-29 00:47:03 +03:00
|
|
|
e
|
|
|
|
);
|
2021-03-04 20:15:57 +03:00
|
|
|
// Re-throw the error so the caller knows that writing the value in
|
|
|
|
// the app update config file failed.
|
2018-11-29 00:47:03 +03:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
})
|
2021-03-04 20:15:57 +03:00
|
|
|
.then(maybeUpdateConfigChanged)
|
|
|
|
.then(() => {
|
|
|
|
// If this value wasn't written, a previous promise in the chain will
|
|
|
|
// have thrown, so we can unconditionally return the expected written
|
|
|
|
// value as the value that was written.
|
|
|
|
return value;
|
|
|
|
});
|
|
|
|
updateConfigIOPromise = writePromise;
|
2018-11-29 00:47:03 +03:00
|
|
|
return writePromise;
|
|
|
|
},
|
2021-03-11 01:12:24 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the specified pref is controlled by policy and thus should
|
|
|
|
* not be changeable by the user.
|
|
|
|
*/
|
|
|
|
appUpdateSettingIsLocked(prefName) {
|
|
|
|
if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
|
|
|
|
return Promise.reject(
|
2021-04-09 01:55:21 +03:00
|
|
|
new Error(
|
|
|
|
`UpdateUtils.appUpdateSettingIsLocked: Unknown per-installation pref '${prefName}'`
|
|
|
|
)
|
2021-03-11 01:12:24 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have policy support, nothing can be locked.
|
|
|
|
if (!Services.policies) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
|
|
|
|
if (!pref.policyFn) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const policyValue = pref.policyFn();
|
|
|
|
return policyValue !== null;
|
|
|
|
},
|
2021-03-04 20:15:57 +03:00
|
|
|
};
|
2020-03-03 21:12:14 +03:00
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
const PER_INSTALLATION_DEFAULTS_BRANCH = "__DEFAULTS__";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Some prefs are specific to the installation, not the profile. They are
|
|
|
|
* stored in JSON format in FILE_UPDATE_CONFIG_JSON.
|
|
|
|
* Not all platforms currently support per-installation prefs, in which case
|
|
|
|
* we fall back to using profile-specific prefs.
|
|
|
|
*
|
|
|
|
* Note: These prefs should always be accessed through UpdateUtils. Do NOT
|
|
|
|
* attempt to read or write their prefs directly.
|
|
|
|
*
|
|
|
|
* Keys in this object should be the name of the pref. The same name will be
|
|
|
|
* used whether we are writing it to the per-installation or per-profile pref.
|
|
|
|
* Values in this object should be objects with the following keys:
|
|
|
|
* type
|
|
|
|
* Must be one of the Update.PER_INSTALLATION_PREF_TYPE_* values, defined
|
|
|
|
* above.
|
|
|
|
* defaultValue
|
|
|
|
* The default value to use for this pref if no value is set. This must be
|
|
|
|
* of a type that is compatible with the type value specified.
|
|
|
|
* migrate
|
|
|
|
* Optional - defaults to false. A boolean indicating whether an existing
|
|
|
|
* value in the profile-specific prefs ought to be migrated to an
|
|
|
|
* installation specific pref. This is useful for prefs like
|
|
|
|
* app.update.auto that used to be profile-specific prefs.
|
|
|
|
* Note - Migration currently happens only on the creation of the JSON
|
|
|
|
* file. If we want to add more prefs that require migration, we
|
|
|
|
* will probably need to change this.
|
|
|
|
* observerTopic
|
|
|
|
* When a config value is changed, an observer will be fired, much like
|
|
|
|
* the existing preference observers. This specifies the topic of the
|
|
|
|
* observer that will be fired.
|
|
|
|
* policyFn
|
|
|
|
* Optional. If defined, should be a function that returns null or a value
|
|
|
|
* of the specified type of this pref. If null is returned, this has no
|
|
|
|
* effect. If another value is returned, it will be used rather than
|
|
|
|
* reading the pref. This function will only be called if
|
|
|
|
* Services.policies is defined. Asynchronous functions are not currently
|
|
|
|
* supported.
|
|
|
|
*/
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS = {
|
|
|
|
"app.update.auto": {
|
|
|
|
type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL,
|
|
|
|
defaultValue: true,
|
|
|
|
migrate: true,
|
|
|
|
observerTopic: "auto-update-config-change",
|
|
|
|
policyFn: () => {
|
|
|
|
if (!Services.policies.isAllowed("app-auto-updates-off")) {
|
|
|
|
// We aren't allowed to turn off auto-update - it is forced on.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!Services.policies.isAllowed("app-auto-updates-on")) {
|
|
|
|
// We aren't allowed to turn on auto-update - it is forced off.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2020-03-03 21:12:14 +03:00
|
|
|
},
|
2021-03-11 01:12:24 +03:00
|
|
|
"app.update.background.enabled": {
|
|
|
|
type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL,
|
|
|
|
defaultValue: true,
|
|
|
|
observerTopic: "background-update-config-change",
|
|
|
|
policyFn: () => {
|
|
|
|
if (!Services.policies.isAllowed("app-background-update-off")) {
|
|
|
|
// We aren't allowed to turn off background update - it is forced on.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!Services.policies.isAllowed("app-background-update-on")) {
|
|
|
|
// We aren't allowed to turn on background update - it is forced off.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
},
|
2015-09-14 21:04:19 +03:00
|
|
|
};
|
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
const TYPE_SPECIFIC_PREF_FNS = {
|
|
|
|
[UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL]: {
|
|
|
|
getProfilePref: Services.prefs.getBoolPref,
|
|
|
|
setProfilePref: Services.prefs.setBoolPref,
|
|
|
|
setProfileDefaultPref: (pref, value) => {
|
|
|
|
let defaults = Services.prefs.getDefaultBranch("");
|
|
|
|
defaults.setBoolPref(pref, value);
|
|
|
|
},
|
|
|
|
isValid: value => typeof value == "boolean",
|
|
|
|
},
|
|
|
|
[UpdateUtils.PER_INSTALLATION_PREF_TYPE_ASCII_STRING]: {
|
|
|
|
getProfilePref: Services.prefs.getCharPref,
|
|
|
|
setProfilePref: Services.prefs.setCharPref,
|
|
|
|
setProfileDefaultPref: (pref, value) => {
|
|
|
|
let defaults = Services.prefs.getDefaultBranch("");
|
|
|
|
defaults.setCharPref(pref, value);
|
|
|
|
},
|
|
|
|
isValid: value => typeof value == "string",
|
|
|
|
},
|
|
|
|
[UpdateUtils.PER_INSTALLATION_PREF_TYPE_INT]: {
|
|
|
|
getProfilePref: Services.prefs.getIntPref,
|
|
|
|
setProfilePref: Services.prefs.setIntPref,
|
|
|
|
setProfileDefaultPref: (pref, value) => {
|
|
|
|
let defaults = Services.prefs.getDefaultBranch("");
|
|
|
|
defaults.setIntPref(pref, value);
|
|
|
|
},
|
|
|
|
isValid: value => Number.isInteger(value),
|
|
|
|
},
|
|
|
|
};
|
2018-11-29 00:47:03 +03:00
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
/**
|
|
|
|
* Used for serializing reads and writes of the app update json config file so
|
|
|
|
* the writes don't happen out of order and the last write is the one that
|
|
|
|
* the sets the value.
|
|
|
|
*/
|
|
|
|
var updateConfigIOPromise = Promise.resolve();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a pref name that we will use to keep track of if the passed pref has
|
|
|
|
* been migrated already, so we don't end up migrating it twice.
|
|
|
|
*/
|
|
|
|
function getPrefMigratedPref(prefName) {
|
|
|
|
return prefName + ".migrated";
|
2018-11-29 00:47:03 +03:00
|
|
|
}
|
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
/**
|
|
|
|
* @return true if prefs need to be migrated from profile-specific prefs to
|
|
|
|
* installation-specific prefs.
|
|
|
|
*/
|
|
|
|
function updateConfigNeedsMigration() {
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
if (pref.migrate) {
|
|
|
|
let migratedPrefName = getPrefMigratedPref(prefName);
|
|
|
|
let migrated = Services.prefs.getBoolPref(migratedPrefName, false);
|
|
|
|
if (!migrated) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2018-11-29 00:47:03 +03:00
|
|
|
}
|
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
function setUpdateConfigMigrationDone() {
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
if (pref.migrate) {
|
|
|
|
let migratedPrefName = getPrefMigratedPref(prefName);
|
|
|
|
Services.prefs.setBoolPref(migratedPrefName, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes the migrated data.
|
|
|
|
*/
|
|
|
|
function onMigrationSuccessful() {
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
if (pref.migrate) {
|
|
|
|
Services.prefs.clearUserPref(prefName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeMigrationUpdateConfig() {
|
|
|
|
let config = makeDefaultUpdateConfig();
|
|
|
|
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
if (!pref.migrate) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let migratedPrefName = getPrefMigratedPref(prefName);
|
|
|
|
let alreadyMigrated = Services.prefs.getBoolPref(migratedPrefName, false);
|
|
|
|
if (alreadyMigrated) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
|
2021-03-12 01:46:10 +03:00
|
|
|
|
|
|
|
let prefHasValue = true;
|
|
|
|
let prefValue;
|
|
|
|
try {
|
|
|
|
// Without a second argument, this will throw if the pref has no user
|
|
|
|
// value or default value.
|
|
|
|
prefValue = prefTypeFns.getProfilePref(prefName);
|
|
|
|
} catch (e) {
|
|
|
|
prefHasValue = false;
|
|
|
|
}
|
|
|
|
if (prefHasValue) {
|
2021-03-04 20:15:57 +03:00
|
|
|
setConfigValue(config, prefName, prefValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeDefaultUpdateConfig() {
|
|
|
|
let config = {};
|
|
|
|
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
setConfigValue(config, prefName, pref.defaultValue, {
|
|
|
|
setDefaultOnly: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the specified value in the config object.
|
|
|
|
*
|
|
|
|
* @param config
|
|
|
|
* The config object for which to set the value
|
|
|
|
* @param prefName
|
|
|
|
* The name of the preference to set.
|
|
|
|
* @param prefValue
|
|
|
|
* The value to set the preference to.
|
|
|
|
* @param options
|
|
|
|
* Optional. An object containing any of the following keys:
|
|
|
|
* setDefaultOnly
|
|
|
|
* If set to true, the default value will be set rather than
|
|
|
|
* user value. If a user value is set for this pref, this will
|
|
|
|
* have no effect on the pref's effective value.
|
|
|
|
*/
|
|
|
|
function setConfigValue(config, prefName, prefValue, options) {
|
|
|
|
if (!options) {
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.setDefaultOnly) {
|
|
|
|
if (!(PER_INSTALLATION_DEFAULTS_BRANCH in config)) {
|
|
|
|
config[PER_INSTALLATION_DEFAULTS_BRANCH] = {};
|
|
|
|
}
|
|
|
|
config[PER_INSTALLATION_DEFAULTS_BRANCH][prefName] = prefValue;
|
|
|
|
} else if (prefValue != readDefaultValue(config, prefName)) {
|
|
|
|
config[prefName] = prefValue;
|
|
|
|
} else {
|
|
|
|
delete config[prefName];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the specified pref out of the given configuration object.
|
|
|
|
* If a user value of the pref is set, that will be returned. If only a default
|
|
|
|
* branch value is set, that will be returned. Otherwise, the default value from
|
|
|
|
* PER_INSTALLATION_PREFS will be returned.
|
|
|
|
*
|
|
|
|
* Values will be validated before being returned. Invalid values are ignored.
|
|
|
|
*
|
|
|
|
* @param config
|
|
|
|
* The configuration object to read.
|
|
|
|
* @param prefName
|
|
|
|
* The name of the preference to read.
|
|
|
|
* @return The value of the preference.
|
|
|
|
*/
|
|
|
|
function readEffectiveValue(config, prefName) {
|
|
|
|
if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
|
|
|
|
throw new Error(
|
|
|
|
`readEffectiveValue: Unknown per-installation pref '${prefName}'`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
|
|
|
|
const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
|
|
|
|
|
|
|
|
if (prefName in config) {
|
|
|
|
if (prefTypeFns.isValid(config[prefName])) {
|
|
|
|
return config[prefName];
|
|
|
|
}
|
|
|
|
Cu.reportError(
|
|
|
|
`readEffectiveValue: Got invalid value for update config's` +
|
|
|
|
` '${prefName}' value: "${config[prefName]}"`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return readDefaultValue(config, prefName);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the default branch pref out of the given configuration object. If one
|
|
|
|
* is not set, the default value from PER_INSTALLATION_PREFS will be returned.
|
|
|
|
*
|
|
|
|
* Values will be validated before being returned. Invalid values are ignored.
|
|
|
|
*
|
|
|
|
* @param config
|
|
|
|
* The configuration object to read.
|
|
|
|
* @param prefName
|
|
|
|
* The name of the preference to read.
|
|
|
|
* @return The value of the preference.
|
|
|
|
*/
|
|
|
|
function readDefaultValue(config, prefName) {
|
|
|
|
if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
|
|
|
|
throw new Error(
|
|
|
|
`readDefaultValue: Unknown per-installation pref '${prefName}'`
|
2018-11-29 00:47:03 +03:00
|
|
|
);
|
|
|
|
}
|
2021-03-04 20:15:57 +03:00
|
|
|
const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
|
|
|
|
const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
|
|
|
|
|
|
|
|
if (PER_INSTALLATION_DEFAULTS_BRANCH in config) {
|
|
|
|
let defaults = config[PER_INSTALLATION_DEFAULTS_BRANCH];
|
|
|
|
if (prefName in defaults) {
|
|
|
|
if (prefTypeFns.isValid(defaults[prefName])) {
|
|
|
|
return defaults[prefName];
|
|
|
|
}
|
|
|
|
Cu.reportError(
|
|
|
|
`readEffectiveValue: Got invalid default value for update` +
|
|
|
|
` config's '${prefName}' value: "${defaults[prefName]}"`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pref.defaultValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the update config and, if necessary, performs migration of un-migrated
|
|
|
|
* values. We don't want to completely give up on update if this file is
|
|
|
|
* unavailable, so default values will be returned on failure rather than
|
|
|
|
* throwing an error.
|
|
|
|
*
|
|
|
|
* @return An Update Config object.
|
|
|
|
*/
|
|
|
|
async function readUpdateConfig() {
|
|
|
|
try {
|
|
|
|
let configFile = FileUtils.getDir("UpdRootD", [], true);
|
|
|
|
configFile.append(FILE_UPDATE_CONFIG_JSON);
|
|
|
|
let binaryData = await OS.File.read(configFile.path);
|
|
|
|
|
|
|
|
// We only migrate once. If we read something, the migration has already
|
|
|
|
// happened so we should make sure it doesn't happen again.
|
|
|
|
setUpdateConfigMigrationDone();
|
|
|
|
|
|
|
|
let jsonData = new TextDecoder().decode(binaryData);
|
|
|
|
let config = JSON.parse(jsonData);
|
|
|
|
return config;
|
|
|
|
} catch (e) {
|
|
|
|
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
|
|
|
|
if (updateConfigNeedsMigration()) {
|
|
|
|
const migrationConfig = makeMigrationUpdateConfig();
|
|
|
|
setUpdateConfigMigrationDone();
|
|
|
|
try {
|
|
|
|
await writeUpdateConfig(migrationConfig);
|
|
|
|
onMigrationSuccessful();
|
|
|
|
return migrationConfig;
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError("readUpdateConfig: Migration failed: " + e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We only migrate once. If we got an error other than the file not
|
|
|
|
// existing, the migration has already happened so we should make sure
|
|
|
|
// it doesn't happen again.
|
|
|
|
setUpdateConfigMigrationDone();
|
|
|
|
|
|
|
|
Cu.reportError(
|
|
|
|
"readUpdateConfig: Unable to read app update configuration file. " +
|
|
|
|
"Exception: " +
|
|
|
|
e
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return makeDefaultUpdateConfig();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes the given configuration to the disk.
|
|
|
|
*
|
|
|
|
* @param config
|
|
|
|
* The configuration object to write.
|
|
|
|
* @return The configuration object written.
|
|
|
|
* @throw An OS.File.Error exception will be thrown on I/O error.
|
|
|
|
*/
|
|
|
|
async function writeUpdateConfig(config) {
|
|
|
|
let configFile = FileUtils.getDir("UpdRootD", [], true);
|
|
|
|
configFile.append(FILE_UPDATE_CONFIG_JSON);
|
|
|
|
await OS.File.writeAtomic(configFile.path, JSON.stringify(config));
|
|
|
|
return config;
|
2018-11-29 00:47:03 +03:00
|
|
|
}
|
2021-03-04 20:15:57 +03:00
|
|
|
|
|
|
|
var gUpdateConfigCache;
|
|
|
|
/**
|
|
|
|
* Notifies observers if any update config prefs have changed.
|
|
|
|
*
|
|
|
|
* @param config
|
|
|
|
* The most up-to-date config object.
|
|
|
|
* @return The same config object that was passed in.
|
|
|
|
*/
|
|
|
|
function maybeUpdateConfigChanged(config) {
|
|
|
|
if (!gUpdateConfigCache) {
|
|
|
|
// We don't want to generate a change notification for every pref on the
|
|
|
|
// first read of the session.
|
|
|
|
gUpdateConfigCache = config;
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const [prefName, pref] of Object.entries(
|
|
|
|
UpdateUtils.PER_INSTALLATION_PREFS
|
|
|
|
)) {
|
|
|
|
let newPrefValue = readEffectiveValue(config, prefName);
|
|
|
|
let oldPrefValue = readEffectiveValue(gUpdateConfigCache, prefName);
|
|
|
|
if (newPrefValue != oldPrefValue) {
|
|
|
|
Services.obs.notifyObservers(
|
|
|
|
null,
|
|
|
|
pref.observerTopic,
|
|
|
|
newPrefValue.toString()
|
|
|
|
);
|
2020-03-03 21:13:07 +03:00
|
|
|
}
|
2021-03-04 20:15:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
gUpdateConfigCache = config;
|
|
|
|
return config;
|
2020-03-03 21:13:07 +03:00
|
|
|
}
|
2018-11-29 00:47:03 +03:00
|
|
|
|
2021-03-04 20:15:57 +03:00
|
|
|
/**
|
|
|
|
* Note that this function sets up observers only, it does not do any I/O.
|
|
|
|
*/
|
|
|
|
UpdateUtils.initPerInstallPrefs();
|
|
|
|
|
2015-09-14 21:04:19 +03:00
|
|
|
/* Get the distribution pref values, from defaults only */
|
|
|
|
function getDistributionPrefValue(aPrefName) {
|
2020-03-05 20:44:44 +03:00
|
|
|
let value = Services.prefs
|
2015-09-14 21:04:19 +03:00
|
|
|
.getDefaultBranch(null)
|
|
|
|
.getCharPref(aPrefName, "default");
|
2020-03-05 20:44:44 +03:00
|
|
|
if (!value) {
|
|
|
|
value = "default";
|
|
|
|
}
|
|
|
|
return value;
|
2015-09-14 21:04:19 +03:00
|
|
|
}
|
|
|
|
|
2017-05-24 07:45:50 +03:00
|
|
|
function getSystemCapabilities() {
|
2017-12-15 19:33:44 +03:00
|
|
|
return "ISET:" + gInstructionSet + ",MEM:" + getMemoryMB();
|
2017-05-24 07:45:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the RAM size in megabytes. This will round the value because sysinfo
|
|
|
|
* doesn't always provide RAM in multiples of 1024.
|
|
|
|
*/
|
|
|
|
function getMemoryMB() {
|
|
|
|
let memoryMB = "unknown";
|
|
|
|
try {
|
|
|
|
memoryMB = Services.sysinfo.getProperty("memsize");
|
|
|
|
if (memoryMB) {
|
|
|
|
memoryMB = Math.round(memoryMB / 1024 / 1024);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError(
|
|
|
|
"Error getting system info memsize property. Exception: " + e
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return memoryMB;
|
|
|
|
}
|
|
|
|
|
2016-05-21 04:17:43 +03:00
|
|
|
/**
|
2017-05-24 07:45:50 +03:00
|
|
|
* Gets the supported CPU instruction set.
|
2016-05-21 04:17:43 +03:00
|
|
|
*/
|
2017-05-24 07:45:50 +03:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "gInstructionSet", function aus_gIS() {
|
2017-12-01 00:39:40 +03:00
|
|
|
const CPU_EXTENSIONS = [
|
|
|
|
"hasSSE4_2",
|
|
|
|
"hasSSE4_1",
|
|
|
|
"hasSSE4A",
|
|
|
|
"hasSSSE3",
|
|
|
|
"hasSSE3",
|
|
|
|
"hasSSE2",
|
|
|
|
"hasSSE",
|
|
|
|
"hasMMX",
|
|
|
|
"hasNEON",
|
|
|
|
"hasARMv7",
|
|
|
|
"hasARMv6",
|
|
|
|
];
|
|
|
|
for (let ext of CPU_EXTENSIONS) {
|
|
|
|
if (Services.sysinfo.getProperty(ext)) {
|
|
|
|
return ext.substring(3);
|
2016-10-08 02:25:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-01 00:39:40 +03:00
|
|
|
return "unknown";
|
2016-05-21 04:17:43 +03:00
|
|
|
});
|
|
|
|
|
2015-09-14 21:04:19 +03:00
|
|
|
/* Windows only getter that returns the processor architecture. */
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
|
|
|
|
// Get processor architecture
|
|
|
|
let arch = "unknown";
|
|
|
|
|
|
|
|
const WORD = ctypes.uint16_t;
|
|
|
|
const DWORD = ctypes.uint32_t;
|
|
|
|
|
|
|
|
// This structure is described at:
|
|
|
|
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
|
|
|
const SYSTEM_INFO = new ctypes.StructType("SYSTEM_INFO", [
|
|
|
|
{ wProcessorArchitecture: WORD },
|
|
|
|
{ wReserved: WORD },
|
|
|
|
{ dwPageSize: DWORD },
|
|
|
|
{ lpMinimumApplicationAddress: ctypes.voidptr_t },
|
|
|
|
{ lpMaximumApplicationAddress: ctypes.voidptr_t },
|
|
|
|
{ dwActiveProcessorMask: DWORD.ptr },
|
|
|
|
{ dwNumberOfProcessors: DWORD },
|
|
|
|
{ dwProcessorType: DWORD },
|
|
|
|
{ dwAllocationGranularity: DWORD },
|
|
|
|
{ wProcessorLevel: WORD },
|
|
|
|
{ wProcessorRevision: WORD },
|
|
|
|
]);
|
|
|
|
|
|
|
|
let kernel32 = false;
|
|
|
|
try {
|
|
|
|
kernel32 = ctypes.open("Kernel32");
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError("Unable to open kernel32! Exception: " + e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kernel32) {
|
|
|
|
try {
|
|
|
|
let GetNativeSystemInfo = kernel32.declare(
|
|
|
|
"GetNativeSystemInfo",
|
2017-04-25 22:00:23 +03:00
|
|
|
ctypes.winapi_abi,
|
2015-09-14 21:04:19 +03:00
|
|
|
ctypes.void_t,
|
|
|
|
SYSTEM_INFO.ptr
|
|
|
|
);
|
|
|
|
let winSystemInfo = SYSTEM_INFO();
|
|
|
|
// Default to unknown
|
|
|
|
winSystemInfo.wProcessorArchitecture = 0xffff;
|
|
|
|
|
|
|
|
GetNativeSystemInfo(winSystemInfo.address());
|
|
|
|
switch (winSystemInfo.wProcessorArchitecture) {
|
2018-12-14 05:23:01 +03:00
|
|
|
case 12:
|
|
|
|
arch = "aarch64";
|
|
|
|
break;
|
2015-09-14 21:04:19 +03:00
|
|
|
case 9:
|
|
|
|
arch = "x64";
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
arch = "IA64";
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
arch = "x86";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
Cu.reportError("Error getting processor architecture. Exception: " + e);
|
|
|
|
} finally {
|
|
|
|
kernel32.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return arch;
|
|
|
|
});
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() {
|
|
|
|
let abi = null;
|
|
|
|
try {
|
|
|
|
abi = Services.appinfo.XPCOMABI;
|
2016-12-31 05:47:25 +03:00
|
|
|
} catch (e) {
|
2015-09-14 21:04:19 +03:00
|
|
|
Cu.reportError("XPCOM ABI unknown");
|
|
|
|
}
|
|
|
|
|
2018-05-31 17:34:23 +03:00
|
|
|
if (AppConstants.platform == "win") {
|
2015-09-14 21:04:19 +03:00
|
|
|
// Windows build should report the CPU architecture that it's running on.
|
|
|
|
abi += "-" + gWinCPUArch;
|
|
|
|
}
|
2018-01-24 21:25:23 +03:00
|
|
|
|
|
|
|
if (AppConstants.ASAN) {
|
|
|
|
// Allow ASan builds to receive their own updates
|
|
|
|
abi += "-asan";
|
|
|
|
}
|
|
|
|
|
2015-09-14 21:04:19 +03:00
|
|
|
return abi;
|
|
|
|
});
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() {
|
|
|
|
let osVersion;
|
|
|
|
try {
|
|
|
|
osVersion =
|
|
|
|
Services.sysinfo.getProperty("name") +
|
|
|
|
" " +
|
|
|
|
Services.sysinfo.getProperty("version");
|
2016-12-31 05:47:25 +03:00
|
|
|
} catch (e) {
|
2015-09-14 21:04:19 +03:00
|
|
|
Cu.reportError("OS Version unknown.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (osVersion) {
|
|
|
|
if (AppConstants.platform == "win") {
|
2020-04-13 22:09:32 +03:00
|
|
|
// Add service pack and build number
|
2015-09-14 21:04:19 +03:00
|
|
|
try {
|
2020-04-13 22:09:32 +03:00
|
|
|
const {
|
|
|
|
servicePackMajor,
|
|
|
|
servicePackMinor,
|
|
|
|
buildNumber,
|
|
|
|
} = WindowsVersionInfo.get();
|
|
|
|
osVersion += `.${servicePackMajor}.${servicePackMinor}.${buildNumber}`;
|
|
|
|
} catch (err) {
|
|
|
|
Cu.reportError(
|
|
|
|
"Unable to retrieve windows version information: " + err
|
|
|
|
);
|
|
|
|
osVersion += ".unknown";
|
2015-09-14 21:04:19 +03:00
|
|
|
}
|
|
|
|
|
2020-04-13 22:09:32 +03:00
|
|
|
// add UBR if on Windows 10
|
|
|
|
if (
|
|
|
|
Services.vc.compare(Services.sysinfo.getProperty("version"), "10") >= 0
|
|
|
|
) {
|
|
|
|
const WINDOWS_UBR_KEY_PATH =
|
|
|
|
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
|
|
|
|
let ubr = WindowsRegistry.readRegKey(
|
|
|
|
Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
|
|
|
|
WINDOWS_UBR_KEY_PATH,
|
|
|
|
"UBR",
|
|
|
|
Ci.nsIWindowsRegKey.WOW64_64
|
|
|
|
);
|
|
|
|
if (ubr !== undefined) {
|
|
|
|
osVersion += `.${ubr}`;
|
|
|
|
} else {
|
2020-04-13 22:09:40 +03:00
|
|
|
osVersion += ".unknown";
|
2015-09-14 21:04:19 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-13 22:09:32 +03:00
|
|
|
|
|
|
|
// Add processor architecture
|
|
|
|
osVersion += " (" + gWinCPUArch + ")";
|
2015-09-14 21:04:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
osVersion +=
|
|
|
|
" (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
|
2016-12-31 05:47:25 +03:00
|
|
|
} catch (e) {
|
2015-09-14 21:04:19 +03:00
|
|
|
// Not all platforms have a secondary widget library, so an error is nothing to worry about.
|
|
|
|
}
|
|
|
|
osVersion = encodeURIComponent(osVersion);
|
|
|
|
}
|
|
|
|
return osVersion;
|
|
|
|
});
|