summaryrefslogtreecommitdiffstats
path: root/devtools/shared/network-observer/ChannelMap.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/network-observer/ChannelMap.sys.mjs')
-rw-r--r--devtools/shared/network-observer/ChannelMap.sys.mjs129
1 files changed, 129 insertions, 0 deletions
diff --git a/devtools/shared/network-observer/ChannelMap.sys.mjs b/devtools/shared/network-observer/ChannelMap.sys.mjs
new file mode 100644
index 0000000000..3c54b1171a
--- /dev/null
+++ b/devtools/shared/network-observer/ChannelMap.sys.mjs
@@ -0,0 +1,129 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * FinalizationRegistry callback, see
+ * https://searchfox.org/mozilla-central/source/js/src/builtin/FinalizationRegistryObject.h
+ *
+ * Will be invoked when the channel corresponding to the weak reference is
+ * "destroyed", at which point we can cleanup the corresponding entry in our
+ * regular map.
+ */
+function deleteIdFromRefMap({ refMap, id }) {
+ refMap.delete(id);
+}
+
+/**
+ * This object implements iterable weak map for HTTP channels tracked by
+ * the network observer.
+ *
+ * We can't use Map() for storing HTTP channel references since we don't
+ * know when we should remove the entry in it (it's wrong to do it in
+ * 'onTransactionClose' since it doesn't have to be the last platform
+ * notification for a given channel). We want the map to auto update
+ * when the channel is garbage collected.
+ *
+ * We can't use WeakMap() since searching for a value by the channel object
+ * isn't reliable (there might be different objects representing the same
+ * channel). We need to search by channel ID, but ID can't be used as key
+ * in WeakMap().
+ *
+ * So, this custom map solves aforementioned issues.
+ */
+export class ChannelMap {
+ #finalizationRegistry;
+ #refMap;
+ #weakMap;
+
+ constructor() {
+ // See https://searchfox.org/mozilla-central/source/js/src/builtin/FinalizationRegistryObject.h
+ this.#finalizationRegistry = new FinalizationRegistry(deleteIdFromRefMap);
+
+ // Map of channel id to a channel weak reference.
+ this.#refMap = new Map();
+
+ /**
+ * WeakMap from nsIChannel instances to objects which encapsulate ChannelMap
+ * values with the following structure:
+ * @property {Object} value
+ * The actual value stored in this ChannelMap entry, which should relate
+ * to this channel.
+ * @property {WeakRef} ref
+ * Weak reference for the channel object which is the key of the entry.
+ */
+ this.#weakMap = new WeakMap();
+ }
+
+ /**
+ * Remove all entries from the ChannelMap.
+ */
+ clear() {
+ this.#refMap.clear();
+ }
+
+ /**
+ * Delete the entry for the provided channel from the underlying maps, if any.
+ * Note that this will only delete entries which were set for the exact same
+ * nsIChannel object, and will not attempt to look up entries by channel id.
+ *
+ * @param {nsIChannel} channel
+ * The key to delete from the ChannelMap.
+ *
+ * @return {boolean}
+ * True if an entry was deleted, false otherwise.
+ */
+ delete(channel) {
+ const entry = this.#weakMap.get(channel);
+ if (!entry) {
+ return false;
+ }
+
+ this.#weakMap.delete(channel);
+ this.#refMap.delete(channel.channelId);
+ this.#finalizationRegistry.unregister(entry.ref);
+ return true;
+ }
+
+ /**
+ * Retrieve a value stored in the ChannelMap by the provided channel.
+ *
+ * @param {nsIChannel} channel
+ * The key to delete from the ChannelMap.
+ *
+ * @return {Object|null}
+ * The value held for the provided channel.
+ * Null if the channel did not match any known key.
+ */
+ get(channel) {
+ const ref = this.#refMap.get(channel.channelId);
+ const key = ref ? ref.deref() : null;
+ if (!key) {
+ return null;
+ }
+ const channelInfo = this.#weakMap.get(key);
+ return channelInfo ? channelInfo.value : null;
+ }
+
+ /**
+ * Adds or updates an entry in the ChannelMap for the provided channel.
+ *
+ * @param {nsIChannel} channel
+ * The key of the entry to add or update.
+ * @param {Object} value
+ * The value to add or update.
+ */
+ set(channel, value) {
+ const ref = new WeakRef(channel);
+ this.#weakMap.set(channel, { value, ref });
+ this.#refMap.set(channel.channelId, ref);
+ this.#finalizationRegistry.register(
+ channel,
+ {
+ refMap: this.#refMap,
+ id: channel.channelId,
+ },
+ ref
+ );
+ }
+}