tor-browser

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

FrontClassWithSpec.js (4329B)


      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 {
      8  BULK_REQUEST,
      9  BULK_RESPONSE,
     10 } = require("resource://devtools/shared/protocol/types.js");
     11 var { Front } = require("resource://devtools/shared/protocol/Front.js");
     12 
     13 /**
     14 * Generates request methods as described by the given actor specification on
     15 * the given front prototype. Returns the front prototype.
     16 */
     17 var generateRequestMethods = function (actorSpec, frontProto) {
     18  if (frontProto._actorSpec) {
     19    throw new Error("frontProto called twice on the same front prototype!");
     20  }
     21 
     22  frontProto.typeName = actorSpec.typeName;
     23 
     24  // Generate request methods.
     25  const methods = actorSpec.methods;
     26  methods.forEach(spec => {
     27    const { name } = spec;
     28 
     29    frontProto[name] = function (...args) {
     30      // If the front is destroyed, the request will not be able to complete.
     31      if (this.isDestroyed()) {
     32        throw new Error(
     33          `Can not send request '${name}' because front '${this.typeName}' is already destroyed.`
     34        );
     35      }
     36 
     37      const startTime = ChromeUtils.now();
     38      let packet;
     39      try {
     40        packet = spec.request.write(args, this);
     41      } catch (ex) {
     42        console.error("Error writing request: " + name);
     43        throw ex;
     44      }
     45      if (spec.oneway) {
     46        // Fire-and-forget oneway packets.
     47        this.send(packet);
     48        return undefined;
     49      }
     50 
     51      // Check if the client request should be sent as a bulk request
     52      const isSendingBulkData = spec.request.template === BULK_REQUEST;
     53 
     54      // If so, pass the last front argument as the bulk initialization callback
     55      const clientBulkCallback = isSendingBulkData ? args.at(-1) : null;
     56 
     57      return this.request(packet, {
     58        bulk: isSendingBulkData,
     59        clientBulkCallback,
     60      }).then(response => {
     61        // If the request returns bulk data, return the transport response as-is.
     62        // We do not expect any custom packet/attributes for bulk responses,
     63        // the transport will handle the binary stream communication and expose
     64        // the StreamCopier as resolution value in the returned Promise.
     65        const isReceivingBulkData = spec.response.template === BULK_RESPONSE;
     66        if (isReceivingBulkData) {
     67          return response;
     68        }
     69 
     70        let ret;
     71        if (!this.conn) {
     72          throw new Error("Missing conn on " + this);
     73        }
     74        if (this.isDestroyed()) {
     75          throw new Error(
     76            `Can not interpret '${name}' response because front '${this.typeName}' is already destroyed.`
     77          );
     78        }
     79        try {
     80          ret = spec.response.read(response, this);
     81        } catch (ex) {
     82          console.error("Error reading response to: " + name + "\n" + ex);
     83          throw ex;
     84        }
     85        ChromeUtils.addProfilerMarker(
     86          "RDP Front",
     87          startTime,
     88          `${this.typeName}:${name}()`
     89        );
     90        return ret;
     91      });
     92    };
     93 
     94    // Release methods should call the destroy function on return.
     95    if (spec.release) {
     96      const fn = frontProto[name];
     97      frontProto[name] = function (...args) {
     98        return fn.apply(this, args).then(result => {
     99          this.destroy();
    100          return result;
    101        });
    102      };
    103    }
    104  });
    105 
    106  // Process event specifications
    107  frontProto._clientSpec = {};
    108 
    109  const actorEvents = actorSpec.events;
    110  if (actorEvents) {
    111    frontProto._clientSpec.events = new Map();
    112 
    113    for (const [name, request] of actorEvents) {
    114      frontProto._clientSpec.events.set(request.type, {
    115        name,
    116        request,
    117      });
    118    }
    119  }
    120 
    121  frontProto._actorSpec = actorSpec;
    122 
    123  return frontProto;
    124 };
    125 
    126 /**
    127 * Create a front class for the given actor specification and front prototype.
    128 *
    129 * @param object actorSpec
    130 *    The actor specification you're creating a front for.
    131 * @param object proto
    132 *    The object prototype.  Must have a 'typeName' property,
    133 *    should have method definitions, can have event definitions.
    134 */
    135 var FrontClassWithSpec = function (actorSpec) {
    136  class OneFront extends Front {}
    137  generateRequestMethods(actorSpec, OneFront.prototype);
    138  return OneFront;
    139 };
    140 exports.FrontClassWithSpec = FrontClassWithSpec;