tor-browser

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

worker.sys.mjs (5477B)


      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 /* global global, postMessage */
      6 
      7 /*
      8 * Worker debugger script that listens for requests to start a `DevToolsServer` for a
      9 * worker in a process.  Loaded into a specific worker during worker-connector.js'
     10 * `connectToWorker` which is called from the same process as the worker.
     11 */
     12 
     13 // This function is used to do remote procedure calls from the worker to the
     14 // main thread. It is exposed as a built-in global to every module by the
     15 // worker loader. To make sure the worker loader can access it, it needs to be
     16 // defined before loading the worker loader script below.
     17 let nextId = 0;
     18 const workerGlobal = globalThis;
     19 
     20 workerGlobal.rpc = function (method, ...params) {
     21  return new Promise((resolve, reject) => {
     22    const id = nextId++;
     23    workerGlobal.addEventListener("message", function onMessageForRpc(event) {
     24      const packet = JSON.parse(event.data);
     25      if (packet.type !== "rpc" || packet.id !== id) {
     26        return;
     27      }
     28      if (packet.error) {
     29        reject(packet.error);
     30      } else {
     31        resolve(packet.result);
     32      }
     33      workerGlobal.removeEventListener("message", onMessageForRpc);
     34    });
     35 
     36    postMessage(
     37      JSON.stringify({
     38        type: "rpc",
     39        method,
     40        params,
     41        id,
     42      })
     43    );
     44  });
     45 };
     46 
     47 const { worker } = ChromeUtils.importESModule(
     48  "resource://devtools/shared/loader/worker-loader.sys.mjs",
     49  { global: "current" }
     50 );
     51 
     52 const { WorkerTargetActor } = worker.require(
     53  "resource://devtools/server/actors/targets/worker.js"
     54 );
     55 const { DevToolsServer } = worker.require(
     56  "resource://devtools/server/devtools-server.js"
     57 );
     58 
     59 DevToolsServer.createRootActor = function () {
     60  throw new Error("Should never get here!");
     61 };
     62 
     63 // This file is only instanciated once for a given WorkerDebugger, which means that
     64 // multiple toolbox could end up using the same instance of this script. In order to handle
     65 // that, we handle a Map of the different connections, keyed by forwarding prefix.
     66 const connections = new Map();
     67 
     68 export async function handleDevToolsPacket(packet) {
     69  switch (packet.type) {
     70    case "connect": {
     71      const { forwardingPrefix } = packet;
     72 
     73      // Force initializing the server each time on connect
     74      // as it may have been destroyed by a previous, now closed toolbox.
     75      // Once the last connection drops, the server auto destroy itself.
     76      DevToolsServer.init();
     77 
     78      // Step 3: Create a connection to the parent.
     79      const connection = DevToolsServer.connectToParent(
     80        forwardingPrefix,
     81        workerGlobal
     82      );
     83 
     84      // Step 4: Create a WorkerTarget actor.
     85      const workerTargetActor = new WorkerTargetActor(
     86        connection,
     87        global,
     88        packet.workerDebuggerData,
     89        packet.options.sessionContext
     90      );
     91      // Make the worker manage itself so it is put in a Pool and assigned an actorID.
     92      workerTargetActor.manage(workerTargetActor);
     93 
     94      // Step 5: Send a response packet to the parent to notify
     95      // it that a connection has been established.
     96      connections.set(forwardingPrefix, {
     97        connection,
     98        workerTargetActor,
     99      });
    100 
    101      // Immediately notify about the target actor form,
    102      // so that we can emit RDP events from the target actor
    103      // and have them correctly routed up to the frontend.
    104      // The target front has to be first created by receiving its form
    105      // before being able to receive RDP events.
    106      postMessage(
    107        JSON.stringify({
    108          type: "connected",
    109          forwardingPrefix,
    110          workerTargetForm: workerTargetActor.form(),
    111        })
    112      );
    113 
    114      // We might receive data to watch.
    115      if (packet.options.sessionData) {
    116        const promises = [];
    117        for (const [type, entries] of Object.entries(
    118          packet.options.sessionData
    119        )) {
    120          promises.push(
    121            workerTargetActor.addOrSetSessionDataEntry(
    122              type,
    123              entries,
    124              false,
    125              "set"
    126            )
    127          );
    128        }
    129        await Promise.all(promises);
    130      }
    131 
    132      // Finally, notify when we are done processing session data
    133      // We are processing breakpoints, which means we can release the execution of the worker
    134      // from the main thread via `WorkerDebugger.setDebuggerReady(true)`
    135      postMessage(JSON.stringify({ type: "session-data-processed" }));
    136 
    137      break;
    138    }
    139 
    140    case "add-or-set-session-data-entry":
    141      await connections
    142        .get(packet.forwardingPrefix)
    143        .workerTargetActor.addOrSetSessionDataEntry(
    144          packet.dataEntryType,
    145          packet.entries,
    146          packet.updateType
    147        );
    148      postMessage(JSON.stringify({ type: "session-data-entry-added-or-set" }));
    149      break;
    150 
    151    case "remove-session-data-entry":
    152      await connections
    153        .get(packet.forwardingPrefix)
    154        .workerTargetActor.removeSessionDataEntry(
    155          packet.dataEntryType,
    156          packet.entries
    157        );
    158      break;
    159 
    160    case "disconnect":
    161      // This will destroy the associate WorkerTargetActor (and the actors it manages).
    162      if (connections.has(packet.forwardingPrefix)) {
    163        connections.get(packet.forwardingPrefix).connection.close();
    164        connections.delete(packet.forwardingPrefix);
    165      }
    166      break;
    167  }
    168 }