tor-browser

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

worker.sys.mjs (4727B)


      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 let MESSAGE_COUNTER = 0;
      6 
      7 function dumpn(_msg) {
      8  // dump(msg + "\n");
      9 }
     10 
     11 /**
     12 * Creates a wrapper around a ChromeWorker, providing easy
     13 * communication to offload demanding tasks. The corresponding URL
     14 * must implement the interface provided by `devtools/shared/worker/helper`.
     15 *
     16 * @param {string} url
     17 *        The URL of the worker.
     18 * @param Object opts
     19 *        An option with the following optional fields:
     20 *        - name: a name that will be printed with logs
     21 *        - verbose: log incoming and outgoing messages
     22 */
     23 export function DevToolsWorker(url, opts) {
     24  opts = opts || {};
     25  this._worker = new ChromeWorker(url);
     26  this._verbose = opts.verbose;
     27  this._name = opts.name;
     28 
     29  this._worker.addEventListener("error", this.onError);
     30 }
     31 
     32 /**
     33 * Performs the given task in a chrome worker, passing in data.
     34 * Returns a promise that resolves when the task is completed, resulting in
     35 * the return value of the task.
     36 *
     37 * @param {string} task
     38 *        The name of the task to execute in the worker.
     39 * @param {any} data
     40 *        Data to be passed into the task implemented by the worker.
     41 * @param {undefined|Array} transfer
     42 *        Optional array of transferable objects to transfer ownership of.
     43 * @return {Promise}
     44 */
     45 DevToolsWorker.prototype.performTask = function (task, data, transfer) {
     46  if (this._destroyed) {
     47    return Promise.reject(
     48      "Cannot call performTask on a destroyed DevToolsWorker"
     49    );
     50  }
     51  const worker = this._worker;
     52  const id = ++MESSAGE_COUNTER;
     53  const payload = { task, id, data };
     54 
     55  if (this._verbose && dumpn) {
     56    dumpn(
     57      "Sending message to worker" +
     58        (this._name ? " (" + this._name + ")" : "") +
     59        ": " +
     60        JSON.stringify(payload, null, 2)
     61    );
     62  }
     63  worker.postMessage(payload, transfer);
     64 
     65  return new Promise((resolve, reject) => {
     66    const listener = ({ data: result }) => {
     67      if (this._verbose && dumpn) {
     68        dumpn(
     69          "Received message from worker" +
     70            (this._name ? " (" + this._name + ")" : "") +
     71            ": " +
     72            JSON.stringify(result, null, 2)
     73        );
     74      }
     75 
     76      if (result.id !== id) {
     77        return;
     78      }
     79      worker.removeEventListener("message", listener);
     80      if (result.error) {
     81        reject(result.error);
     82      } else {
     83        resolve(result.response);
     84      }
     85    };
     86 
     87    worker.addEventListener("message", listener);
     88  });
     89 };
     90 
     91 /**
     92 * Terminates the underlying worker. Use when no longer needing the worker.
     93 */
     94 DevToolsWorker.prototype.destroy = function () {
     95  this._worker.terminate();
     96  this._worker = null;
     97  this._destroyed = true;
     98 };
     99 
    100 DevToolsWorker.prototype.onError = function ({ message, filename, lineno }) {
    101  dump(new Error(message + " @ " + filename + ":" + lineno) + "\n");
    102 };
    103 
    104 /**
    105 * Takes a function and returns a Worker-wrapped version of the same function.
    106 * Returns a promise upon resolution.
    107 *
    108 * @see `./devtools/shared/shared/tests/browser/browser_devtools-worker-03.js
    109 *
    110 * ⚠ This should only be used for tests or A/B testing performance ⚠
    111 *
    112 * The original function must:
    113 *
    114 * Be a pure function, that is, not use any variables not declared within the
    115 * function, or its arguments.
    116 *
    117 * Return a value or a promise.
    118 *
    119 * Note any state change in the worker will not affect the callee's context.
    120 *
    121 * @param {function} fn
    122 * @return {function}
    123 */
    124 export function workerify(fn) {
    125  console.warn(
    126    "`workerify` should only be used in tests or measuring performance. " +
    127      "This creates an object URL on the browser window, and should not be " +
    128      "used in production."
    129  );
    130  // Fetch modules here as we don't want to include it normally.
    131  // eslint-disable-next-line no-shadow
    132  const { URL, Blob } = Services.wm.getMostRecentBrowserWindow();
    133  const stringifiedFn = createWorkerString(fn);
    134  const blob = new Blob([stringifiedFn]);
    135  const url = URL.createObjectURL(blob);
    136  const worker = new DevToolsWorker(url);
    137 
    138  const wrapperFn = (data, transfer) =>
    139    worker.performTask("workerifiedTask", data, transfer);
    140 
    141  wrapperFn.destroy = function () {
    142    URL.revokeObjectURL(url);
    143    worker.destroy();
    144  };
    145 
    146  return wrapperFn;
    147 }
    148 
    149 /**
    150 * Takes a function, and stringifies it, attaching the worker-helper.js
    151 * boilerplate hooks.
    152 */
    153 function createWorkerString(fn) {
    154  return `importScripts("resource://gre/modules/workers/require.js");
    155          const { createTask } = require("resource://devtools/shared/worker/helper.js");
    156          createTask(self, "workerifiedTask", ${fn.toString()});`;
    157 }