summaryrefslogtreecommitdiffstats
path: root/browser/components/installerprefs/InstallerPrefs.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/installerprefs/InstallerPrefs.sys.mjs')
-rw-r--r--browser/components/installerprefs/InstallerPrefs.sys.mjs142
1 files changed, 142 insertions, 0 deletions
diff --git a/browser/components/installerprefs/InstallerPrefs.sys.mjs b/browser/components/installerprefs/InstallerPrefs.sys.mjs
new file mode 100644
index 0000000000..a477d59e9e
--- /dev/null
+++ b/browser/components/installerprefs/InstallerPrefs.sys.mjs
@@ -0,0 +1,142 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+/**
+ * The installer prefs component provides a way to get a specific set of prefs
+ * from a profile into a place where the installer can read them. The primary
+ * reason for wanting to do this is so we can run things like Shield studies
+ * on installer features; normally those are enabled by setting a pref, but
+ * the installer runs outside of any profile and so has no access to prefs.
+ * So we need to do something else to allow it to read these prefs.
+ *
+ * The mechanism we use here is to reflect the values of a list of relevant
+ * prefs into registry values. One registry value is created for each pref
+ * that is set. Each installation of the product gets its own registry key
+ * (based on the path hash). This is obviously a somewhat wider scope than a
+ * single profile, but it should be close enough in enough cases to suit our
+ * purposes here.
+ *
+ * Currently this module only supports bool prefs. Other types could likely
+ * be added if needed, but it doesn't seem necessary for the primary use case.
+ */
+
+// All prefs processed through this component must be in this branch.
+const INSTALLER_PREFS_BRANCH = "installer.";
+
+// This is the list of prefs that will be reflected to the registry. It should
+// be kept up to date so that it reflects the list of prefs that are in
+// current use (e.g., currently active experiments).
+// Only add prefs to this list which are in INSTALLER_PREFS_BRANCH;
+// any others will be ignored.
+const INSTALLER_PREFS_LIST = ["installer.taskbarpin.win10.enabled"];
+
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+// This constructor can take a list of prefs to override the default one,
+// but this is really only intended for tests to use. Normal usage should be
+// to leave the parameter omitted/undefined.
+export function InstallerPrefs(prefsList) {
+ this.prefsList = prefsList || INSTALLER_PREFS_LIST;
+
+ // Each pref to be reflected will get a value created under this key, in HKCU.
+ // The path will look something like:
+ // "Software\Mozilla\Firefox\Installer\71AE18FE3142402B\".
+ XPCOMUtils.defineLazyGetter(this, "_registryKeyPath", function () {
+ const app = AppConstants.MOZ_APP_NAME;
+ const vendor = Services.appinfo.vendor || "Mozilla";
+ const xreDirProvider = Cc[
+ "@mozilla.org/xre/directory-provider;1"
+ ].getService(Ci.nsIXREDirProvider);
+ const installHash = xreDirProvider.getInstallHash();
+ return `Software\\${vendor}\\${app}\\Installer\\${installHash}`;
+ });
+}
+
+InstallerPrefs.prototype = {
+ classID: Components.ID("{cd8a6995-1f19-4cdd-9ed1-d6263302f594}"),
+ contractID: "@mozilla.org/installerprefs;1",
+
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+
+ observe(subject, topic, data) {
+ switch (topic) {
+ case "profile-after-change": {
+ if (
+ AppConstants.platform != "win" ||
+ !this.prefsList ||
+ !this.prefsList.length
+ ) {
+ // This module has no work to do.
+ break;
+ }
+ const regKey = this._openRegKey();
+ this._reflectPrefsToRegistry(regKey);
+ this._registerPrefListeners();
+ regKey.close();
+ break;
+ }
+ case "nsPref:changed": {
+ const regKey = this._openRegKey();
+ if (this.prefsList.includes(data)) {
+ this._reflectOnePrefToRegistry(regKey, data);
+ }
+ regKey.close();
+ break;
+ }
+ }
+ },
+
+ _registerPrefListeners() {
+ Services.prefs.addObserver(INSTALLER_PREFS_BRANCH, this);
+ },
+
+ _cleanRegistryKey(regKey) {
+ for (let i = regKey.valueCount - 1; i >= 0; --i) {
+ const name = regKey.getValueName(i);
+ if (name.startsWith(INSTALLER_PREFS_BRANCH)) {
+ regKey.removeValue(name);
+ }
+ }
+ },
+
+ _reflectPrefsToRegistry(regKey) {
+ this._cleanRegistryKey(regKey);
+ this.prefsList.forEach(pref =>
+ this._reflectOnePrefToRegistry(regKey, pref)
+ );
+ },
+
+ _reflectOnePrefToRegistry(regKey, pref) {
+ if (!pref.startsWith(INSTALLER_PREFS_BRANCH)) {
+ return;
+ }
+
+ const value = Services.prefs.getBoolPref(pref, false);
+ if (value) {
+ regKey.writeIntValue(pref, 1);
+ } else {
+ try {
+ regKey.removeValue(pref);
+ } catch (ex) {
+ // This removeValue call is prone to failing because the value we
+ // tried to remove didn't exist. Obviously that isn't really an error
+ // that we need to handle.
+ }
+ }
+ },
+
+ _openRegKey() {
+ const key = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
+ Ci.nsIWindowsRegKey
+ );
+ key.create(
+ key.ROOT_KEY_CURRENT_USER,
+ this._registryKeyPath,
+ key.ACCESS_READ | key.ACCESS_WRITE | key.WOW64_64
+ );
+ return key;
+ },
+};