tor-browser

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

RootMessageHandler.sys.mjs (6631B)


      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 { MessageHandler } from "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  NavigationManager: "chrome://remote/content/shared/NavigationManager.sys.mjs",
     11  RootTransport:
     12    "chrome://remote/content/shared/messagehandler/transports/RootTransport.sys.mjs",
     13  SessionData:
     14    "chrome://remote/content/shared/messagehandler/sessiondata/SessionData.sys.mjs",
     15  SessionDataMethod:
     16    "chrome://remote/content/shared/messagehandler/sessiondata/SessionData.sys.mjs",
     17  WindowGlobalMessageHandler:
     18    "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs",
     19 });
     20 
     21 /**
     22 * A RootMessageHandler is the root node of a MessageHandler network. It lives
     23 * in the parent process. It can forward commands to MessageHandlers in other
     24 * layers (at the moment WindowGlobalMessageHandlers in content processes).
     25 */
     26 export class RootMessageHandler extends MessageHandler {
     27  #navigationManager;
     28  #realms;
     29  #rootTransport;
     30  #sessionData;
     31 
     32  /**
     33   * Returns the RootMessageHandler module path.
     34   *
     35   * @returns {string}
     36   */
     37  static get modulePath() {
     38    return "root";
     39  }
     40 
     41  /**
     42   * Returns the RootMessageHandler type.
     43   *
     44   * @returns {string}
     45   */
     46  static get type() {
     47    return "ROOT";
     48  }
     49 
     50  /**
     51   * The ROOT MessageHandler is unique for a given MessageHandler network
     52   * (ie for a given sessionId). Reuse the type as context id here.
     53   */
     54  static getIdFromContext() {
     55    return RootMessageHandler.type;
     56  }
     57 
     58  /**
     59   * Create a new RootMessageHandler instance.
     60   *
     61   * @param {string} sessionId
     62   *     ID of the session the handler is used for.
     63   */
     64  constructor(sessionId) {
     65    super(sessionId, null);
     66 
     67    this.#rootTransport = new lazy.RootTransport(this);
     68    this.#sessionData = new lazy.SessionData(this);
     69    this.#navigationManager = new lazy.NavigationManager();
     70    this.#navigationManager.startMonitoring();
     71 
     72    // Map with inner window ids as keys, and sets of realm ids, associated with
     73    // this window as values.
     74    this.#realms = new Map();
     75    // In the general case, we don't get notified that realms got destroyed,
     76    // because there is no communication between content and parent process at this moment,
     77    // so we have to listen to the this notification to clean up the internal
     78    // map and trigger the events.
     79    Services.obs.addObserver(this, "window-global-destroyed");
     80  }
     81 
     82  get navigationManager() {
     83    return this.#navigationManager;
     84  }
     85 
     86  get realms() {
     87    return this.#realms;
     88  }
     89 
     90  get sessionData() {
     91    return this.#sessionData;
     92  }
     93 
     94  destroy() {
     95    this.#sessionData.destroy();
     96    this.#navigationManager.destroy();
     97 
     98    Services.obs.removeObserver(this, "window-global-destroyed");
     99    this.#realms = null;
    100 
    101    super.destroy();
    102  }
    103 
    104  /**
    105   * Add new session data items of a given module, category and
    106   * contextDescriptor.
    107   *
    108   * Forwards the call to the SessionData instance owned by this
    109   * RootMessageHandler and propagates the information via a command to existing
    110   * MessageHandlers.
    111   */
    112  addSessionDataItem(sessionData = {}) {
    113    sessionData.method = lazy.SessionDataMethod.Add;
    114    return this.updateSessionData([sessionData]);
    115  }
    116 
    117  emitEvent(name, eventPayload, contextInfo) {
    118    // Intercept realm created and destroyed events to update internal map.
    119    if (name === "realm-created") {
    120      this.#onRealmCreated(eventPayload);
    121    }
    122    // We receive this events in the case of moving the page to BFCache.
    123    if (name === "windowglobal-pagehide") {
    124      this.#cleanUpRealmsForWindow(
    125        eventPayload.innerWindowId,
    126        eventPayload.context
    127      );
    128    }
    129 
    130    super.emitEvent(name, eventPayload, contextInfo);
    131  }
    132 
    133  /**
    134   * Emit a public protocol event. This event will be sent over to the client.
    135   *
    136   * @param {string} name
    137   *     Name of the event. Protocol level events should be of the
    138   *     form [module name].[event name].
    139   * @param {object} data
    140   *     The event's data.
    141   */
    142  emitProtocolEvent(name, data) {
    143    this.emit("message-handler-protocol-event", {
    144      name,
    145      data,
    146      sessionId: this.sessionId,
    147    });
    148  }
    149 
    150  /**
    151   * Forward the provided command to WINDOW_GLOBAL MessageHandlers via the
    152   * RootTransport.
    153   *
    154   * @param {Command} command
    155   *     The command to forward. See type definition in MessageHandler.js
    156   * @returns {Promise}
    157   *     Returns a promise that resolves with the result of the command.
    158   */
    159  forwardCommand(command) {
    160    switch (command.destination.type) {
    161      case lazy.WindowGlobalMessageHandler.type:
    162        return this.#rootTransport.forwardCommand(command);
    163      default:
    164        throw new Error(
    165          `Cannot forward command to "${command.destination.type}" from "${this.constructor.type}".`
    166        );
    167    }
    168  }
    169 
    170  matchesContext() {
    171    return true;
    172  }
    173 
    174  observe(subject, topic) {
    175    if (topic !== "window-global-destroyed") {
    176      return;
    177    }
    178 
    179    this.#cleanUpRealmsForWindow(
    180      subject.innerWindowId,
    181      subject.browsingContext
    182    );
    183  }
    184 
    185  /**
    186   * Remove session data items of a given module, category and
    187   * contextDescriptor.
    188   *
    189   * Forwards the call to the SessionData instance owned by this
    190   * RootMessageHandler and propagates the information via a command to existing
    191   * MessageHandlers.
    192   */
    193  removeSessionDataItem(sessionData = {}) {
    194    sessionData.method = lazy.SessionDataMethod.Remove;
    195    return this.updateSessionData([sessionData]);
    196  }
    197 
    198  /**
    199   * Update session data items of a given module, category and
    200   * contextDescriptor.
    201   *
    202   * Forwards the call to the SessionData instance owned by this
    203   * RootMessageHandler.
    204   */
    205  async updateSessionData(sessionData = []) {
    206    await this.#sessionData.updateSessionData(sessionData);
    207  }
    208 
    209  #cleanUpRealmsForWindow(innerWindowId, context) {
    210    const realms = this.#realms.get(innerWindowId);
    211 
    212    if (!realms) {
    213      return;
    214    }
    215 
    216    realms.forEach(realm => {
    217      this.#realms.get(innerWindowId).delete(realm);
    218 
    219      this.emitEvent("realm-destroyed", {
    220        context,
    221        realm,
    222      });
    223    });
    224 
    225    this.#realms.delete(innerWindowId);
    226  }
    227 
    228  #onRealmCreated = data => {
    229    const { innerWindowId, realmInfo } = data;
    230 
    231    if (!this.#realms.has(innerWindowId)) {
    232      this.#realms.set(innerWindowId, new Set());
    233    }
    234 
    235    this.#realms.get(innerWindowId).add(realmInfo.realm);
    236  };
    237 }