22 * License, v. 2.0. If a copy of the MPL was not distributed with this
33 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44
5+ /**
6+ * @import { ExperimentManager } from "./lib/ExperimentManager.sys.mjs"
7+ * @import { RemoteSettingsExperimentLoader } from "./lib/RemoteSettingsExperimentLoader.sys.mjs"
8+ */
9+
510import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs" ;
611import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs" ;
712
@@ -74,26 +79,6 @@ const experimentBranchAccessor = {
7479
7580const NIMBUS_PROFILE_ID_PREF = "nimbus.profileId" ;
7681
77- let cachedProfileId = null ;
78-
79- /**
80- * Ensure the Nimbus profile ID exists.
81- *
82- * @returns {string } The profile ID.
83- */
84- function ensureNimbusProfileId ( ) {
85- if ( ! cachedProfileId ) {
86- if ( Services . prefs . prefHasUserValue ( NIMBUS_PROFILE_ID_PREF ) ) {
87- cachedProfileId = Services . prefs . getStringPref ( NIMBUS_PROFILE_ID_PREF ) ;
88- } else {
89- cachedProfileId = Services . uuid . generateUUID ( ) . toString ( ) . slice ( 1 , - 1 ) ;
90- Services . prefs . setStringPref ( NIMBUS_PROFILE_ID_PREF , cachedProfileId ) ;
91- }
92- }
93-
94- return cachedProfileId ;
95- }
96-
9782/**
9883 * Metadata about an enrollment.
9984 *
@@ -131,11 +116,96 @@ export const EnrollmentType = Object.freeze({
131116 ROLLOUT : "rollout" ,
132117} ) ;
133118
134- let initialized = false ;
135- let experimentManager = null ;
136- let experimentLoader = null ;
119+ export const ExperimentAPI = new ( class {
120+ /**
121+ * Whether or not the ExperimentAPI has been initialized.
122+ */
123+ #initialized = false ;
124+
125+ /**
126+ * The current ExperimentManager.
127+ *
128+ * @type {ExperimentManager | null }
129+ */
130+ #experimentManager = null ;
131+
132+ /**
133+ * The current RemoteSettingsExperimentLoader.
134+ *
135+ * @type {RemoteSettingsExperimentLoader | null }
136+ */
137+ #experimentLoader = null ;
138+
139+ /**
140+ * The unique ID of this Profile.
141+ *
142+ * @type {string | null }
143+ */
144+ #cachedProfileId = null ;
145+
146+ /**
147+ * Cached pref values.
148+ */
149+ #prefValues = {
150+ /**
151+ * Whether or not opt-out studies are enabled.
152+ *
153+ * @see {@link STUDIES_OPT_OUT_PREF }
154+ */
155+ studiesEnabled : false ,
156+
157+ /**
158+ * Whether or not telemetry is enabled.
159+ *
160+ * @see {@link UPLOAD_ENABLED_PREF }
161+ */
162+ telemetryEnabled : false ,
163+ } ;
164+
165+ constructor ( ) {
166+ // Ensure that the profile ID is cached in a pref.
167+ if ( Services . prefs . prefHasUserValue ( NIMBUS_PROFILE_ID_PREF ) ) {
168+ this . #cachedProfileId = Services . prefs . getStringPref (
169+ NIMBUS_PROFILE_ID_PREF
170+ ) ;
171+ } else {
172+ this . #cachedProfileId = Services . uuid
173+ . generateUUID ( )
174+ . toString ( )
175+ . slice ( 1 , - 1 ) ;
176+ Services . prefs . setStringPref (
177+ NIMBUS_PROFILE_ID_PREF ,
178+ this . #cachedProfileId
179+ ) ;
180+ }
181+
182+ const onStudiesEnabledChanged = this . #onStudiesEnabledChanged. bind ( this ) ;
183+
184+ XPCOMUtils . defineLazyPreferenceGetter (
185+ this . #prefValues,
186+ "studiesEnabled" ,
187+ STUDIES_OPT_OUT_PREF ,
188+ false ,
189+ onStudiesEnabledChanged
190+ ) ;
191+
192+ XPCOMUtils . defineLazyPreferenceGetter (
193+ this . #prefValues,
194+ "telemetryEnabled" ,
195+ UPLOAD_ENABLED_PREF ,
196+ false ,
197+ onStudiesEnabledChanged
198+ ) ;
199+
200+ this . _annotateCrashReport = this . _annotateCrashReport . bind ( this ) ;
201+ this . _removeCrashReportAnnotator =
202+ this . _removeCrashReportAnnotator . bind ( this ) ;
203+
204+ ChromeUtils . defineLazyGetter ( this , "_remoteSettingsClient" , function ( ) {
205+ return lazy . RemoteSettings ( lazy . COLLECTION_ID ) ;
206+ } ) ;
207+ }
137208
138- export const ExperimentAPI = {
139209 /**
140210 * The topic that is notified when either the studies enabled pref or the
141211 * telemetry enabled pref changes.
@@ -145,7 +215,7 @@ export const ExperimentAPI = {
145215 */
146216 get STUDIES_ENABLED_CHANGED ( ) {
147217 return "nimbus:studies-enabled-changed" ;
148- } ,
218+ }
149219
150220 /**
151221 * Initialize the ExperimentAPI.
@@ -166,13 +236,11 @@ export const ExperimentAPI = {
166236 * Whether or not the ExperimentAPI was initialized.
167237 */
168238 async init ( { extraContext, forceSync = false } = { } ) {
169- if ( initialized ) {
239+ if ( this . # initialized) {
170240 return false ;
171241 }
172242
173- ensureNimbusProfileId ( ) ;
174-
175- initialized = true ;
243+ this . #initialized = true ;
176244
177245 const studiesEnabled = this . studiesEnabled ;
178246
@@ -240,78 +308,75 @@ export const ExperimentAPI = {
240308 ) ;
241309 }
242310
243- Services . prefs . addObserver (
244- UPLOAD_ENABLED_PREF ,
245- this . _onStudiesEnabledChanged
246- ) ;
247- Services . prefs . addObserver (
248- STUDIES_OPT_OUT_PREF ,
249- this . _onStudiesEnabledChanged
250- ) ;
251-
252311 // If Nimbus was disabled between the start of this function and registering
253312 // the pref observers we have not handled it yet.
254313 if ( studiesEnabled !== this . studiesEnabled ) {
255- await this . _onStudiesEnabledChanged ( ) ;
314+ await this . #onStudiesEnabledChanged ( ) ;
256315 }
257316
258317 return true ;
259- } ,
318+ }
260319
261320 /**
262321 * Return the global ExperimentManager.
263322 *
264323 * The ExperimentManager will be lazily created upon first access to this
265324 * property.
325+ *
326+ * @type {ExperimentManager }
266327 */
267328 get manager ( ) {
268- if ( experimentManager === null ) {
269- experimentManager = new lazy . ExperimentManager ( ) ;
329+ if ( this . # experimentManager === null ) {
330+ this . # experimentManager = new lazy . ExperimentManager ( ) ;
270331 }
271332
272- return experimentManager ;
273- } ,
333+ return this . # experimentManager;
334+ }
274335
275336 /**
276337 * Return the global ExperimentManager.
277338 *
278339 * @deprecated Use ExperimentAPI.Manager instead of this property.
340+ *
341+ * @type {ExperimentManager }
279342 */
280343 get _manager ( ) {
281344 return this . manager ;
282- } ,
345+ }
283346
284347 /**
285348 * Return the global RemoteSettingsExperimentLoader.
349+ *
350+ * @type {RemoteSettingsExperimentLoader }
286351 */
287352 get _rsLoader ( ) {
288- if ( experimentLoader === null ) {
289- experimentLoader = new lazy . RemoteSettingsExperimentLoader ( this . manager ) ;
353+ if ( this . #experimentLoader === null ) {
354+ this . #experimentLoader = new lazy . RemoteSettingsExperimentLoader (
355+ this . manager
356+ ) ;
290357 }
291358
292- return experimentLoader ;
293- } ,
359+ return this . # experimentLoader;
360+ }
294361
295362 _resetForTests ( ) {
296- experimentLoader ?. disable ( ) ;
297- experimentLoader = null ;
363+ this . # experimentLoader?. disable ( ) ;
364+ this . # experimentLoader = null ;
298365
299- lazy . CleanupManager . removeCleanupHandler (
300- ExperimentAPI . _removeCrashReportAnnotator
301- ) ;
302- experimentManager ?. store . off ( "update" , this . _annotateCrashReport ) ;
303- experimentManager = null ;
366+ lazy . CleanupManager . removeCleanupHandler ( this . _removeCrashReportAnnotator ) ;
367+ this . #experimentManager?. store . off ( "update" , this . _annotateCrashReport ) ;
368+ this . #experimentManager = null ;
304369
305- initialized = false ;
306- } ,
370+ this . # initialized = false ;
371+ }
307372
308373 get studiesEnabled ( ) {
309374 return (
310- Services . prefs . getBoolPref ( UPLOAD_ENABLED_PREF , false ) &&
311- Services . prefs . getBoolPref ( STUDIES_OPT_OUT_PREF , false ) &&
375+ this . #prefValues . studiesEnabled &&
376+ this . #prefValues . telemetryEnabled &&
312377 Services . policies . isAllowed ( "Shield" )
313378 ) ;
314- } ,
379+ }
315380
316381 /**
317382 * Return the profile ID.
@@ -326,8 +391,8 @@ export const ExperimentAPI = {
326391 * @returns {string } The profile ID.
327392 */
328393 get profileId ( ) {
329- return ensureNimbusProfileId ( ) ;
330- } ,
394+ return this . #cachedProfileId ;
395+ }
331396
332397 /**
333398 * Wait for the ExperimentAPI to become ready.
@@ -342,7 +407,7 @@ export const ExperimentAPI = {
342407 */
343408 async ready ( ) {
344409 return this . manager . store . ready ( ) ;
345- } ,
410+ }
346411
347412 /**
348413 * Annotate the current crash report with current enrollments.
@@ -362,23 +427,27 @@ export const ExperimentAPI = {
362427 "NimbusEnrollments" ,
363428 activeEnrollments
364429 ) ;
365- } ,
430+ }
366431
367432 _removeCrashReportAnnotator ( ) {
368- if ( initialized ) {
369- experimentManager ?. store . off ( "update" , this . _annotateCrashReport ) ;
433+ if ( this . #initialized) {
434+ this . #experimentManager?. store . off ( "update" , this . _annotateCrashReport ) ;
435+ }
436+ }
437+
438+ async #onStudiesEnabledChanged( ) {
439+ if ( ! this . #initialized) {
440+ return ;
370441 }
371- } ,
372442
373- async _onStudiesEnabledChanged ( ) {
374443 if ( ! this . studiesEnabled ) {
375444 await this . manager . _handleStudiesOptOut ( ) ;
376445 }
377446
378447 await this . _rsLoader . onEnabledPrefChange ( ) ;
379448
380449 Services . obs . notifyObservers ( null , this . STUDIES_ENABLED_CHANGED ) ;
381- } ,
450+ }
382451
383452 /**
384453 * Returns the recipe for a given experiment slug
@@ -415,7 +484,7 @@ export const ExperimentAPI = {
415484 }
416485
417486 return recipe ;
418- } ,
487+ }
419488
420489 /**
421490 * Returns all the branches for a given experiment slug
@@ -437,7 +506,7 @@ export const ExperimentAPI = {
437506 return recipe ?. branches . map (
438507 branch => new Proxy ( branch , experimentBranchAccessor )
439508 ) ;
440- } ,
509+ }
441510
442511 /**
443512 * Opt-in to the given experiment on the given branch.
@@ -465,8 +534,8 @@ export const ExperimentAPI = {
465534 */
466535 async optInToExperiment ( options ) {
467536 return this . _rsLoader . _optInToExperiment ( options ) ;
468- } ,
469- } ;
537+ }
538+ } ) ( ) ;
470539
471540/**
472541 * Singleton that holds lazy references to _ExperimentFeature instances
@@ -933,21 +1002,6 @@ export class _ExperimentFeature {
9331002 }
9341003}
9351004
936- ExperimentAPI . _annotateCrashReport =
937- ExperimentAPI . _annotateCrashReport . bind ( ExperimentAPI ) ;
938- ExperimentAPI . _onStudiesEnabledChanged =
939- ExperimentAPI . _onStudiesEnabledChanged . bind ( ExperimentAPI ) ;
940- ExperimentAPI . _removeCrashReportAnnotator =
941- ExperimentAPI . _removeCrashReportAnnotator . bind ( ExperimentAPI ) ;
942-
943- ChromeUtils . defineLazyGetter (
944- ExperimentAPI ,
945- "_remoteSettingsClient" ,
946- function ( ) {
947- return lazy . RemoteSettings ( lazy . COLLECTION_ID ) ;
948- }
949- ) ;
950-
9511005class ExperimentLocalizationError extends Error {
9521006 constructor ( reason , locale ) {
9531007 super ( `Localized experiment error (${ reason } )` ) ;
0 commit comments