ChannelMap.sys.mjs (4021B)
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 /** 6 * FinalizationRegistry callback, see 7 * https://searchfox.org/mozilla-central/source/js/src/builtin/FinalizationRegistryObject.h 8 * 9 * Will be invoked when the channel corresponding to the weak reference is 10 * "destroyed", at which point we can cleanup the corresponding entry in our 11 * regular map. 12 */ 13 function deleteIdFromRefMap({ refMap, id }) { 14 refMap.delete(id); 15 } 16 17 /** 18 * This object implements iterable weak map for HTTP channels tracked by 19 * the network observer. 20 * 21 * We can't use Map() for storing HTTP channel references since we don't 22 * know when we should remove the entry in it (it's wrong to do it in 23 * 'onTransactionClose' since it doesn't have to be the last platform 24 * notification for a given channel). We want the map to auto update 25 * when the channel is garbage collected. 26 * 27 * We can't use WeakMap() since searching for a value by the channel object 28 * isn't reliable (there might be different objects representing the same 29 * channel). We need to search by channel ID, but ID can't be used as key 30 * in WeakMap(). 31 * 32 * So, this custom map solves aforementioned issues. 33 */ 34 export class ChannelMap { 35 #finalizationRegistry; 36 #refMap; 37 #weakMap; 38 39 constructor() { 40 // See https://searchfox.org/mozilla-central/source/js/src/builtin/FinalizationRegistryObject.h 41 this.#finalizationRegistry = new FinalizationRegistry(deleteIdFromRefMap); 42 43 // Map of channel id to a channel weak reference. 44 this.#refMap = new Map(); 45 46 /** 47 * WeakMap from nsIChannel instances to objects which encapsulate ChannelMap 48 * values with the following structure: 49 * 50 * @property {object} value 51 * The actual value stored in this ChannelMap entry, which should relate 52 * to this channel. 53 * @property {WeakRef} ref 54 * Weak reference for the channel object which is the key of the entry. 55 */ 56 this.#weakMap = new WeakMap(); 57 } 58 59 /** 60 * Remove all entries from the ChannelMap. 61 */ 62 clear() { 63 this.#refMap.clear(); 64 } 65 66 /** 67 * Delete the entry for the provided channel from the underlying maps, if any. 68 * Note that this will only delete entries which were set for the exact same 69 * nsIChannel object, and will not attempt to look up entries by channel id. 70 * 71 * @param {nsIChannel} channel 72 * The key to delete from the ChannelMap. 73 * 74 * @return {boolean} 75 * True if an entry was deleted, false otherwise. 76 */ 77 delete(channel) { 78 const entry = this.#weakMap.get(channel); 79 if (!entry) { 80 return false; 81 } 82 83 this.#weakMap.delete(channel); 84 this.#refMap.delete(channel.channelId); 85 this.#finalizationRegistry.unregister(entry.ref); 86 return true; 87 } 88 89 /** 90 * Retrieve a value stored in the ChannelMap by the provided channel. 91 * 92 * @param {nsIChannel} channel 93 * The key to delete from the ChannelMap. 94 * 95 * @return {object | null} 96 * The value held for the provided channel. 97 * Null if the channel did not match any known key. 98 */ 99 get(channel) { 100 const ref = this.#refMap.get(channel.channelId); 101 const key = ref ? ref.deref() : null; 102 if (!key) { 103 return null; 104 } 105 const channelInfo = this.#weakMap.get(key); 106 return channelInfo ? channelInfo.value : null; 107 } 108 109 /** 110 * Adds or updates an entry in the ChannelMap for the provided channel. 111 * 112 * @param {nsIChannel} channel 113 * The key of the entry to add or update. 114 * @param {object} value 115 * The value to add or update. 116 */ 117 set(channel, value) { 118 const ref = new WeakRef(channel); 119 this.#weakMap.set(channel, { value, ref }); 120 this.#refMap.set(channel.channelId, ref); 121 this.#finalizationRegistry.register( 122 channel, 123 { 124 refMap: this.#refMap, 125 id: channel.channelId, 126 }, 127 ref 128 ); 129 } 130 }