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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
9 ChromeUtils.defineESModuleGetters(lazy, {
10 WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
12 "resource://gre/modules/components-utils/WindowsVersionInfo.sys.mjs",
13 ctypes: "resource://gre/modules/ctypes.sys.mjs",
16 const PER_INSTALLATION_PREFS_PLATFORMS = ["win"];
18 // The file that stores Application Update configuration settings. The file is
19 // located in the update directory which makes it a common setting across all
20 // application profiles and allows the Background Update Agent to read it.
21 const FILE_UPDATE_CONFIG_JSON = "update-config.json";
22 const FILE_UPDATE_LOCALE = "update.locale";
23 const PREF_APP_DISTRIBUTION = "distribution.id";
24 const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
26 export var UpdateUtils = {
28 _configFilePath: undefined,
31 * Read the update channel from defaults only. We do this to ensure that
32 * the channel is tightly coupled with the application and does not apply
33 * to other instances of the application that may use the same profile.
35 * @param [optional] aIncludePartners
36 * Whether or not to include the partner bits. Default: true.
38 getUpdateChannel(aIncludePartners = true) {
39 let defaults = Services.prefs.getDefaultBranch(null);
40 let channel = defaults.getCharPref(
42 AppConstants.MOZ_UPDATE_CHANNEL
45 if (aIncludePartners) {
47 let partners = Services.prefs.getChildList("app.partner.").sort();
48 if (partners.length) {
50 partners.forEach(function (prefName) {
51 channel += "-" + Services.prefs.getCharPref(prefName);
63 return this.getUpdateChannel();
67 * Formats a URL by replacing %...% values with OS, build and locale specific
72 * @return The formatted URL.
74 async formatUpdateURL(url) {
75 const locale = await this.getLocale();
77 return url.replace(/%(\w+)%/g, (match, name) => {
78 let replacement = match;
81 replacement = Services.appinfo.name;
84 replacement = Services.appinfo.version;
87 replacement = Services.appinfo.appBuildID;
90 replacement = Services.appinfo.OS + "_" + this.ABI;
93 replacement = this.OSVersion;
99 replacement = this.UpdateChannel;
101 case "PLATFORM_VERSION":
102 replacement = Services.appinfo.platformVersion;
104 case "SYSTEM_CAPABILITIES":
105 replacement = getSystemCapabilities();
108 replacement = getDistributionPrefValue(PREF_APP_DISTRIBUTION);
110 case "DISTRIBUTION_VERSION":
111 replacement = getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION);
114 return encodeURIComponent(replacement);
119 * Gets the locale from the update.locale file for replacing %LOCALE% in the
120 * update url. The update.locale file can be located in the application
121 * directory or the GRE directory with preference given to it being located in
122 * the application directory.
125 if (this._locale !== undefined) {
129 for (let res of ["app", "gre"]) {
130 const url = "resource://" + res + "/" + FILE_UPDATE_LOCALE;
133 data = await fetch(url);
137 const locale = await data.text();
139 return (this._locale = locale.trim());
145 " file doesn't exist in either the application or GRE directories"
148 return (this._locale = null);
151 /* Get the path to the config file. */
152 getConfigFilePath() {
153 let path = PathUtils.join(
154 Services.dirsvc.get("UpdRootD", Ci.nsIFile).path,
155 FILE_UPDATE_CONFIG_JSON
157 return (this._configFilePath = path);
160 get configFilePath() {
161 if (this._configFilePath !== undefined) {
162 return this._configFilePath;
164 return this.getConfigFilePath();
168 * Determines whether or not the Application Update Service automatically
169 * downloads and installs updates. This corresponds to whether or not the user
170 * has selected "Automatically install updates" in about:preferences.
172 * On Windows, this setting is shared across all profiles for the installation
173 * and is read asynchronously from the file. On other operating systems, this
174 * setting is stored in a pref and is thus a per-profile setting.
176 * @return A Promise that resolves with a boolean.
178 async getAppUpdateAutoEnabled() {
179 return this.readUpdateConfigSetting("app.update.auto");
183 * Toggles whether the Update Service automatically downloads and installs
184 * updates. This effectively selects between the "Automatically install
185 * updates" and "Check for updates but let you choose to install them" options
186 * in about:preferences.
188 * On Windows, this setting is shared across all profiles for the installation
189 * and is written asynchronously to the file. On other operating systems, this
190 * setting is stored in a pref and is thus a per-profile setting.
192 * If this method is called when the setting is locked, the returned promise
193 * will reject. The lock status can be determined with
194 * UpdateUtils.appUpdateAutoSettingIsLocked()
196 * @param enabled If set to true, automatic download and installation of
197 * updates will be enabled. If set to false, this will be
199 * @return A Promise that, once the setting has been saved, resolves with the
200 * boolean value that was saved. If the setting could not be
201 * successfully saved, the Promise will reject.
202 * On Windows, where this setting is stored in a file, this Promise
203 * may reject with an I/O error.
204 * On other operating systems, this promise should not reject as
205 * this operation simply sets a pref.
207 async setAppUpdateAutoEnabled(enabledValue) {
208 return this.writeUpdateConfigSetting("app.update.auto", !!enabledValue);
212 * This function should be used to determine if the automatic application
213 * update setting is locked by an enterprise policy
215 * @return true if the automatic update setting is currently locked.
218 appUpdateAutoSettingIsLocked() {
219 return this.appUpdateSettingIsLocked("app.update.auto");
223 * Indicates whether or not per-installation prefs are supported on this
226 PER_INSTALLATION_PREFS_SUPPORTED: PER_INSTALLATION_PREFS_PLATFORMS.includes(
227 AppConstants.platform
231 * Possible per-installation pref types.
233 PER_INSTALLATION_PREF_TYPE_BOOL: "boolean",
234 PER_INSTALLATION_PREF_TYPE_ASCII_STRING: "ascii",
235 PER_INSTALLATION_PREF_TYPE_INT: "integer",
238 * We want the preference definitions to be part of UpdateUtils for a couple
239 * of reasons. It's a clean way for consumers to look up things like observer
240 * topic names. It also allows us to manipulate the supported prefs during
241 * testing. However, we want to use values out of UpdateUtils (like pref
242 * types) to construct this object. Therefore, this will initially be a
243 * placeholder, which we will properly define after the UpdateUtils object
246 PER_INSTALLATION_PREFS: null,
249 * This function initializes per-installation prefs. Note that it does not
250 * need to be called manually; it is already called within the file.
252 * This function is called on startup, so it does not read or write to disk.
254 initPerInstallPrefs() {
255 // If we don't have per-installation prefs, we store the update config in
256 // preferences. In that case, the best way to notify observers of this
257 // setting is just to propagate it from a pref observer. This ensures that
258 // the expected observers still get notified, even if a user manually
259 // changes the pref value.
260 if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) {
261 let initialConfig = {};
262 for (const [prefName, pref] of Object.entries(
263 UpdateUtils.PER_INSTALLATION_PREFS
265 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
268 let initialValue = prefTypeFns.getProfilePref(prefName);
269 initialConfig[prefName] = initialValue;
272 Services.prefs.addObserver(prefName, async (subject, topic, data) => {
273 let config = { ...gUpdateConfigCache };
274 config[prefName] = await UpdateUtils.readUpdateConfigSetting(
277 maybeUpdateConfigChanged(config);
281 // On the first call to maybeUpdateConfigChanged, it has nothing to
282 // compare its input to, so it just populates the cache and doesn't notify
283 // any observers. This makes sense during normal usage, because the first
284 // call will be on the first config file read, and we don't want to notify
285 // observers of changes on the first read. But that means that when
286 // propagating pref observers, we need to make one initial call to
287 // simulate that initial read so that the cache will be populated when the
288 // first pref observer fires.
289 maybeUpdateConfigChanged(initialConfig);
294 * Reads an installation-specific configuration setting from the update config
295 * JSON file. This function is guaranteed not to throw. If there are problems
296 * reading the file, the default value will be returned so that update can
297 * proceed. This is particularly important since the configuration file is
298 * writable by anyone and we don't want an unprivileged user to be able to
299 * break update for other users.
301 * If relevant policies are active, this function will read the policy value
302 * rather than the stored value.
305 * The preference to read. Must be a key of the
306 * PER_INSTALLATION_PREFS object.
307 * @return A Promise that resolves with the pref's value.
309 readUpdateConfigSetting(prefName) {
310 if (!(prefName in this.PER_INSTALLATION_PREFS)) {
311 return Promise.reject(
313 `UpdateUtils.readUpdateConfigSetting: Unknown per-installation ` +
319 const pref = this.PER_INSTALLATION_PREFS[prefName];
320 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
322 if (Services.policies && "policyFn" in pref) {
323 let policyValue = pref.policyFn();
324 if (policyValue !== null) {
325 return Promise.resolve(policyValue);
329 if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
330 // If we don't have per-installation prefs, we use regular preferences.
331 let prefValue = prefTypeFns.getProfilePref(prefName, pref.defaultValue);
332 return Promise.resolve(prefValue);
335 let readPromise = updateConfigIOPromise
336 // All promises returned by (read|write)UpdateConfigSetting are part of a
337 // single promise chain in order to serialize disk operations. But we
338 // don't want the entire promise chain to reject when one operation fails.
339 // So we are going to silently clear any rejections the promise chain
342 // We will also pass an empty function for the first then() argument as
343 // well, just to make sure we are starting fresh rather than potentially
344 // propagating some stale value.
349 .then(readUpdateConfig)
350 .then(maybeUpdateConfigChanged)
352 return readEffectiveValue(config, prefName);
354 updateConfigIOPromise = readPromise;
359 * Changes an installation-specific configuration setting by writing it to
360 * the update config JSON file.
362 * If this method is called on a prefName that is locked, the returned promise
363 * will reject. The lock status can be determined with
364 * appUpdateSettingIsLocked().
367 * The preference to change. This must be a key of the
368 * PER_INSTALLATION_PREFS object.
370 * The value to be written. Its type must match
371 * PER_INSTALLATION_PREFS[prefName].type
373 * Optional. An object containing any of the following keys:
375 * If set to true, the default branch value will be set rather
376 * than user value. If a user value is set for this pref, this
377 * will have no effect on the pref's effective value.
378 * NOTE - The behavior of the default pref branch currently
379 * differs depending on whether the current platform
380 * supports per-installation prefs. If they are
381 * supported, default branch values persist across
382 * Firefox sessions. If they aren't supported, default
383 * branch values reset when Firefox shuts down.
384 * @return A Promise that, once the setting has been saved, resolves with the
385 * value that was saved.
386 * @throw If there is an I/O error when attempting to write to the config
387 * file, the returned Promise will reject with a DOMException.
389 writeUpdateConfigSetting(prefName, value, options) {
390 if (!(prefName in this.PER_INSTALLATION_PREFS)) {
391 return Promise.reject(
393 `UpdateUtils.writeUpdateConfigSetting: Unknown per-installation ` +
399 if (this.appUpdateSettingIsLocked(prefName)) {
400 return Promise.reject(
402 `UpdateUtils.writeUpdateConfigSetting: Unable to change value of ` +
403 `setting '${prefName}' because it is locked by policy`
412 const pref = this.PER_INSTALLATION_PREFS[prefName];
413 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
415 if (!prefTypeFns.isValid(value)) {
416 return Promise.reject(
418 `UpdateUtils.writeUpdateConfigSetting: Attempted to change pref ` +
419 `'${prefName} to invalid value: ${JSON.stringify(value)}`
424 if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
425 // If we don't have per-installation prefs, we use regular preferences.
426 if (options.setDefaultOnly) {
427 prefTypeFns.setProfileDefaultPref(prefName, value);
429 prefTypeFns.setProfilePref(prefName, value);
431 // Rather than call maybeUpdateConfigChanged, a pref observer has
432 // been connected to the relevant pref. This allows us to catch direct
433 // changes to prefs (which Firefox shouldn't be doing, but the user
434 // might do in about:config).
435 return Promise.resolve(value);
438 let writePromise = updateConfigIOPromise
439 // All promises returned by (read|write)UpdateConfigSetting are part of a
440 // single promise chain in order to serialize disk operations. But we
441 // don't want the entire promise chain to reject when one operation fails.
442 // So we are going to silently clear any rejections the promise chain
445 // We will also pass an empty function for the first then() argument as
446 // well, just to make sure we are starting fresh rather than potentially
447 // propagating some stale value.
452 // We always re-read the update config before writing, rather than using a
453 // cached version. Otherwise, two simultaneous instances may overwrite
454 // each other's changes.
455 .then(readUpdateConfig)
456 .then(async config => {
457 setConfigValue(config, prefName, value, {
458 setDefaultOnly: !!options.setDefaultOnly,
462 await writeUpdateConfig(config);
466 "UpdateUtils.writeUpdateConfigSetting: App update configuration " +
467 "file write failed. Exception: ",
470 // Re-throw the error so the caller knows that writing the value in
471 // the app update config file failed.
475 .then(maybeUpdateConfigChanged)
477 // If this value wasn't written, a previous promise in the chain will
478 // have thrown, so we can unconditionally return the expected written
479 // value as the value that was written.
482 updateConfigIOPromise = writePromise;
487 * Returns true if the specified pref is controlled by policy and thus should
488 * not be changeable by the user.
490 appUpdateSettingIsLocked(prefName) {
491 if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
492 return Promise.reject(
494 `UpdateUtils.appUpdateSettingIsLocked: Unknown per-installation pref '${prefName}'`
499 // If we don't have policy support, nothing can be locked.
500 if (!Services.policies) {
504 const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
505 if (!pref.policyFn) {
508 const policyValue = pref.policyFn();
509 return policyValue !== null;
513 const PER_INSTALLATION_DEFAULTS_BRANCH = "__DEFAULTS__";
516 * Some prefs are specific to the installation, not the profile. They are
517 * stored in JSON format in FILE_UPDATE_CONFIG_JSON.
518 * Not all platforms currently support per-installation prefs, in which case
519 * we fall back to using profile-specific prefs.
521 * Note: These prefs should always be accessed through UpdateUtils. Do NOT
522 * attempt to read or write their prefs directly.
524 * Keys in this object should be the name of the pref. The same name will be
525 * used whether we are writing it to the per-installation or per-profile pref.
526 * Values in this object should be objects with the following keys:
528 * Must be one of the Update.PER_INSTALLATION_PREF_TYPE_* values, defined
531 * The default value to use for this pref if no value is set. This must be
532 * of a type that is compatible with the type value specified.
534 * Optional - defaults to false. A boolean indicating whether an existing
535 * value in the profile-specific prefs ought to be migrated to an
536 * installation specific pref. This is useful for prefs like
537 * app.update.auto that used to be profile-specific prefs.
538 * Note - Migration currently happens only on the creation of the JSON
539 * file. If we want to add more prefs that require migration, we
540 * will probably need to change this.
542 * When a config value is changed, an observer will be fired, much like
543 * the existing preference observers. This specifies the topic of the
544 * observer that will be fired.
546 * Optional. If defined, should be a function that returns null or a value
547 * of the specified type of this pref. If null is returned, this has no
548 * effect. If another value is returned, it will be used rather than
549 * reading the pref. This function will only be called if
550 * Services.policies is defined. Asynchronous functions are not currently
553 UpdateUtils.PER_INSTALLATION_PREFS = {
555 type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL,
558 observerTopic: "auto-update-config-change",
560 if (!Services.policies.isAllowed("app-auto-updates-off")) {
561 // We aren't allowed to turn off auto-update - it is forced on.
564 if (!Services.policies.isAllowed("app-auto-updates-on")) {
565 // We aren't allowed to turn on auto-update - it is forced off.
571 "app.update.background.enabled": {
572 type: UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL,
574 observerTopic: "background-update-config-change",
576 if (!Services.policies.isAllowed("app-background-update-off")) {
577 // We aren't allowed to turn off background update - it is forced on.
580 if (!Services.policies.isAllowed("app-background-update-on")) {
581 // We aren't allowed to turn on background update - it is forced off.
589 const TYPE_SPECIFIC_PREF_FNS = {
590 [UpdateUtils.PER_INSTALLATION_PREF_TYPE_BOOL]: {
591 getProfilePref: Services.prefs.getBoolPref,
592 setProfilePref: Services.prefs.setBoolPref,
593 setProfileDefaultPref: (pref, value) => {
594 let defaults = Services.prefs.getDefaultBranch("");
595 defaults.setBoolPref(pref, value);
597 isValid: value => typeof value == "boolean",
599 [UpdateUtils.PER_INSTALLATION_PREF_TYPE_ASCII_STRING]: {
600 getProfilePref: Services.prefs.getCharPref,
601 setProfilePref: Services.prefs.setCharPref,
602 setProfileDefaultPref: (pref, value) => {
603 let defaults = Services.prefs.getDefaultBranch("");
604 defaults.setCharPref(pref, value);
606 isValid: value => typeof value == "string",
608 [UpdateUtils.PER_INSTALLATION_PREF_TYPE_INT]: {
609 getProfilePref: Services.prefs.getIntPref,
610 setProfilePref: Services.prefs.setIntPref,
611 setProfileDefaultPref: (pref, value) => {
612 let defaults = Services.prefs.getDefaultBranch("");
613 defaults.setIntPref(pref, value);
615 isValid: value => Number.isInteger(value),
620 * Used for serializing reads and writes of the app update json config file so
621 * the writes don't happen out of order and the last write is the one that
622 * the sets the value.
624 var updateConfigIOPromise = Promise.resolve();
627 * Returns a pref name that we will use to keep track of if the passed pref has
628 * been migrated already, so we don't end up migrating it twice.
630 function getPrefMigratedPref(prefName) {
631 return prefName + ".migrated";
635 * @return true if prefs need to be migrated from profile-specific prefs to
636 * installation-specific prefs.
638 function updateConfigNeedsMigration() {
639 for (const [prefName, pref] of Object.entries(
640 UpdateUtils.PER_INSTALLATION_PREFS
643 let migratedPrefName = getPrefMigratedPref(prefName);
644 let migrated = Services.prefs.getBoolPref(migratedPrefName, false);
653 function setUpdateConfigMigrationDone() {
654 for (const [prefName, pref] of Object.entries(
655 UpdateUtils.PER_INSTALLATION_PREFS
658 let migratedPrefName = getPrefMigratedPref(prefName);
659 Services.prefs.setBoolPref(migratedPrefName, true);
665 * Deletes the migrated data.
667 function onMigrationSuccessful() {
668 for (const [prefName, pref] of Object.entries(
669 UpdateUtils.PER_INSTALLATION_PREFS
672 Services.prefs.clearUserPref(prefName);
677 function makeMigrationUpdateConfig() {
678 let config = makeDefaultUpdateConfig();
680 for (const [prefName, pref] of Object.entries(
681 UpdateUtils.PER_INSTALLATION_PREFS
686 let migratedPrefName = getPrefMigratedPref(prefName);
687 let alreadyMigrated = Services.prefs.getBoolPref(migratedPrefName, false);
688 if (alreadyMigrated) {
692 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
694 let prefHasValue = true;
697 // Without a second argument, this will throw if the pref has no user
698 // value or default value.
699 prefValue = prefTypeFns.getProfilePref(prefName);
701 prefHasValue = false;
704 setConfigValue(config, prefName, prefValue);
711 function makeDefaultUpdateConfig() {
714 for (const [prefName, pref] of Object.entries(
715 UpdateUtils.PER_INSTALLATION_PREFS
717 setConfigValue(config, prefName, pref.defaultValue, {
718 setDefaultOnly: true,
726 * Sets the specified value in the config object.
729 * The config object for which to set the value
731 * The name of the preference to set.
733 * The value to set the preference to.
735 * Optional. An object containing any of the following keys:
737 * If set to true, the default value will be set rather than
738 * user value. If a user value is set for this pref, this will
739 * have no effect on the pref's effective value.
741 function setConfigValue(config, prefName, prefValue, options) {
746 if (options.setDefaultOnly) {
747 if (!(PER_INSTALLATION_DEFAULTS_BRANCH in config)) {
748 config[PER_INSTALLATION_DEFAULTS_BRANCH] = {};
750 config[PER_INSTALLATION_DEFAULTS_BRANCH][prefName] = prefValue;
751 } else if (prefValue != readDefaultValue(config, prefName)) {
752 config[prefName] = prefValue;
754 delete config[prefName];
759 * Reads the specified pref out of the given configuration object.
760 * If a user value of the pref is set, that will be returned. If only a default
761 * branch value is set, that will be returned. Otherwise, the default value from
762 * PER_INSTALLATION_PREFS will be returned.
764 * Values will be validated before being returned. Invalid values are ignored.
767 * The configuration object to read.
769 * The name of the preference to read.
770 * @return The value of the preference.
772 function readEffectiveValue(config, prefName) {
773 if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
775 `readEffectiveValue: Unknown per-installation pref '${prefName}'`
778 const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
779 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
781 if (prefName in config) {
782 if (prefTypeFns.isValid(config[prefName])) {
783 return config[prefName];
786 `readEffectiveValue: Got invalid value for update config's` +
787 ` '${prefName}' value: "${config[prefName]}"`
790 return readDefaultValue(config, prefName);
794 * Reads the default branch pref out of the given configuration object. If one
795 * is not set, the default value from PER_INSTALLATION_PREFS will be returned.
797 * Values will be validated before being returned. Invalid values are ignored.
800 * The configuration object to read.
802 * The name of the preference to read.
803 * @return The value of the preference.
805 function readDefaultValue(config, prefName) {
806 if (!(prefName in UpdateUtils.PER_INSTALLATION_PREFS)) {
808 `readDefaultValue: Unknown per-installation pref '${prefName}'`
811 const pref = UpdateUtils.PER_INSTALLATION_PREFS[prefName];
812 const prefTypeFns = TYPE_SPECIFIC_PREF_FNS[pref.type];
814 if (PER_INSTALLATION_DEFAULTS_BRANCH in config) {
815 let defaults = config[PER_INSTALLATION_DEFAULTS_BRANCH];
816 if (prefName in defaults) {
817 if (prefTypeFns.isValid(defaults[prefName])) {
818 return defaults[prefName];
821 `readEffectiveValue: Got invalid default value for update` +
822 ` config's '${prefName}' value: "${defaults[prefName]}"`
826 return pref.defaultValue;
830 * Reads the update config and, if necessary, performs migration of un-migrated
831 * values. We don't want to completely give up on update if this file is
832 * unavailable, so default values will be returned on failure rather than
835 * @return An Update Config object.
837 async function readUpdateConfig() {
839 let config = await IOUtils.readJSON(UpdateUtils.getConfigFilePath());
841 // We only migrate once. If we read something, the migration has already
842 // happened so we should make sure it doesn't happen again.
843 setUpdateConfigMigrationDone();
847 if (DOMException.isInstance(e) && e.name == "NotFoundError") {
848 if (updateConfigNeedsMigration()) {
849 const migrationConfig = makeMigrationUpdateConfig();
850 setUpdateConfigMigrationDone();
852 await writeUpdateConfig(migrationConfig);
853 onMigrationSuccessful();
854 return migrationConfig;
856 console.error("readUpdateConfig: Migration failed: ", e);
860 // We only migrate once. If we got an error other than the file not
861 // existing, the migration has already happened so we should make sure
862 // it doesn't happen again.
863 setUpdateConfigMigrationDone();
866 "readUpdateConfig: Unable to read app update configuration file. " +
871 return makeDefaultUpdateConfig();
876 * Writes the given configuration to the disk.
879 * The configuration object to write.
880 * @return The configuration object written.
881 * @throw A DOMException will be thrown on I/O error.
883 async function writeUpdateConfig(config) {
884 let path = UpdateUtils.getConfigFilePath();
885 await IOUtils.writeJSON(path, config, { tmpPath: `${path}.tmp` });
889 var gUpdateConfigCache;
891 * Notifies observers if any update config prefs have changed.
894 * The most up-to-date config object.
895 * @return The same config object that was passed in.
897 function maybeUpdateConfigChanged(config) {
898 if (!gUpdateConfigCache) {
899 // We don't want to generate a change notification for every pref on the
900 // first read of the session.
901 gUpdateConfigCache = config;
905 for (const [prefName, pref] of Object.entries(
906 UpdateUtils.PER_INSTALLATION_PREFS
908 let newPrefValue = readEffectiveValue(config, prefName);
909 let oldPrefValue = readEffectiveValue(gUpdateConfigCache, prefName);
910 if (newPrefValue != oldPrefValue) {
911 Services.obs.notifyObservers(
914 newPrefValue.toString()
919 gUpdateConfigCache = config;
924 * Note that this function sets up observers only, it does not do any I/O.
926 UpdateUtils.initPerInstallPrefs();
928 /* Get the distribution pref values, from defaults only */
929 function getDistributionPrefValue(aPrefName) {
930 let value = Services.prefs
931 .getDefaultBranch(null)
932 .getCharPref(aPrefName, "default");
939 function getSystemCapabilities() {
940 return "ISET:" + lazy.gInstructionSet + ",MEM:" + getMemoryMB();
944 * Gets the RAM size in megabytes. This will round the value because sysinfo
945 * doesn't always provide RAM in multiples of 1024.
947 function getMemoryMB() {
948 let memoryMB = "unknown";
950 memoryMB = Services.sysinfo.getProperty("memsize");
952 memoryMB = Math.round(memoryMB / 1024 / 1024);
955 console.error("Error getting system info memsize property. Exception: ", e);
961 * Gets the supported CPU instruction set.
963 ChromeUtils.defineLazyGetter(lazy, "gInstructionSet", function aus_gIS() {
964 const CPU_EXTENSIONS = [
977 for (let ext of CPU_EXTENSIONS) {
978 if (Services.sysinfo.getProperty(ext)) {
979 return ext.substring(3);
986 /* Windows only getter that returns the processor architecture. */
987 ChromeUtils.defineLazyGetter(lazy, "gWinCPUArch", function aus_gWinCPUArch() {
988 // Get processor architecture
989 let arch = "unknown";
991 const WORD = lazy.ctypes.uint16_t;
992 const DWORD = lazy.ctypes.uint32_t;
994 // This structure is described at:
995 // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
996 const SYSTEM_INFO = new lazy.ctypes.StructType("SYSTEM_INFO", [
997 { wProcessorArchitecture: WORD },
999 { dwPageSize: DWORD },
1000 { lpMinimumApplicationAddress: lazy.ctypes.voidptr_t },
1001 { lpMaximumApplicationAddress: lazy.ctypes.voidptr_t },
1002 { dwActiveProcessorMask: DWORD.ptr },
1003 { dwNumberOfProcessors: DWORD },
1004 { dwProcessorType: DWORD },
1005 { dwAllocationGranularity: DWORD },
1006 { wProcessorLevel: WORD },
1007 { wProcessorRevision: WORD },
1010 let kernel32 = false;
1012 kernel32 = lazy.ctypes.open("Kernel32");
1014 console.error("Unable to open kernel32! Exception: ", e);
1019 let GetNativeSystemInfo = kernel32.declare(
1020 "GetNativeSystemInfo",
1021 lazy.ctypes.winapi_abi,
1025 let winSystemInfo = SYSTEM_INFO();
1026 // Default to unknown
1027 winSystemInfo.wProcessorArchitecture = 0xffff;
1029 GetNativeSystemInfo(winSystemInfo.address());
1030 switch (winSystemInfo.wProcessorArchitecture) {
1045 console.error("Error getting processor architecture. Exception: ", e);
1054 ChromeUtils.defineLazyGetter(UpdateUtils, "ABI", function () {
1057 abi = Services.appinfo.XPCOMABI;
1059 console.error("XPCOM ABI unknown");
1062 if (AppConstants.platform == "win") {
1063 // Windows build should report the CPU architecture that it's running on.
1064 abi += "-" + lazy.gWinCPUArch;
1067 if (AppConstants.ASAN) {
1068 // Allow ASan builds to receive their own updates
1075 ChromeUtils.defineLazyGetter(UpdateUtils, "OSVersion", function () {
1079 Services.sysinfo.getProperty("name") +
1081 Services.sysinfo.getProperty("version");
1083 console.error("OS Version unknown.");
1087 if (AppConstants.platform == "win") {
1088 // Add service pack and build number
1090 const { servicePackMajor, servicePackMinor, buildNumber } =
1091 lazy.WindowsVersionInfo.get();
1092 osVersion += `.${servicePackMajor}.${servicePackMinor}.${buildNumber}`;
1094 console.error("Unable to retrieve windows version information: ", err);
1095 osVersion += ".unknown";
1098 // add UBR if on Windows 10
1100 Services.vc.compare(Services.sysinfo.getProperty("version"), "10") >= 0
1102 const WINDOWS_UBR_KEY_PATH =
1103 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
1104 let ubr = lazy.WindowsRegistry.readRegKey(
1105 Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
1106 WINDOWS_UBR_KEY_PATH,
1108 Ci.nsIWindowsRegKey.WOW64_64
1110 if (ubr !== undefined) {
1111 osVersion += `.${ubr}`;
1113 osVersion += ".unknown";
1117 // Add processor architecture
1118 osVersion += " (" + lazy.gWinCPUArch + ")";
1123 " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
1125 // Not all platforms have a secondary widget library, so an error is nothing to worry about.
1127 osVersion = encodeURIComponent(osVersion);