summaryrefslogtreecommitdiffstats
path: root/toolkit/modules/WindowsLaunchOnLogin.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/modules/WindowsLaunchOnLogin.sys.mjs')
-rw-r--r--toolkit/modules/WindowsLaunchOnLogin.sys.mjs216
1 files changed, 216 insertions, 0 deletions
diff --git a/toolkit/modules/WindowsLaunchOnLogin.sys.mjs b/toolkit/modules/WindowsLaunchOnLogin.sys.mjs
new file mode 100644
index 0000000000..962c80e400
--- /dev/null
+++ b/toolkit/modules/WindowsLaunchOnLogin.sys.mjs
@@ -0,0 +1,216 @@
+/* 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/. */
+
+/**
+ * "Launch on Login" is a Firefox feature automatically launches Firefox when the
+ * user logs in to Windows. The technical mechanism is simply writing a registry
+ * key to `Software\Microsoft\Windows\CurrentVersion\Run`, but there is an issue:
+ * when disabled in the Windows UI, additional registry keys are written under
+ * `Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run`. Any
+ * keys stored here should be seen as a user override and should never be modified.
+ * When such keys are present, the launch on login feature should be considered
+ * disabled and not available from within Firefox. This module provides the
+ * functionality to access and modify these registry keys.
+ */
+export var WindowsLaunchOnLogin = {
+ /**
+ * Accepts another function as an argument and provides an open Windows
+ * launch on login registry key for the passed-in function to manipulate.
+ *
+ * @param func
+ * The function to use.
+ */
+ async withLaunchOnLoginRegistryKey(func) {
+ let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
+ Ci.nsIWindowsRegKey
+ );
+ wrk.open(
+ wrk.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
+ wrk.ACCESS_ALL
+ );
+ try {
+ await func(wrk);
+ } finally {
+ wrk.close();
+ }
+ },
+
+ /**
+ * Safely creates a Windows launch on login registry key
+ */
+ async createLaunchOnLoginRegistryKey() {
+ try {
+ await this.withLaunchOnLoginRegistryKey(async wrk => {
+ // Added launch option -os-autostart for telemetry
+ // Add quote marks around path to account for spaces in path
+ let autostartPath =
+ this.quoteString(Services.dirsvc.get("XREExeF", Ci.nsIFile).path) +
+ " -os-autostart";
+ try {
+ wrk.writeStringValue(
+ this.getLaunchOnLoginRegistryName(),
+ autostartPath
+ );
+ } catch (e) {
+ console.error("Could not write value to registry", e);
+ }
+ });
+ } catch (e) {
+ // We should only end up here if we fail to open the registry
+ console.error("Failed to open Windows registry", e);
+ }
+ },
+
+ /**
+ * Safely removes a Windows launch on login registry key
+ */
+ async removeLaunchOnLoginRegistryKey() {
+ try {
+ await this.withLaunchOnLoginRegistryKey(async wrk => {
+ let registryName = this.getLaunchOnLoginRegistryName();
+ if (wrk.hasValue(registryName)) {
+ try {
+ wrk.removeValue(registryName);
+ } catch (e) {
+ console.error("Failed to remove Windows registry value", e);
+ }
+ }
+ });
+ } catch (e) {
+ // We should only end up here if we fail to open the registry
+ console.error("Failed to open Windows registry", e);
+ }
+ },
+
+ /**
+ * Gets a list of all launch on login shortcuts in the
+ * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder
+ * that point to the current Firefox executable.
+ */
+ getLaunchOnLoginShortcutList() {
+ let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
+ Ci.nsIWindowsShellService
+ );
+ return shellService.getLaunchOnLoginShortcuts();
+ },
+
+ /**
+ * Safely removes all launch on login shortcuts in the
+ * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder
+ * that point to the current Firefox executable.
+ */
+ async removeLaunchOnLoginShortcuts() {
+ let shortcuts = this.getLaunchOnLoginShortcutList();
+ for (let i = 0; i < shortcuts.length; i++) {
+ await IOUtils.remove(shortcuts[i]);
+ }
+ },
+
+ /**
+ * Checks if Windows launch on login was independently enabled or disabled
+ * by the user in the Windows Startup Apps menu. The registry key that
+ * stores this information should not be modified.
+ */
+ getLaunchOnLoginApproved() {
+ try {
+ let wrkApproved = Cc[
+ "@mozilla.org/windows-registry-key;1"
+ ].createInstance(Ci.nsIWindowsRegKey);
+ wrkApproved.open(
+ wrkApproved.ROOT_KEY_CURRENT_USER,
+ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run",
+ wrkApproved.ACCESS_READ
+ );
+ let registryName = this.getLaunchOnLoginRegistryName();
+ if (!wrkApproved.hasValue(registryName)) {
+ wrkApproved.close();
+ return true;
+ }
+ // There's very little consistency with these binary values aside from if the first byte
+ // is even it's enabled and odd is disabled. There's also no published specification.
+ let approvedByWindows =
+ wrkApproved.readBinaryValue(registryName).charCodeAt(0).toString(16) %
+ 2 ==
+ 0;
+ wrkApproved.close();
+ return approvedByWindows;
+ } catch (e) {
+ // We should only end up here if we fail to open the registry
+ console.error("Failed to open Windows registry", e);
+ }
+ return true;
+ },
+
+ /**
+ * Checks if Windows launch on login has an existing registry key or user-created shortcut in
+ * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. The registry key that
+ * stores this information should not be modified.
+ */
+ getLaunchOnLoginEnabled() {
+ let registryName = this.getLaunchOnLoginRegistryName();
+ let regExists = false;
+ let shortcutExists = false;
+ this.withLaunchOnLoginRegistryKey(wrk => {
+ try {
+ // Start by checking if registry key exists.
+ regExists = wrk.hasValue(registryName);
+ } catch (e) {
+ // We should only end up here if we fail to open the registry
+ console.error("Failed to open Windows registry", e);
+ }
+ });
+ if (!regExists) {
+ shortcutExists = !!this.getLaunchOnLoginShortcutList().length;
+ }
+ // Even if a user disables it later on we want the launch on login
+ // infobar to remain disabled as the user is aware of the option.
+ if (regExists || shortcutExists) {
+ Services.prefs.setBoolPref(
+ "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt",
+ true
+ );
+ }
+ return regExists || shortcutExists;
+ },
+
+ /**
+ * Quotes a string for use as a single command argument, using Windows quoting
+ * conventions.
+ *
+ * @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx
+ *
+ * @param {string} str
+ * The argument string to quote.
+ * @returns {string}
+ */
+ quoteString(str) {
+ if (!/[\s"]/.test(str)) {
+ return str;
+ }
+
+ let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => {
+ if (m2) {
+ m2 = `\\${m2}`;
+ }
+ return `${m1}${m1}${m2}`;
+ });
+
+ return `"${escaped}"`;
+ },
+
+ /**
+ * Generates a unique registry name for the current application
+ * like "Mozilla-Firefox-71AE18FE3142402B".
+ */
+ getLaunchOnLoginRegistryName() {
+ let xreDirProvider = Cc[
+ "@mozilla.org/xre/directory-provider;1"
+ ].createInstance(Ci.nsIXREDirProvider);
+ let registryName = `${Services.appinfo.vendor}-${
+ Services.appinfo.name
+ }-${xreDirProvider.getInstallHash()}`;
+ return registryName;
+ },
+};