InstallerPrefs.sys.mjs (4978B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /** 7 * The installer prefs component provides a way to get a specific set of prefs 8 * from a profile into a place where the installer can read them. The primary 9 * reason for wanting to do this is so we can run things like Shield studies 10 * on installer features; normally those are enabled by setting a pref, but 11 * the installer runs outside of any profile and so has no access to prefs. 12 * So we need to do something else to allow it to read these prefs. 13 * 14 * The mechanism we use here is to reflect the values of a list of relevant 15 * prefs into registry values. One registry value is created for each pref 16 * that is set. Each installation of the product gets its own registry key 17 * (based on the path hash). This is obviously a somewhat wider scope than a 18 * single profile, but it should be close enough in enough cases to suit our 19 * purposes here. 20 * 21 * Currently this module only supports bool prefs. Other types could likely 22 * be added if needed, but it doesn't seem necessary for the primary use case. 23 */ 24 25 // All prefs processed through this component must be in this branch. 26 const INSTALLER_PREFS_BRANCH = "installer."; 27 28 // This is the list of prefs that will be reflected to the registry. It should 29 // be kept up to date so that it reflects the list of prefs that are in 30 // current use (e.g., currently active experiments). 31 // Only add prefs to this list which are in INSTALLER_PREFS_BRANCH; 32 // any others will be ignored. 33 const INSTALLER_PREFS_LIST = ["installer.taskbarpin.win10.enabled"]; 34 35 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 36 37 // This constructor can take a list of prefs to override the default one, 38 // but this is really only intended for tests to use. Normal usage should be 39 // to leave the parameter omitted/undefined. 40 export function InstallerPrefs(prefsList) { 41 this.prefsList = prefsList || INSTALLER_PREFS_LIST; 42 43 // Each pref to be reflected will get a value created under this key, in HKCU. 44 // The path will look something like: 45 // "Software\Mozilla\Firefox\Installer\71AE18FE3142402B\". 46 ChromeUtils.defineLazyGetter(this, "_registryKeyPath", function () { 47 const app = AppConstants.MOZ_APP_NAME; 48 const vendor = Services.appinfo.vendor || "Mozilla"; 49 const xreDirProvider = Cc[ 50 "@mozilla.org/xre/directory-provider;1" 51 ].getService(Ci.nsIXREDirProvider); 52 const installHash = xreDirProvider.getInstallHash(); 53 return `Software\\${vendor}\\${app}\\Installer\\${installHash}`; 54 }); 55 } 56 57 InstallerPrefs.prototype = { 58 classID: Components.ID("{cd8a6995-1f19-4cdd-9ed1-d6263302f594}"), 59 contractID: "@mozilla.org/installerprefs;1", 60 61 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), 62 63 observe(subject, topic, data) { 64 switch (topic) { 65 case "profile-after-change": { 66 if ( 67 AppConstants.platform != "win" || 68 !this.prefsList || 69 !this.prefsList.length 70 ) { 71 // This module has no work to do. 72 break; 73 } 74 const regKey = this._openRegKey(); 75 this._reflectPrefsToRegistry(regKey); 76 this._registerPrefListeners(); 77 regKey.close(); 78 break; 79 } 80 case "nsPref:changed": { 81 const regKey = this._openRegKey(); 82 if (this.prefsList.includes(data)) { 83 this._reflectOnePrefToRegistry(regKey, data); 84 } 85 regKey.close(); 86 break; 87 } 88 } 89 }, 90 91 _registerPrefListeners() { 92 Services.prefs.addObserver(INSTALLER_PREFS_BRANCH, this); 93 }, 94 95 _cleanRegistryKey(regKey) { 96 for (let i = regKey.valueCount - 1; i >= 0; --i) { 97 const name = regKey.getValueName(i); 98 if (name.startsWith(INSTALLER_PREFS_BRANCH)) { 99 regKey.removeValue(name); 100 } 101 } 102 }, 103 104 _reflectPrefsToRegistry(regKey) { 105 this._cleanRegistryKey(regKey); 106 this.prefsList.forEach(pref => 107 this._reflectOnePrefToRegistry(regKey, pref) 108 ); 109 }, 110 111 _reflectOnePrefToRegistry(regKey, pref) { 112 if (!pref.startsWith(INSTALLER_PREFS_BRANCH)) { 113 return; 114 } 115 116 const value = Services.prefs.getBoolPref(pref, false); 117 if (value) { 118 regKey.writeIntValue(pref, 1); 119 } else { 120 try { 121 regKey.removeValue(pref); 122 } catch (ex) { 123 // This removeValue call is prone to failing because the value we 124 // tried to remove didn't exist. Obviously that isn't really an error 125 // that we need to handle. 126 } 127 } 128 }, 129 130 _openRegKey() { 131 const key = Cc["@mozilla.org/windows-registry-key;1"].createInstance( 132 Ci.nsIWindowsRegKey 133 ); 134 key.create( 135 key.ROOT_KEY_CURRENT_USER, 136 this._registryKeyPath, 137 key.ACCESS_READ | key.ACCESS_WRITE | key.WOW64_64 138 ); 139 return key; 140 }, 141 };