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/. */
6 * "Launch on Login" is a Firefox feature automatically launches Firefox when the
7 * user logs in to Windows. The technical mechanism is simply writing a registry
8 * key to `Software\Microsoft\Windows\CurrentVersion\Run`, but there is an issue:
9 * when disabled in the Windows UI, additional registry keys are written under
10 * `Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run`. Any
11 * keys stored here should be seen as a user override and should never be modified.
12 * When such keys are present, the launch on login feature should be considered
13 * disabled and not available from within Firefox. This module provides the
14 * functionality to access and modify these registry keys.
16 export var WindowsLaunchOnLogin = {
18 * Accepts another function as an argument and provides an open Windows
19 * launch on login registry key for the passed-in function to manipulate.
22 * The function to use.
24 async withLaunchOnLoginRegistryKey(func) {
25 let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
29 wrk.ROOT_KEY_CURRENT_USER,
30 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
41 * Safely creates a Windows launch on login registry key
43 async createLaunchOnLoginRegistryKey() {
45 await this.withLaunchOnLoginRegistryKey(async wrk => {
46 // Added launch option -os-autostart for telemetry
47 // Add quote marks around path to account for spaces in path
49 this.quoteString(Services.dirsvc.get("XREExeF", Ci.nsIFile).path) +
53 this.getLaunchOnLoginRegistryName(),
57 console.error("Could not write value to registry", e);
61 // We should only end up here if we fail to open the registry
62 console.error("Failed to open Windows registry", e);
67 * Safely removes a Windows launch on login registry key
69 async removeLaunchOnLoginRegistryKey() {
71 await this.withLaunchOnLoginRegistryKey(async wrk => {
72 let registryName = this.getLaunchOnLoginRegistryName();
73 if (wrk.hasValue(registryName)) {
75 wrk.removeValue(registryName);
77 console.error("Failed to remove Windows registry value", e);
82 // We should only end up here if we fail to open the registry
83 console.error("Failed to open Windows registry", e);
88 * Gets a list of all launch on login shortcuts in the
89 * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder
90 * that point to the current Firefox executable.
92 getLaunchOnLoginShortcutList() {
93 let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
94 Ci.nsIWindowsShellService
96 return shellService.getLaunchOnLoginShortcuts();
100 * Safely removes all launch on login shortcuts in the
101 * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup folder
102 * that point to the current Firefox executable.
104 async removeLaunchOnLoginShortcuts() {
105 let shortcuts = this.getLaunchOnLoginShortcutList();
106 for (let i = 0; i < shortcuts.length; i++) {
107 await IOUtils.remove(shortcuts[i]);
112 * Checks if Windows launch on login was independently enabled or disabled
113 * by the user in the Windows Startup Apps menu. The registry key that
114 * stores this information should not be modified.
116 getLaunchOnLoginApproved() {
118 let wrkApproved = Cc[
119 "@mozilla.org/windows-registry-key;1"
120 ].createInstance(Ci.nsIWindowsRegKey);
122 wrkApproved.ROOT_KEY_CURRENT_USER,
123 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run",
124 wrkApproved.ACCESS_READ
126 let registryName = this.getLaunchOnLoginRegistryName();
127 if (!wrkApproved.hasValue(registryName)) {
131 // There's very little consistency with these binary values aside from if the first byte
132 // is even it's enabled and odd is disabled. There's also no published specification.
133 let approvedByWindows =
134 wrkApproved.readBinaryValue(registryName).charCodeAt(0).toString(16) %
138 return approvedByWindows;
140 // We should only end up here if we fail to open the registry
141 console.error("Failed to open Windows registry", e);
147 * Checks if Windows launch on login has an existing registry key or user-created shortcut in
148 * %USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. The registry key that
149 * stores this information should not be modified.
151 getLaunchOnLoginEnabled() {
152 let registryName = this.getLaunchOnLoginRegistryName();
153 let regExists = false;
154 let shortcutExists = false;
155 this.withLaunchOnLoginRegistryKey(wrk => {
157 // Start by checking if registry key exists.
158 regExists = wrk.hasValue(registryName);
160 // We should only end up here if we fail to open the registry
161 console.error("Failed to open Windows registry", e);
165 shortcutExists = !!this.getLaunchOnLoginShortcutList().length;
167 // Even if a user disables it later on we want the launch on login
168 // infobar to remain disabled as the user is aware of the option.
169 if (regExists || shortcutExists) {
170 Services.prefs.setBoolPref(
171 "browser.startup.windowsLaunchOnLogin.disableLaunchOnLoginPrompt",
175 return regExists || shortcutExists;
179 * Quotes a string for use as a single command argument, using Windows quoting
182 * @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx
184 * @param {string} str
185 * The argument string to quote.
189 if (!/[\s"]/.test(str)) {
193 let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => {
197 return `${m1}${m1}${m2}`;
200 return `"${escaped}"`;
204 * Generates a unique registry name for the current application
205 * like "Mozilla-Firefox-71AE18FE3142402B".
207 getLaunchOnLoginRegistryName() {
208 let xreDirProvider = Cc[
209 "@mozilla.org/xre/directory-provider;1"
210 ].createInstance(Ci.nsIXREDirProvider);
211 let registryName = `${Services.appinfo.vendor}-${
212 Services.appinfo.name
213 }-${xreDirProvider.getInstallHash()}`;