tor-browser

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

MessageHandlerRegistry.sys.mjs (7469B)


      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 import { EventEmitter } from "resource://gre/modules/EventEmitter.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  Log: "chrome://remote/content/shared/Log.sys.mjs",
     11  readSessionData:
     12    "chrome://remote/content/shared/messagehandler/sessiondata/SessionDataReader.sys.mjs",
     13  RootMessageHandler:
     14    "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs",
     15  WindowGlobalMessageHandler:
     16    "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs",
     17 });
     18 
     19 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
     20 
     21 /**
     22 * Map of MessageHandler type to MessageHandler subclass.
     23 */
     24 ChromeUtils.defineLazyGetter(
     25  lazy,
     26  "MessageHandlerClasses",
     27  () =>
     28    new Map([
     29      [lazy.RootMessageHandler.type, lazy.RootMessageHandler],
     30      [lazy.WindowGlobalMessageHandler.type, lazy.WindowGlobalMessageHandler],
     31    ])
     32 );
     33 
     34 /**
     35 * Get the MessageHandler subclass corresponding to the provided type.
     36 
     37 * @param {string} type
     38 *     MessageHandler type, one of MessageHandler.type.
     39 * @returns {Class}
     40 *     A MessageHandler subclass
     41 * @throws {Error}
     42 *      Throws if no MessageHandler subclass is found for the provided type.
     43 */
     44 export function getMessageHandlerClass(type) {
     45  if (!lazy.MessageHandlerClasses.has(type)) {
     46    throw new Error(`No MessageHandler class available for type "${type}"`);
     47  }
     48  return lazy.MessageHandlerClasses.get(type);
     49 }
     50 
     51 /**
     52 * The MessageHandlerRegistry allows to create and retrieve MessageHandler
     53 * instances for different session ids.
     54 *
     55 * A MessageHandlerRegistry instance is bound to a specific MessageHandler type
     56 * and context. All MessageHandler instances created by the same registry will
     57 * use the type and context of the registry, but each will be associated to a
     58 * different session id.
     59 *
     60 * The registry is useful to retrieve the appropriate MessageHandler instance
     61 * after crossing a technical boundary (eg process, thread...).
     62 */
     63 export class MessageHandlerRegistry extends EventEmitter {
     64  /**
     65   * @param {string} type
     66   *     MessageHandler type, one of MessageHandler.type.
     67   * @param {object} context
     68   *     The context object, which depends on the type.
     69   */
     70  constructor(type, context) {
     71    super();
     72 
     73    this._messageHandlerClass = getMessageHandlerClass(type);
     74    this._context = context;
     75    this._type = type;
     76 
     77    /**
     78     * Map of session id to MessageHandler instance
     79     */
     80    this._messageHandlersMap = new Map();
     81 
     82    this._onMessageHandlerDestroyed =
     83      this._onMessageHandlerDestroyed.bind(this);
     84    this._onMessageHandlerEvent = this._onMessageHandlerEvent.bind(this);
     85  }
     86 
     87  /**
     88   * Create all message handlers for the current context, based on the content
     89   * of the session data.
     90   * This should typically be called when the context is ready to be used and
     91   * to receive/send commands.
     92   */
     93  createAllMessageHandlers() {
     94    const data = lazy.readSessionData();
     95    for (const [sessionId, sessionDataItems] of data) {
     96      // Create a message handler for this context for each active message
     97      // handler session.
     98      // TODO: In the future, to support debugging use cases we might want to
     99      // only create a message handler if there is relevant data.
    100      // For automation scenarios, this is less critical.
    101      this._createMessageHandler(sessionId, sessionDataItems);
    102    }
    103  }
    104 
    105  destroy() {
    106    this._messageHandlersMap.forEach(messageHandler => {
    107      messageHandler.destroy();
    108    });
    109  }
    110 
    111  /**
    112   * Retrieve all MessageHandler instances held in this registry, for all
    113   * session IDs.
    114   *
    115   * @returns {Iterable.<MessageHandler>}
    116   *     Iterator of MessageHandler instances
    117   */
    118  getAllMessageHandlers() {
    119    return this._messageHandlersMap.values();
    120  }
    121 
    122  /**
    123   * Retrieve an existing MessageHandler instance matching the provided session
    124   * id. Returns null if no MessageHandler was found.
    125   *
    126   * @param {string} sessionId
    127   *     ID of the session the handler is used for.
    128   * @returns {MessageHandler=}
    129   *     A MessageHandler instance, null if not found.
    130   */
    131  getExistingMessageHandler(sessionId) {
    132    return this._messageHandlersMap.get(sessionId);
    133  }
    134 
    135  /**
    136   * Retrieve the MessageHandler instance registered for the provided session
    137   * id. Will create and register a MessageHander if no instance was found.
    138   *
    139   * @param {string} sessionId
    140   *     ID of the session the handler is used for.
    141   * @returns {MessageHandler}
    142   *     A MessageHandler instance.
    143   */
    144  getOrCreateMessageHandler(sessionId) {
    145    let messageHandler = this.getExistingMessageHandler(sessionId);
    146    if (!messageHandler) {
    147      messageHandler = this._createMessageHandler(sessionId);
    148    }
    149 
    150    return messageHandler;
    151  }
    152 
    153  /**
    154   * Retrieve an already registered RootMessageHandler instance matching the
    155   * provided sessionId.
    156   *
    157   * @param {string} sessionId
    158   *     ID of the session the handler is used for.
    159   * @returns {RootMessageHandler}
    160   *     A RootMessageHandler instance.
    161   * @throws {Error}
    162   *     If no root MessageHandler can be found for the provided session id.
    163   */
    164  getRootMessageHandler(sessionId) {
    165    const rootMessageHandler = this.getExistingMessageHandler(
    166      sessionId,
    167      lazy.RootMessageHandler.type
    168    );
    169    if (!rootMessageHandler) {
    170      throw new Error(
    171        `Unable to find a root MessageHandler for session id ${sessionId}`
    172      );
    173    }
    174    return rootMessageHandler;
    175  }
    176 
    177  toString() {
    178    return `[object ${this.constructor.name}]`;
    179  }
    180 
    181  /**
    182   * Create a new MessageHandler instance.
    183   *
    184   * @param {string} sessionId
    185   *     ID of the session the handler will be used for.
    186   * @param {Array<SessionDataItem>=} sessionDataItems
    187   *     Optional array of session data items to be applied automatically to the
    188   *     MessageHandler.
    189   * @returns {MessageHandler}
    190   *     A new MessageHandler instance.
    191   */
    192  _createMessageHandler(sessionId, sessionDataItems) {
    193    const messageHandler = new this._messageHandlerClass(
    194      sessionId,
    195      this._context,
    196      this
    197    );
    198 
    199    messageHandler.on(
    200      "message-handler-destroyed",
    201      this._onMessageHandlerDestroyed
    202    );
    203    messageHandler.on("message-handler-event", this._onMessageHandlerEvent);
    204 
    205    messageHandler.initialize(sessionDataItems);
    206 
    207    this._messageHandlersMap.set(sessionId, messageHandler);
    208 
    209    lazy.logger.trace(
    210      `Created MessageHandler ${this._type} for session ${sessionId}`
    211    );
    212 
    213    return messageHandler;
    214  }
    215 
    216  // Event handlers
    217 
    218  _onMessageHandlerDestroyed(eventName, messageHandler) {
    219    messageHandler.off(
    220      "message-handler-destroyed",
    221      this._onMessageHandlerDestroyed
    222    );
    223    messageHandler.off("message-handler-event", this._onMessageHandlerEvent);
    224    this._messageHandlersMap.delete(messageHandler.sessionId);
    225 
    226    lazy.logger.trace(
    227      `Unregistered MessageHandler ${messageHandler.constructor.type} for session ${messageHandler.sessionId}`
    228    );
    229  }
    230 
    231  _onMessageHandlerEvent(eventName, messageHandlerEvent) {
    232    // The registry simply re-emits MessageHandler events so that consumers
    233    // don't have to attach listeners to individual MessageHandler instances.
    234    this.emit("message-handler-registry-event", messageHandlerEvent);
    235  }
    236 }