tor-browser

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

worker-connector.js (7255B)


      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 "use strict";
      6 
      7 var DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
      8 
      9 loader.lazyRequireGetter(
     10  this,
     11  "MainThreadWorkerDebuggerTransport",
     12  "resource://devtools/shared/transport/worker-transport.js",
     13  true
     14 );
     15 
     16 /**
     17 * Start a DevTools server in a worker and add it as a child server for a given active connection.
     18 *
     19 * @param {DevToolsConnection} connection
     20 * @param {WorkerDebugger} dbg: The WorkerDebugger we want to create a target actor for.
     21 * @param {string} forwardingPrefix: The prefix that will be used to forward messages
     22 *                 to the DevToolsServer on the worker thread.
     23 * @param {object} options: An option object that will be passed with the "connect" packet.
     24 * @param {object} options.sessionData: The sessionData object that will be passed to the
     25 *                 worker target actor.
     26 */
     27 function connectToWorker(connection, dbg, forwardingPrefix, options) {
     28  return new Promise((resolve, reject) => {
     29    if (!DevToolsUtils.isWorkerDebuggerAlive(dbg)) {
     30      reject("closed");
     31      return;
     32    }
     33 
     34    // Step 1: Ensure the worker debugger is initialized.
     35    if (!dbg.isInitialized) {
     36      dbg.initialize(
     37        "resource://devtools/server/startup/shared-worker-initializer.js"
     38      );
     39 
     40      // Create a listener for rpc requests from the worker debugger. Only do
     41      // this once, when the worker debugger is first initialized, rather than
     42      // for each connection.
     43      const listener = {
     44        onClose: () => {
     45          dbg.removeListener(listener);
     46        },
     47 
     48        onMessage: message => {
     49          message = JSON.parse(message);
     50          if (message.type !== "rpc") {
     51            if (message.type == "session-data-processed") {
     52              // The thread actor has finished processing session data, including breakpoints.
     53              // Allow content to begin executing in the worker and possibly hit early breakpoints.
     54              dbg.setDebuggerReady(true);
     55            }
     56            return;
     57          }
     58 
     59          Promise.resolve()
     60            .then(() => {
     61              const method = {
     62                fetch: DevToolsUtils.fetch,
     63              }[message.method];
     64              if (!method) {
     65                throw Error("Unknown method: " + message.method);
     66              }
     67 
     68              return method.apply(undefined, message.params);
     69            })
     70            .then(
     71              value => {
     72                dbg.postMessage(
     73                  JSON.stringify({
     74                    type: "rpc",
     75                    result: value,
     76                    error: null,
     77                    id: message.id,
     78                  })
     79                );
     80              },
     81              reason => {
     82                dbg.postMessage(
     83                  JSON.stringify({
     84                    type: "rpc",
     85                    result: null,
     86                    error: reason,
     87                    id: message.id,
     88                  })
     89                );
     90              }
     91            );
     92        },
     93      };
     94 
     95      dbg.addListener(listener);
     96    }
     97 
     98    if (!DevToolsUtils.isWorkerDebuggerAlive(dbg)) {
     99      reject("closed");
    100      return;
    101    }
    102 
    103    // WorkerDebugger.url isn't always an absolute URL.
    104    // Use the related document URL in order to make it absolute.
    105    const absoluteURL = dbg.window?.location?.href
    106      ? new URL(dbg.url, dbg.window.location.href).href
    107      : dbg.url;
    108 
    109    // Step 2: Send a connect request to the worker debugger.
    110    dbg.postMessage(
    111      JSON.stringify({
    112        type: "connect",
    113        forwardingPrefix,
    114        options,
    115        workerDebuggerData: {
    116          id: dbg.id,
    117          name: dbg.name,
    118          type: dbg.type,
    119          relatedDocumentInnerWindowId:
    120            dbg.window?.windowGlobalChild?.innerWindowId,
    121          url: absoluteURL,
    122          // We don't have access to Services.prefs in Worker thread, so pass its value
    123          // from here.
    124          workerConsoleApiMessagesDispatchedToMainThread:
    125            Services.prefs.getBoolPref(
    126              "dom.worker.console.dispatch_events_to_main_thread"
    127            ),
    128        },
    129      })
    130    );
    131 
    132    // Steps 3-5 are performed on the worker thread (see worker.js).
    133 
    134    // Step 6: Wait for a connection response from the worker debugger.
    135    const listener = {
    136      onClose: () => {
    137        dbg.removeListener(listener);
    138 
    139        reject("closed");
    140      },
    141 
    142      onMessage: message => {
    143        message = JSON.parse(message);
    144        if (
    145          message.type !== "connected" ||
    146          message.forwardingPrefix !== forwardingPrefix
    147        ) {
    148          return;
    149        }
    150 
    151        // The initial connection message has been received, don't
    152        // need to listen any longer
    153        dbg.removeListener(listener);
    154 
    155        // Step 7: Create a transport for the connection to the worker.
    156        const transport = new MainThreadWorkerDebuggerTransport(
    157          dbg,
    158          forwardingPrefix
    159        );
    160        transport.ready();
    161        transport.hooks = {
    162          onTransportClosed: () => {
    163            if (DevToolsUtils.isWorkerDebuggerAlive(dbg)) {
    164              // If the worker happens to be shutting down while we are trying
    165              // to close the connection, there is a small interval during
    166              // which no more runnables can be dispatched to the worker, but
    167              // the worker debugger has not yet been closed. In that case,
    168              // the call to postMessage below will fail. The onTransportClosed hook on
    169              // DebuggerTransport is not supposed to throw exceptions, so we
    170              // need to make sure to catch these early.
    171              try {
    172                dbg.postMessage(
    173                  JSON.stringify({
    174                    type: "disconnect",
    175                    forwardingPrefix,
    176                  })
    177                );
    178              } catch (e) {
    179                // We can safely ignore these exceptions. The only time the
    180                // call to postMessage can fail is if the worker is either
    181                // shutting down, or has finished shutting down. In both
    182                // cases, there is nothing to clean up, so we don't care
    183                // whether this message arrives or not.
    184              }
    185            }
    186 
    187            connection.cancelForwarding(forwardingPrefix);
    188          },
    189 
    190          onPacket: packet => {
    191            // Ensure that any packets received from the server on the worker
    192            // thread are forwarded to the client on the main thread, as if
    193            // they had been sent by the server on the main thread.
    194            connection.send(packet);
    195          },
    196        };
    197 
    198        // Ensure that any packets received from the client on the main thread
    199        // to actors on the worker thread are forwarded to the server on the
    200        // worker thread.
    201        connection.setForwarding(forwardingPrefix, transport);
    202 
    203        resolve({
    204          workerTargetForm: message.workerTargetForm,
    205          transport,
    206        });
    207      },
    208    };
    209    dbg.addListener(listener);
    210  });
    211 }
    212 
    213 exports.connectToWorker = connectToWorker;