NetworkCacheManager.sys.mjs (4811B)
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 BrowsingContextListener: 9 "chrome://remote/content/shared/listeners/BrowsingContextListener.sys.mjs", 10 TabManager: "chrome://remote/content/shared/TabManager.sys.mjs", 11 }); 12 13 /** 14 * Enum of possible network cache behaviors. 15 * 16 * @readonly 17 * @enum {CacheBehavior} 18 */ 19 export const CacheBehavior = { 20 Default: "default", 21 Bypass: "bypass", 22 }; 23 24 /** 25 * The NetworkCacheManager is responsible for managing the cache status (enabling/disabling cache) 26 * for navigables. It's meant to be a singleton, and the consumers can use the exported 27 * methods to change the cache status or perform the state cleanup. 28 * 29 * @class NetworkCacheManager 30 */ 31 class NetworkCacheManager { 32 #contextListener; 33 #defaultCacheBehavior; 34 #navigableCacheBehaviorMap; 35 36 constructor() { 37 this.#contextListener = new lazy.BrowsingContextListener(); 38 this.#contextListener.on("attached", this.#onContextAttached); 39 40 this.#defaultCacheBehavior = CacheBehavior.Default; 41 // WeakMap from navigables to cache behavior settings (CacheBehavior). 42 this.#navigableCacheBehaviorMap = new WeakMap(); 43 } 44 45 destroy() { 46 this.#contextListener.off("attached", this.#onContextAttached); 47 this.#contextListener.destroy(); 48 49 this.cleanup(); 50 } 51 52 #getLoadFlags(behavior) { 53 return behavior === CacheBehavior.Bypass 54 ? Ci.nsIRequest.LOAD_BYPASS_CACHE 55 : Ci.nsIRequest.LOAD_NORMAL; 56 } 57 58 #getWeakMapSize(weakMap) { 59 return ChromeUtils.nondeterministicGetWeakMapKeys(weakMap).length; 60 } 61 62 #onContextAttached = (eventName, data = {}) => { 63 if (this.#defaultCacheBehavior === CacheBehavior.Bypass) { 64 this.#setLoadFlagsForBrowsingContext( 65 data.browsingContext, 66 this.#getLoadFlags(CacheBehavior.Bypass) 67 ); 68 } 69 }; 70 71 #setDefaultCacheBehavior(behavior) { 72 this.#defaultCacheBehavior = behavior; 73 this.#navigableCacheBehaviorMap = new WeakMap(); 74 75 const loadFlags = this.#getLoadFlags(behavior); 76 77 // Update cache settings for all existing navigables. 78 for (const browser of lazy.TabManager.getBrowsers()) { 79 this.#setLoadFlagsForBrowsingContext(browser.browsingContext, loadFlags); 80 } 81 82 // In case the cache is globally disabled we have to listen to all 83 // newly attached contexts and update the cache behavior for them. 84 if (this.#defaultCacheBehavior === CacheBehavior.Bypass) { 85 this.#contextListener.startListening(); 86 } else { 87 this.#contextListener.stopListening(); 88 } 89 } 90 91 #setLoadFlagsForBrowsingContext(browsingContext, loadFlags) { 92 if (browsingContext.defaultLoadFlags !== loadFlags) { 93 browsingContext.defaultLoadFlags = loadFlags; 94 } 95 } 96 97 /** 98 * Reset network cache behavior to the default. 99 */ 100 cleanup() { 101 this.#setDefaultCacheBehavior(CacheBehavior.Default); 102 103 if (this.#getWeakMapSize(this.#navigableCacheBehaviorMap) === 0) { 104 return; 105 } 106 107 const loadFlags = this.#getLoadFlags(CacheBehavior.Default); 108 109 for (const browser of lazy.TabManager.getBrowsers()) { 110 if (this.#navigableCacheBehaviorMap.has(browser.browsingContext)) { 111 this.#setLoadFlagsForBrowsingContext( 112 browser.browsingContext, 113 loadFlags 114 ); 115 } 116 } 117 118 this.#navigableCacheBehaviorMap = new WeakMap(); 119 } 120 121 /** 122 * Update network cache behavior to a provided value 123 * and optionally specified contexts. 124 * 125 * @param {CacheBehavior} behavior 126 * An enum value to set the network cache behavior. 127 * @param {Array<BrowsingContext>=} contexts 128 * The list of browsing contexts where the network cache 129 * behaviour should be updated. 130 */ 131 updateCacheBehavior(behavior, contexts = null) { 132 if (contexts === null) { 133 this.#setDefaultCacheBehavior(behavior); 134 return; 135 } 136 137 const loadFlags = this.#getLoadFlags(behavior); 138 139 for (const context of contexts) { 140 if (this.#navigableCacheBehaviorMap.get(context) === behavior) { 141 continue; 142 } 143 144 this.#setLoadFlagsForBrowsingContext(context, loadFlags); 145 146 if (behavior === CacheBehavior.Default) { 147 this.#navigableCacheBehaviorMap.delete(context); 148 } else { 149 this.#navigableCacheBehaviorMap.set(context, behavior); 150 } 151 } 152 } 153 } 154 155 // Create a private NetworkCacheManager singleton. 156 const networkCacheManager = new NetworkCacheManager(); 157 158 export function updateCacheBehavior(behavior, contexts) { 159 return networkCacheManager.updateCacheBehavior(behavior, contexts); 160 } 161 162 export function cleanupCacheBypassState() { 163 return networkCacheManager.cleanup(); 164 }