Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / toolkit / modules / FirstStartup.sys.mjs
blobc09885abe90c38597942216741e79b8ea21fbb63
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 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
7 const lazy = {};
9 ChromeUtils.defineESModuleGetters(lazy, {
10   Normandy: "resource://normandy/Normandy.sys.mjs",
11   TaskScheduler: "resource://gre/modules/TaskScheduler.sys.mjs",
12 });
14 const PREF_TIMEOUT = "first-startup.timeout";
16 /**
17  * Service for blocking application startup, to be used on the first install. The intended
18  * use case is for `FirstStartup` to be invoked when the application is called by an installer,
19  * such as the Windows Stub Installer, to allow the application to do some first-install tasks
20  * such as performance tuning and downloading critical data.
21  *
22  * In this scenario, the installer does not exit until the first application window appears,
23  * which gives the user experience of the application starting up quickly on first install.
24  */
25 export var FirstStartup = {
26   NOT_STARTED: 0,
27   IN_PROGRESS: 1,
28   TIMED_OUT: 2,
29   SUCCESS: 3,
30   UNSUPPORTED: 4,
32   _state: 0, // NOT_STARTED,
33   /**
34    * Initialize and run first-startup services. This will always run synchronously
35    * and spin the event loop until either all required services have
36    * completed, or until a timeout is reached.
37    *
38    * In the latter case, services are expected to run post-UI instead as usual.
39    *
40    * @param {boolean} newProfile
41    *   True if a new profile was just created, false otherwise.
42    */
43   init(newProfile) {
44     if (!newProfile) {
45       // In this case, we actually don't want to do any FirstStartup work,
46       // since a pre-existing profile was detected (presumably, we entered here
47       // because a user re-installed via the stub installer when there existed
48       // previous user profiles on the file system). We do, however, want to
49       // measure how often this occurs.
50       Glean.firstStartup.statusCode.set(this.NOT_STARTED);
51       Glean.firstStartup.newProfile.set(false);
52       GleanPings.firstStartup.submit();
53       return;
54     }
56     Glean.firstStartup.newProfile.set(true);
58     this._state = this.IN_PROGRESS;
59     const timeout = Services.prefs.getIntPref(PREF_TIMEOUT, 30000); // default to 30 seconds
60     let startingTime = Cu.now();
61     let initialized = false;
63     let promises = [];
65     let normandyInitEndTime = null;
66     if (AppConstants.MOZ_NORMANDY) {
67       promises.push(
68         lazy.Normandy.init({ runAsync: false }).finally(() => {
69           normandyInitEndTime = Cu.now();
70         })
71       );
72     }
74     let deleteTasksEndTime = null;
75     if (AppConstants.MOZ_UPDATE_AGENT) {
76       // It's technically possible for a previous installation to leave an old
77       // OS-level scheduled task around.  Start fresh.
78       promises.push(
79         lazy.TaskScheduler.deleteAllTasks()
80           .catch(() => {})
81           .finally(() => {
82             deleteTasksEndTime = Cu.now();
83           })
84       );
85     }
87     if (promises.length) {
88       Promise.all(promises).then(() => (initialized = true));
90       this.elapsed = 0;
91       Services.tm.spinEventLoopUntil("FirstStartup.sys.mjs:init", () => {
92         this.elapsed = Math.round(Cu.now() - startingTime);
93         if (this.elapsed >= timeout) {
94           this._state = this.TIMED_OUT;
95           return true;
96         } else if (initialized) {
97           this._state = this.SUCCESS;
98           return true;
99         }
100         return false;
101       });
102     } else {
103       this._state = this.UNSUPPORTED;
104     }
106     if (AppConstants.MOZ_NORMANDY) {
107       Glean.firstStartup.normandyInitTime.set(
108         Math.ceil(normandyInitEndTime || Cu.now() - startingTime)
109       );
110     }
112     if (AppConstants.MOZ_UPDATE_AGENT) {
113       Glean.firstStartup.deleteTasksTime.set(
114         Math.ceil(deleteTasksEndTime || Cu.now() - startingTime)
115       );
116     }
118     Glean.firstStartup.statusCode.set(this._state);
119     Glean.firstStartup.elapsed.set(this.elapsed);
120     GleanPings.firstStartup.submit();
121   },
123   get state() {
124     return this._state;
125   },
127   /**
128    * For testing only. This puts us back into the initial NOT_STARTED state.
129    */
130   resetForTesting() {
131     this._state = this.NOT_STARTED;
132   },