1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
7 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
11 ChromeUtils.defineESModuleGetters(lazy, {
12 WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
14 "resource://gre/modules/components-utils/WindowsVersionInfo.sys.mjs",
15 ctypes: "resource://gre/modules/ctypes.sys.mjs",
18 const PER_INSTALLATION_PREFS_PLATFORMS = ["win"];
20 // The file that stores Application Update configuration settings. The file is
21 // located in the update directory which makes it a common setting across all
22 // application profiles and allows the Background Update Agent to read it.
23 const FILE_UPDATE_CONFIG_JSON = "update-config.json";
24 const FILE_UPDATE_LOCALE = "update.locale";
25 const PREF_APP_DISTRIBUTION = "distribution.id";
26 const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
28 export var UpdateUtils = {
30 _configFilePath: undefined,
33 * Read the update channel from defaults only. We do this to ensure that
34 * the channel is tightly coupled with the application and does not apply
35 * to other instances of the application that may use the same profile.
37 * @param [optional] aIncludePartners
38 * Whether or not to include the partner bits. Default: true.
40 getUpdateChannel(aIncludePartners = true) {
41 let defaults = Services.prefs.getDefaultBranch(null);
42 let channel = defaults.getCharPref(
44 AppConstants.MOZ_UPDATE_CHANNEL
47 if (aIncludePartners) {
49 let partners = Services.prefs.getChildList("app.partner.").sort();
50 if (partners.length) {
52 partners.forEach(function (prefName) {
53 channel += "-" + Services.prefs.getCharPref(prefName);
65 return this.getUpdateChannel();
69 * Formats a URL by replacing %...% values with OS, build and locale specific
74 * @return The formatted URL.
76 async formatUpdateURL(url) {
77 const locale = await this.getLocale();
80 .replace(/%(\w+)%/g, (match, name) => {
83 return Services.appinfo.name;
85 return Services.appinfo.version;
87 return Services.appinfo.appBuildID;
89 return Services.appinfo.OS + "_" + this.ABI;
91 return this.OSVersion;
95 return this.UpdateChannel;
96 case "PLATFORM_VERSION":
97 return Services.appinfo.platformVersion;
98 case "SYSTEM_CAPABILITIES":
99 return getSystemCapabilities();
101 return getDistributionPrefValue(PREF_APP_DISTRIBUTION);
102 case "DISTRIBUTION_VERSION":
103 return getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION);
107 .replace(/\+/g, "%2B");
111 * Gets the locale from the update.locale file for replacing %LOCALE% in the
112 * update url. The update.locale file can be located in the application
113 * directory or the GRE directory with preference given to it being located in
114 * the application directory.
117 if (this._locale !== undefined) {
121 for (let res of ["app", "gre"]) {
122 const url = "resource://" + res + "/" + FILE_UPDATE_LOCALE;
125 data = await fetch(url);
129 const locale = await data.text();
131 return (this._locale = locale.trim());
137 " file doesn't exist in either the application or GRE directories"
140 return (this._locale = null);
143 /* Get the path to the config file. */
144 getConfigFilePath() {
145 let path = PathUtils.join(
146 Services.dirsvc.get("UpdRootD", Ci.nsIFile).path,
147 FILE_UPDATE_CONFIG_JSON
149 return (this._configFilePath = path);
152 get configFilePath() {
153 if (this._configFilePath !== undefined) {
154 return this._configFilePath;
156 return this.getConfigFilePath();
160 * Determines whether or not the Application Update Service automatically
161 * downloads and installs updates. This corresponds to whether or not the user
162 * has selected "Automatically install updates" in about:preferences.
164 * On Windows, this setting is shared across all profiles for the installation
165 * and is read asynchronously from the file. On other operating systems, this
166 * setting is stored in a pref and is thus a per-profile setting.
168 * @return A Promise that resolves with a boolean.
170 async getAppUpdateAutoEnabled() {
171 return this.readUpdateConfigSetting("app.update.auto");
175 * Toggles whether the Update Service automatically downloads and installs
176 * updates. This effectively selects between the "Automatically install
177 * updates" and "Check for updates but let you choose to install them" options
178 * in about:preferences.
180 * On Windows, this setting is shared across all profiles for the installation
181 * and is written asynchronously to the file. On other operating systems, this
182 * setting is stored in a pref and is thus a per-profile setting.
184 * If this method is called when the setting is locked, the returned promise
185 * will reject. The lock status can be determined with
186 * UpdateUtils.appUpdateAutoSettingIsLocked()
188 * @param enabled If set to true, automatic download and installation of
189 * updates will be enabled. If set to false, this will be
191 * @return A Promise that, once the setting has been saved, resolves with the
192 * boolean value that was saved. If the setting could not be
193 * successfully saved, the Promise will reject.
194 * On Windows, where this setting is stored in a file, this Promise
195 * may reject with an I/O error.
196 * On other operating systems, this promise should not reject as
197 * this operation simply sets a pref.
199 async setAppUpdateAutoEnabled(enabledValue) {
200 return this.writeUpdateConfigSetting("app.update.auto", !!enabledValue);
204 * This function should be used to determine if the automatic application
205 * update setting is locked by an enterprise policy
207 * @return true if the automatic update setting is currently locked.
210 appUpdateAutoSettingIsLocked() {
211 return this.appUpdateSettingIsLocked("app.update.auto");
215 * Indicates whether or not per-installation prefs are supported on this
218 PER_INSTALLATION_PREFS_SUPPORTED: PER_INSTALLATION_PREFS_PLATFORMS.includes(
219 AppConstants.platform
223 * Possible per-installation pref types.
225 PER_INSTALLATION_PREF_TYPE_BOOL: "boolean",
226 PER_INSTALLATION_PREF_TYPE_ASCII_STRING: "ascii",
227 PER_INSTALLATION_PREF_TYPE_INT: "integer",
230 * We want the preference definitions to be part of UpdateUtils for a couple
231 * of reasons. It's a clean way for consumers to look up things like observer
232 * topic names. It also allows us to manipulate the supported prefs during
233 * testing. However, we want to use values out of UpdateUtils (like pref
234 * types) to construct this object. Therefore, this will initially be a
235 * placeholder, which we will properly define after the UpdateUtils object
238 PER_INSTALLATION_PREFS: null,
241 * This function initializes per-installation prefs. Note that it does not
242 * need to be called manually; it is already called within the file.
244 * This function is called on startup, so it does not read or write to disk.
246 initPerInstallPrefs() {
247 // If we don't have per-installation prefs, we store the update config in
248 // preferences. In that case, the best way to notify observers of this
249 // setting is just to propagate it from a pref observer. This ensures that
250 // the expected observers still get notified, even if a user manually
251 // changes the pref value.
252 if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) {
253 let initialConfig = {};
254 for (const [prefName, pref] of Object.entries(
255 UpdateUtils.PER_INSTALLATION_PREFS
257 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
260 let initialValue = prefTypeFns.getProfilePref(prefName);
261 initialConfig[prefName] = initialValue;
264 Services.prefs.addObserver(prefName, async (subject, topic, data) => {
265 let config = { ...gUpdateConfigCache };
266 config[prefName] = await UpdateUtils.readUpdateConfigSetting(
269 maybeUpdateConfigChanged(config);
273 // On the first call to maybeUpdateConfigChanged, it has nothing to
274 // compare its input to, so it just populates the cache and doesn't notify
275 // any observers. This makes sense during normal usage, because the first
276 // call will be on the first config file read, and we don't want to notify
277 // observers of changes on the first read. But that means that when
278 // propagating pref observers, we need to make one initial call to
279 // simulate that initial read so that the cache will be populated when the
280 // first pref observer fires.
281 maybeUpdateConfigChanged(initialConfig);
286 * Reads an installation-specific configuration setting from the update config
287 * JSON file. This function is guaranteed not to throw. If there are problems
288 * reading the file, the default value will be returned so that update can
289 * proceed. This is particularly important since the configuration file is
290 * writable by anyone and we don't want an unprivileged user to be able to
291 * break update for other users.
293 * If relevant policies are active, this function will read the policy value
294 * rather than the stored value.
297 * The preference to read. Must be a key of the
298 * PER_INSTALLATION_PREFS object.
299 * @return A Promise that resolves with the pref's value.
301 readUpdateConfigSetting(prefName) {
302 if (!(prefName in this.PER_INSTALLATION_PREFS)) {
303 return Promise.reject(
305 `UpdateUtils.readUpdateConfigSetting: Unknown per-installation ` +
311 const pref = this.PER_INSTALLATION_PREFS[prefName];
312 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
314 if (Services.policies && "policyFn" in pref) {
315 let policyValue = pref.policyFn();
316 if (policyValue !== null) {
317 return Promise.resolve(policyValue);
321 if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
322 // If we don't have per-installation prefs, we use regular preferences.
323 let prefValue = prefTypeFns.getProfilePref(prefName, pref.defaultValue);
324 return Promise.resolve(prefValue);
327 let readPromise = updateConfigIOPromise
328 // All promises returned by (read|write)UpdateConfigSetting are part of a
329 // single promise chain in order to serialize disk operations. But we
330 // don't want the entire promise chain to reject when one operation fails.
331 // So we are going to silently clear any rejections the promise chain
334 // We will also pass an empty function for the first then() argument as
335 // well, just to make sure we are starting fresh rather than potentially
336 // propagating some stale value.
341 .then(readUpdateConfig)
342 .then(maybeUpdateConfigChanged)
344 return readEffectiveValue(config, prefName);
346 updateConfigIOPromise = readPromise;
351 * Changes an installation-specific configuration setting by writing it to
352 * the update config JSON file.
354 * If this method is called on a prefName that is locked, the returned promise
355 * will reject. The lock status can be determined with
356 * appUpdateSettingIsLocked().
359 * The preference to change. This must be a key of the
360 * PER_INSTALLATION_PREFS object.
362 * The value to be written. Its type must match
363 * PER_INSTALLATION_PREFS[prefName].type
365 * Optional. An object containing any of the following keys:
367 * If set to true, the default branch value will be set rather
368 * than user value. If a user value is set for this pref, this
369 * will have no effect on the pref's effective value.
370 * NOTE - The behavior of the default pref branch currently
371 * differs depending on whether the current platform
372 * supports per-installation prefs. If they are
373 * supported, default branch values persist across
374 * Firefox sessions. If they aren't supported, default
375 * branch values reset when Firefox shuts down.
376 * @return A Promise that, once the setting has been saved, resolves with the
377 * value that was saved.
378 * @throw If there is an I/O error when attempting to write to the config
379 * file, the returned Promise will reject with a DOMException.
381 writeUpdateConfigSetting(prefName, value, options) {
382 if (!(prefName in this.PER_INSTALLATION_PREFS)) {
383 return Promise.reject(
385 `UpdateUtils.writeUpdateConfigSetting: Unknown per-installation ` +
391 if (this.appUpdateSettingIsLocked(prefName)) {
392 return Promise.reject(
394 `UpdateUtils.writeUpdateConfigSetting: Unable to change value of ` +
395 `setting '${prefName}' because it is locked by policy`
404 const pref = this.PER_INSTALLATION_PREFS[prefName];
405 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
407 if (!prefTypeFns.isValid(value)) {
408 return Promise.reject(
410 `UpdateUtils.writeUpdateConfigSetting: Attempted to change pref ` +
411 `'${prefName} to invalid value: ${JSON.stringify(value)}`
416 if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
417 // If we don't have per-installation prefs, we use regular preferences.
418 if (options.setDefaultOnly) {
419 prefTypeFns.setProfileDefaultPref(prefName, value);
421 prefTypeFns.setProfilePref(prefName, value);
423 // Rather than call maybeUpdateConfigChanged, a pref observer has
424 // been connected to the relevant pref. This allows us to catch direct
425 // changes to prefs (which Firefox shouldn't be doing, but the user
426 // might do in about:config).
427 return Promise.resolve(value);
430 let writePromise = updateConfigIOPromise
431 // All promises returned by (read|write)UpdateConfigSetting are part of a
432 // single promise chain in order to serialize disk operations. But we
433 // don't want the entire promise chain to reject when one operation fails.
434 // So we are going to silently clear any rejections the promise chain
437 // We will also pass an empty function for the first then() argument as
438 // well, just to make sure we are starting fresh rather than potentially
439 // propagating some stale value.
444 // We always re-read the update config before writing, rather than using a
445 // cached version. Otherwise, two simultaneous instances may overwrite
446 // each other's changes.
447 .then(readUpdateConfig)
448 .then(async config => {
449 setConfigValue(config, prefName, value, {
450 setDefaultOnly: !!options.setDefaultOnly,
454 await writeUpdateConfig(config);
458 "UpdateUtils.writeUpdateConfigSetting: App update configuration " +
459 "file write failed. Exception: ",
462 // Re-throw the error so the caller knows that writing the value in
463 // the app update config file failed.
467 .then(maybeUpdateConfigChanged)
469 // If this value wasn't written, a previous promise in the chain will
470 // have thrown, so we can unconditionally return the expected written
471 // value as the value that was written.
474 updateConfigIOPromise = writePromise;
479 * Returns true if the specified pref is controlled by policy and thus should
480 * not be changeable by the user.
482 appUpdateSettingIsLocked(prefName) {
483 if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
484 return Promise.reject(
486 `UpdateUtils.appUpdateSettingIsLocked: Unknown per-installation pref '${prefName}'`
491 // If we don't have policy support, nothing can be locked.
492 if (!Services.policies) {
496 const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
497 if (!pref.policyFn) {
500 const policyValue = pref.policyFn();
501 return policyValue !== null;
505 const PER_INSTALLATION_DEFAULTS_BRANCH = "__DEFAULTS__";
508 * Some prefs are specific to the installation, not the profile. They are
509 * stored in JSON format in FILE_UPDATE_CONFIG_JSON.
510 * Not all platforms currently support per-installation prefs, in which case
511 * we fall back to using profile-specific prefs.
513 * Note: These prefs should always be accessed through UpdateUtils. Do NOT
514 * attempt to read or write their prefs directly.
516 * Keys in this object should be the name of the pref. The same name will be
517 * used whether we are writing it to the per-installation or per-profile pref.
518 * Values in this object should be objects with the following keys:
520 * Must be one of the Update.PER_INSTALLATION_PREF_TYPE_* values, defined
523 * The default value to use for this pref if no value is set. This must be
524 * of a type that is compatible with the type value specified.
526 * Optional - defaults to false. A boolean indicating whether an existing
527 * value in the profile-specific prefs ought to be migrated to an
528 * installation specific pref. This is useful for prefs like
529 * app.update.auto that used to be profile-specific prefs.
530 * Note - Migration currently happens only on the creation of the JSON
531 * file. If we want to add more prefs that require migration, we
532 * will probably need to change this.
534 * When a config value is changed, an observer will be fired, much like
535 * the existing preference observers. This specifies the topic of the
536 * observer that will be fired.
538 * Optional. If defined, should be a function that returns null or a value
539 * of the specified type of this pref. If null is returned, this has no
540 * effect. If another value is returned, it will be used rather than
541 * reading the pref. This function will only be called if
542 * Services.policies is defined. Asynchronous functions are not currently
545 UpdateUtils.PER_INSTALLATION_PREFS = {
547 type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL,
550 observerTopic: "auto-update-config-change",
552 if (!Services.policies.isAllowed("app-auto-updates-off")) {
553 // We aren't allowed to turn off auto-update - it is forced on.
556 if (!Services.policies.isAllowed("app-auto-updates-on")) {
557 // We aren't allowed to turn on auto-update - it is forced off.
563 "app.update.background.enabled": {
564 type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL,
566 observerTopic: "background-update-config-change",
568 if (!Services.policies.isAllowed("app-background-update-off")) {
569 // We aren't allowed to turn off background update - it is forced on.
572 if (!Services.policies.isAllowed("app-background-update-on")) {
573 // We aren't allowed to turn on background update - it is forced off.
581 const TYPE_SPECIFIC_PREF_FNS = {
582 [UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL]: {
583 getProfilePref: Services.prefs.getBoolPref,
584 setProfilePref: Services.prefs.setBoolPref,
585 setProfileDefaultPref: (pref, value) => {
586 let defaults = Services.prefs.getDefaultBranch("");
587 defaults.setBoolPref(pref, value);
589 isValid: value => typeof value == "boolean",
591 [UpdateUtils.PER_INSTALLATION_PREF_TYPE_ASCII_STRING]: {
592 getProfilePref: Services.prefs.getCharPref,
593 setProfilePref: Services.prefs.setCharPref,
594 setProfileDefaultPref: (pref, value) => {
595 let defaults = Services.prefs.getDefaultBranch("");
596 defaults.setCharPref(pref, value);
598 isValid: value => typeof value == "string",
600 [UpdateUtils.PER_INSTALLATION_PREF_TYPE_INT]: {
601 getProfilePref: Services.prefs.getIntPref,
602 setProfilePref: Services.prefs.setIntPref,
603 setProfileDefaultPref: (pref, value) => {
604 let defaults = Services.prefs.getDefaultBranch("");
605 defaults.setIntPref(pref, value);
607 isValid: value => Number.isInteger(value),
612 * Used for serializing reads and writes of the app update json config file so
613 * the writes don't happen out of order and the last write is the one that
614 * the sets the value.
616 var updateConfigIOPromise = Promise.resolve();
619 * Returns a pref name that we will use to keep track of if the passed pref has
620 * been migrated already, so we don't end up migrating it twice.
622 function getPrefMigratedPref(prefName) {
623 return prefName + ".migrated";
627 * @return true if prefs need to be migrated from profile-specific prefs to
628 * installation-specific prefs.
630 function updateConfigNeedsMigration() {
631 for (const [prefName, pref] of Object.entries(
632 UpdateUtils.PER_INSTALLATION_PREFS
635 let migratedPrefName = getPrefMigratedPref(prefName);
636 let migrated = Services.prefs.getBoolPref(migratedPrefName, false);
645 function setUpdateConfigMigrationDone() {
646 for (const [prefName, pref] of Object.entries(
647 UpdateUtils.PER_INSTALLATION_PREFS
650 let migratedPrefName = getPrefMigratedPref(prefName);
651 Services.prefs.setBoolPref(migratedPrefName, true);
657 * Deletes the migrated data.
659 function onMigrationSuccessful() {
660 for (const [prefName, pref] of Object.entries(
661 UpdateUtils.PER_INSTALLATION_PREFS
664 Services.prefs.clearUserPref(prefName);
669 function makeMigrationUpdateConfig() {
670 let config = makeDefaultUpdateConfig();
672 for (const [prefName, pref] of Object.entries(
673 UpdateUtils.PER_INSTALLATION_PREFS
678 let migratedPrefName = getPrefMigratedPref(prefName);
679 let alreadyMigrated = Services.prefs.getBoolPref(migratedPrefName, false);
680 if (alreadyMigrated) {
684 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
686 let prefHasValue = true;
689 // Without a second argument, this will throw if the pref has no user
690 // value or default value.
691 prefValue = prefTypeFns.getProfilePref(prefName);
693 prefHasValue = false;
696 setConfigValue(config, prefName, prefValue);
703 function makeDefaultUpdateConfig() {
706 for (const [prefName, pref] of Object.entries(
707 UpdateUtils.PER_INSTALLATION_PREFS
709 setConfigValue(config, prefName, pref.defaultValue, {
710 setDefaultOnly: true,
718 * Sets the specified value in the config object.
721 * The config object for which to set the value
723 * The name of the preference to set.
725 * The value to set the preference to.
727 * Optional. An object containing any of the following keys:
729 * If set to true, the default value will be set rather than
730 * user value. If a user value is set for this pref, this will
731 * have no effect on the pref's effective value.
733 function setConfigValue(config, prefName, prefValue, options) {
738 if (options.setDefaultOnly) {
739 if (!(PER_INSTALLATION_DEFAULTS_BRANCH in config)) {
740 config[PER_INSTALLATION_DEFAULTS_BRANCH] = {};
742 config[PER_INSTALLATION_DEFAULTS_BRANCH][prefName] = prefValue;
743 } else if (prefValue != readDefaultValue(config, prefName)) {
744 config[prefName] = prefValue;
746 delete config[prefName];
751 * Reads the specified pref out of the given configuration object.
752 * If a user value of the pref is set, that will be returned. If only a default
753 * branch value is set, that will be returned. Otherwise, the default value from
754 * PER_INSTALLATION_PREFS will be returned.
756 * Values will be validated before being returned. Invalid values are ignored.
759 * The configuration object to read.
761 * The name of the preference to read.
762 * @return The value of the preference.
764 function readEffectiveValue(config, prefName) {
765 if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
767 `readEffectiveValue: Unknown per-installation pref '${prefName}'`
770 const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
771 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
773 if (prefName in config) {
774 if (prefTypeFns.isValid(config[prefName])) {
775 return config[prefName];
778 `readEffectiveValue: Got invalid value for update config's` +
779 ` '${prefName}' value: "${config[prefName]}"`
782 return readDefaultValue(config, prefName);
786 * Reads the default branch pref out of the given configuration object. If one
787 * is not set, the default value from PER_INSTALLATION_PREFS will be returned.
789 * Values will be validated before being returned. Invalid values are ignored.
792 * The configuration object to read.
794 * The name of the preference to read.
795 * @return The value of the preference.
797 function readDefaultValue(config, prefName) {
798 if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
800 `readDefaultValue: Unknown per-installation pref '${prefName}'`
803 const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
804 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
806 if (PER_INSTALLATION_DEFAULTS_BRANCH in config) {
807 let defaults = config[PER_INSTALLATION_DEFAULTS_BRANCH];
808 if (prefName in defaults) {
809 if (prefTypeFns.isValid(defaults[prefName])) {
810 return defaults[prefName];
813 `readEffectiveValue: Got invalid default value for update` +
814 ` config's '${prefName}' value: "${defaults[prefName]}"`
818 return pref.defaultValue;
822 * Reads the update config and, if necessary, performs migration of un-migrated
823 * values. We don't want to completely give up on update if this file is
824 * unavailable, so default values will be returned on failure rather than
827 * @return An Update Config object.
829 async function readUpdateConfig() {
831 let config = await IOUtils.readJSON(UpdateUtils.getConfigFilePath());
833 // We only migrate once. If we read something, the migration has already
834 // happened so we should make sure it doesn't happen again.
835 setUpdateConfigMigrationDone();
839 if (DOMException.isInstance(e) && e.name == "NotFoundError") {
840 if (updateConfigNeedsMigration()) {
841 const migrationConfig = makeMigrationUpdateConfig();
842 setUpdateConfigMigrationDone();
844 await writeUpdateConfig(migrationConfig);
845 onMigrationSuccessful();
846 return migrationConfig;
848 console.error("readUpdateConfig: Migration failed: ", e);
852 // We only migrate once. If we got an error other than the file not
853 // existing, the migration has already happened so we should make sure
854 // it doesn't happen again.
855 setUpdateConfigMigrationDone();
858 "readUpdateConfig: Unable to read app update configuration file. " +
863 return makeDefaultUpdateConfig();
868 * Writes the given configuration to the disk.
871 * The configuration object to write.
872 * @return The configuration object written.
873 * @throw A DOMException will be thrown on I/O error.
875 async function writeUpdateConfig(config) {
876 let path = UpdateUtils.getConfigFilePath();
877 await IOUtils.writeJSON(path, config, { tmpPath: `${path}.tmp` });
881 var gUpdateConfigCache;
883 * Notifies observers if any update config prefs have changed.
886 * The most up-to-date config object.
887 * @return The same config object that was passed in.
889 function maybeUpdateConfigChanged(config) {
890 if (!gUpdateConfigCache) {
891 // We don't want to generate a change notification for every pref on the
892 // first read of the session.
893 gUpdateConfigCache = config;
897 for (const [prefName, pref] of Object.entries(
898 UpdateUtils.PER_INSTALLATION_PREFS
900 let newPrefValue = readEffectiveValue(config, prefName);
901 let oldPrefValue = readEffectiveValue(gUpdateConfigCache, prefName);
902 if (newPrefValue != oldPrefValue) {
903 Services.obs.notifyObservers(
906 newPrefValue.toString()
911 gUpdateConfigCache = config;
916 * Note that this function sets up observers only, it does not do any I/O.
918 UpdateUtils.initPerInstallPrefs();
920 /* Get the distribution pref values, from defaults only */
921 function getDistributionPrefValue(aPrefName) {
922 let value = Services.prefs
923 .getDefaultBranch(null)
924 .getCharPref(aPrefName, "default");
931 function getSystemCapabilities() {
932 return "ISET:" + lazy.gInstructionSet + ",MEM:" + getMemoryMB();
936 * Gets the RAM size in megabytes. This will round the value because sysinfo
937 * doesn't always provide RAM in multiples of 1024.
939 function getMemoryMB() {
940 let memoryMB = "unknown";
942 memoryMB = Services.sysinfo.getProperty("memsize");
944 memoryMB = Math.round(memoryMB / 1024 / 1024);
947 console.error("Error getting system info memsize property. Exception: ", e);
953 * Gets the supported CPU instruction set.
955 XPCOMUtils.defineLazyGetter(lazy, "gInstructionSet", function aus_gIS() {
956 const CPU_EXTENSIONS = [
969 for (let ext of CPU_EXTENSIONS) {
970 if (Services.sysinfo.getProperty(ext)) {
971 return ext.substring(3);
978 /* Windows only getter that returns the processor architecture. */
979 XPCOMUtils.defineLazyGetter(lazy, "gWinCPUArch", function aus_gWinCPUArch() {
980 // Get processor architecture
981 let arch = "unknown";
983 const WORD = lazy.ctypes.uint16_t;
984 const DWORD = lazy.ctypes.uint32_t;
986 // This structure is described at:
987 // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
988 const SYSTEM_INFO = new lazy.ctypes.StructType("SYSTEM_INFO", [
989 { wProcessorArchitecture: WORD },
991 { dwPageSize: DWORD },
992 { lpMinimumApplicationAddress: lazy.ctypes.voidptr_t },
993 { lpMaximumApplicationAddress: lazy.ctypes.voidptr_t },
994 { dwActiveProcessorMask: DWORD.ptr },
995 { dwNumberOfProcessors: DWORD },
996 { dwProcessorType: DWORD },
997 { dwAllocationGranularity: DWORD },
998 { wProcessorLevel: WORD },
999 { wProcessorRevision: WORD },
1002 let kernel32 = false;
1004 kernel32 = lazy.ctypes.open("Kernel32");
1006 console.error("Unable to open kernel32! Exception: ", e);
1011 let GetNativeSystemInfo = kernel32.declare(
1012 "GetNativeSystemInfo",
1013 lazy.ctypes.winapi_abi,
1017 let winSystemInfo = SYSTEM_INFO();
1018 // Default to unknown
1019 winSystemInfo.wProcessorArchitecture = 0xffff;
1021 GetNativeSystemInfo(winSystemInfo.address());
1022 switch (winSystemInfo.wProcessorArchitecture) {
1037 console.error("Error getting processor architecture. Exception: ", e);
1046 XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function () {
1049 abi = Services.appinfo.XPCOMABI;
1051 console.error("XPCOM ABI unknown");
1054 if (AppConstants.platform == "win") {
1055 // Windows build should report the CPU architecture that it's running on.
1056 abi += "-" + lazy.gWinCPUArch;
1059 if (AppConstants.ASAN) {
1060 // Allow ASan builds to receive their own updates
1067 XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function () {
1071 Services.sysinfo.getProperty("name") +
1073 Services.sysinfo.getProperty("version");
1075 console.error("OS Version unknown.");
1079 if (AppConstants.platform == "win") {
1080 // Add service pack and build number
1082 const { servicePackMajor, servicePackMinor, buildNumber } =
1083 lazy.WindowsVersionInfo.get();
1084 osVersion += `.${servicePackMajor}.${servicePackMinor}.${buildNumber}`;
1086 console.error("Unable to retrieve windows version information: ", err);
1087 osVersion += ".unknown";
1090 // add UBR if on Windows 10
1092 Services.vc.compare(Services.sysinfo.getProperty("version"), "10") >= 0
1094 const WINDOWS_UBR_KEY_PATH =
1095 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
1096 let ubr = lazy.WindowsRegistry.readRegKey(
1097 Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
1098 WINDOWS_UBR_KEY_PATH,
1100 Ci.nsIWindowsRegKey.WOW64_64
1102 if (ubr !== undefined) {
1103 osVersion += `.${ubr}`;
1105 osVersion += ".unknown";
1109 // Add processor architecture
1110 osVersion += " (" + lazy.gWinCPUArch + ")";
1115 " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
1117 // Not all platforms have a secondary widget library, so an error is nothing to worry about.
1119 osVersion = encodeURIComponent(osVersion);