tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }