IPPStartupCache.sys.mjs (3725B)
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/. */ 4 5 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 IPProtectionService: 9 "moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs", 10 IPProtectionStates: 11 "moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs", 12 }); 13 14 const STATE_CACHE_PREF = "browser.ipProtection.stateCache"; 15 const ENTITLEMENT_CACHE_PREF = "browser.ipProtection.entitlementCache"; 16 const LOCATIONLIST_CACHE_PREF = "browser.ipProtection.locationListCache"; 17 18 /** 19 * This class implements a cache for the IPP state machine. The cache is used 20 * until we receive the `sessionstore-windows-restored` event 21 */ 22 class IPPStartupCacheSingleton { 23 #stateFromCache = null; 24 #startupCompleted = false; 25 26 constructor() { 27 // For XPCShell tests, the cache must be disabled. 28 if ( 29 Services.prefs.getBoolPref("browser.ipProtection.cacheDisabled", false) 30 ) { 31 this.#startupCompleted = true; 32 return; 33 } 34 35 this.handleEvent = this.#handleEvent.bind(this); 36 37 const stateFromCache = Services.prefs.getCharPref( 38 STATE_CACHE_PREF, 39 "unset" 40 ); 41 if (stateFromCache !== "unset") { 42 this.#stateFromCache = stateFromCache; 43 } 44 45 Services.obs.addObserver(this, "sessionstore-windows-restored"); 46 } 47 48 init() { 49 lazy.IPProtectionService.addEventListener( 50 "IPProtectionService:StateChanged", 51 this.handleEvent 52 ); 53 } 54 55 async initOnStartupCompleted() {} 56 57 uninit() { 58 lazy.IPProtectionService.removeEventListener( 59 "IPProtectionService:StateChanged", 60 this.handleEvent 61 ); 62 } 63 64 get isStartupCompleted() { 65 return this.#startupCompleted; 66 } 67 68 get state() { 69 if (this.#startupCompleted) { 70 throw new Error("IPPStartupCache should not be used after the startup"); 71 } 72 73 if (Object.values(lazy.IPProtectionStates).includes(this.#stateFromCache)) { 74 return this.#stateFromCache; 75 } 76 77 // This should not happen. 78 return lazy.IPProtectionStates.UNINITIALIZED; 79 } 80 81 async observe(_subject, topic, _) { 82 if (topic !== "sessionstore-windows-restored") { 83 return; 84 } 85 86 // The browser is ready! Let's invalidate the cache and let's recompute the 87 // state. 88 89 Services.obs.removeObserver(this, "sessionstore-windows-restored"); 90 this.#startupCompleted = true; 91 this.#stateFromCache = null; 92 93 await lazy.IPProtectionService.initOnStartupCompleted(); 94 lazy.IPProtectionService.updateState(); 95 } 96 97 storeEntitlement(entitlement) { 98 Services.prefs.setCharPref( 99 ENTITLEMENT_CACHE_PREF, 100 JSON.stringify(entitlement) 101 ); 102 } 103 104 get entitlement() { 105 try { 106 const entitlement = Services.prefs.getCharPref( 107 ENTITLEMENT_CACHE_PREF, 108 "" 109 ); 110 return JSON.parse(entitlement); 111 } catch (e) { 112 return null; 113 } 114 } 115 116 storeLocationList(locationList) { 117 Services.prefs.setCharPref( 118 LOCATIONLIST_CACHE_PREF, 119 JSON.stringify(locationList) 120 ); 121 } 122 123 get locationList() { 124 try { 125 const locationList = Services.prefs.getCharPref( 126 LOCATIONLIST_CACHE_PREF, 127 "" 128 ); 129 return JSON.parse(locationList); 130 } catch (e) { 131 return null; 132 } 133 } 134 135 #handleEvent(_event) { 136 const state = lazy.IPProtectionService.state; 137 if (this.#startupCompleted) { 138 Services.prefs.setCharPref(STATE_CACHE_PREF, state); 139 } else { 140 this.#stateFromCache = state; 141 } 142 } 143 } 144 145 const IPPStartupCache = new IPPStartupCacheSingleton(); 146 147 export { IPPStartupCache, IPPStartupCacheSingleton };