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";
9 ChromeUtils.defineESModuleGetters(lazy, {
10 Normandy: "resource://normandy/Normandy.sys.mjs",
11 TaskScheduler: "resource://gre/modules/TaskScheduler.sys.mjs",
14 const PREF_TIMEOUT = "first-startup.timeout";
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.
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.
25 export var FirstStartup = {
32 _state: 0, // NOT_STARTED,
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.
38 * In the latter case, services are expected to run post-UI instead as usual.
40 * @param {boolean} newProfile
41 * True if a new profile was just created, false otherwise.
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();
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;
65 let normandyInitEndTime = null;
66 if (AppConstants.MOZ_NORMANDY) {
68 lazy.Normandy.init({ runAsync: false }).finally(() => {
69 normandyInitEndTime = Cu.now();
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.
79 lazy.TaskScheduler.deleteAllTasks()
82 deleteTasksEndTime = Cu.now();
87 if (promises.length) {
88 Promise.all(promises).then(() => (initialized = true));
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;
96 } else if (initialized) {
97 this._state = this.SUCCESS;
103 this._state = this.UNSUPPORTED;
106 if (AppConstants.MOZ_NORMANDY) {
107 Glean.firstStartup.normandyInitTime.set(
108 Math.ceil(normandyInitEndTime || Cu.now() - startingTime)
112 if (AppConstants.MOZ_UPDATE_AGENT) {
113 Glean.firstStartup.deleteTasksTime.set(
114 Math.ceil(deleteTasksEndTime || Cu.now() - startingTime)
118 Glean.firstStartup.statusCode.set(this._state);
119 Glean.firstStartup.elapsed.set(this.elapsed);
120 GleanPings.firstStartup.submit();
128 * For testing only. This puts us back into the initial NOT_STARTED state.
131 this._state = this.NOT_STARTED;