no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / toolkit / modules / WindowsLaunchOnLogin.sys.mjs
blob962c80e400c3d5cca4e45214604b269d076cbc68
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 /**
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.
15  */
16 export var WindowsLaunchOnLogin = {
17   /**
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.
20    *
21    * @param func
22    *        The function to use.
23    */
24   async withLaunchOnLoginRegistryKey(func) {
25     let wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
26       Ci.nsIWindowsRegKey
27     );
28     wrk.open(
29       wrk.ROOT_KEY_CURRENT_USER,
30       "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
31       wrk.ACCESS_ALL
32     );
33     try {
34       await func(wrk);
35     } finally {
36       wrk.close();
37     }
38   },
40   /**
41    * Safely creates a Windows launch on login registry key
42    */
43   async createLaunchOnLoginRegistryKey() {
44     try {
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
48         let autostartPath =
49           this.quoteString(Services.dirsvc.get("XREExeF", Ci.nsIFile).path) +
50           " -os-autostart";
51         try {
52           wrk.writeStringValue(
53             this.getLaunchOnLoginRegistryName(),
54             autostartPath
55           );
56         } catch (e) {
57           console.error("Could not write value to registry", e);
58         }
59       });
60     } catch (e) {
61       // We should only end up here if we fail to open the registry
62       console.error("Failed to open Windows registry", e);
63     }
64   },
66   /**
67    * Safely removes a Windows launch on login registry key
68    */
69   async removeLaunchOnLoginRegistryKey() {
70     try {
71       await this.withLaunchOnLoginRegistryKey(async wrk => {
72         let registryName = this.getLaunchOnLoginRegistryName();
73         if (wrk.hasValue(registryName)) {
74           try {
75             wrk.removeValue(registryName);
76           } catch (e) {
77             console.error("Failed to remove Windows registry value", e);
78           }
79         }
80       });
81     } catch (e) {
82       // We should only end up here if we fail to open the registry
83       console.error("Failed to open Windows registry", e);
84     }
85   },
87   /**
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.
91    */
92   getLaunchOnLoginShortcutList() {
93     let shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
94       Ci.nsIWindowsShellService
95     );
96     return shellService.getLaunchOnLoginShortcuts();
97   },
99   /**
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.
103    */
104   async removeLaunchOnLoginShortcuts() {
105     let shortcuts = this.getLaunchOnLoginShortcutList();
106     for (let i = 0; i < shortcuts.length; i++) {
107       await IOUtils.remove(shortcuts[i]);
108     }
109   },
111   /**
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.
115    */
116   getLaunchOnLoginApproved() {
117     try {
118       let wrkApproved = Cc[
119         "@mozilla.org/windows-registry-key;1"
120       ].createInstance(Ci.nsIWindowsRegKey);
121       wrkApproved.open(
122         wrkApproved.ROOT_KEY_CURRENT_USER,
123         "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartupApproved\\Run",
124         wrkApproved.ACCESS_READ
125       );
126       let registryName = this.getLaunchOnLoginRegistryName();
127       if (!wrkApproved.hasValue(registryName)) {
128         wrkApproved.close();
129         return true;
130       }
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) %
135           2 ==
136         0;
137       wrkApproved.close();
138       return approvedByWindows;
139     } catch (e) {
140       // We should only end up here if we fail to open the registry
141       console.error("Failed to open Windows registry", e);
142     }
143     return true;
144   },
146   /**
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.
150    */
151   getLaunchOnLoginEnabled() {
152     let registryName = this.getLaunchOnLoginRegistryName();
153     let regExists = false;
154     let shortcutExists = false;
155     this.withLaunchOnLoginRegistryKey(wrk => {
156       try {
157         // Start by checking if registry key exists.
158         regExists = wrk.hasValue(registryName);
159       } catch (e) {
160         // We should only end up here if we fail to open the registry
161         console.error("Failed to open Windows registry", e);
162       }
163     });
164     if (!regExists) {
165       shortcutExists = !!this.getLaunchOnLoginShortcutList().length;
166     }
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",
172         true
173       );
174     }
175     return regExists || shortcutExists;
176   },
178   /**
179    * Quotes a string for use as a single command argument, using Windows quoting
180    * conventions.
181    *
182    * @see https://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx
183    *
184    * @param {string} str
185    *        The argument string to quote.
186    * @returns {string}
187    */
188   quoteString(str) {
189     if (!/[\s"]/.test(str)) {
190       return str;
191     }
193     let escaped = str.replace(/(\\*)("|$)/g, (m0, m1, m2) => {
194       if (m2) {
195         m2 = `\\${m2}`;
196       }
197       return `${m1}${m1}${m2}`;
198     });
200     return `"${escaped}"`;
201   },
203   /**
204    * Generates a unique registry name for the current application
205    * like "Mozilla-Firefox-71AE18FE3142402B".
206    */
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()}`;
214     return registryName;
215   },