tor-browser

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

script-command.js (6617B)


      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 const {
      8  getAdHocFrontOrPrimitiveGrip,
      9  // eslint-disable-next-line mozilla/reject-some-requires
     10 } = require("resource://devtools/client/fronts/object.js");
     11 
     12 class ScriptCommand {
     13  constructor({ commands }) {
     14    this._commands = commands;
     15  }
     16 
     17  /**
     18   * Execute a JavaScript expression.
     19   *
     20   * @param {string} expression: The code you want to evaluate.
     21   * @param {object} options: Options for evaluation:
     22   * @param {object} options.frameActor: a FrameActor ID. The actor holds a reference to
     23   *        a Debugger.Frame. This option allows you to evaluate the string in the frame
     24   *        of the given FrameActor.
     25   * @param {string} options.url: the url to evaluate the script as. Defaults to "debugger eval code".
     26   * @param {TargetFront} options.selectedTargetFront: When passed, the expression will be
     27   *        evaluated in the context of the target (as opposed to the default, top-level one).
     28   * @param {string} options.selectedNodeActor: A NodeActor ID that may be used by helper
     29   *        functions that can reference the currently selected node in the Inspector, like $0.
     30   * @param {string} options.selectedObjectActor: the actorID of a given objectActor.
     31   *        This is used by context menu entries to get a reference to an object, in order
     32   *        to perform some operation on it (copy it, store it as a global variable, …).
     33   * @param {number} options.innerWindowID: An optional window id to be used for the evaluation,
     34   *        instead of the regular webConsoleActor.evalWindow.
     35   *        This is used by functions that may want to evaluate in a different window (for
     36   *        example a non-remote iframe), like getting the elements of a given document.
     37   * @param {object} options.mapped: An optional object indicating if the original expression
     38   *        entered by the users have been modified
     39   * @param {boolean} options.mapped.await: true if the expression was a top-level await
     40   *        expression that was wrapped in an async-iife
     41   * @param {boolean} options.disableBreaks: Set to true to avoid triggering any
     42   *        type of breakpoint when evaluating the source. Also, the evaluated source won't be
     43   *        visible in the debugger UI.
     44   * @param {boolean} options.preferConsoleCommandsOverLocalSymbols: Set to true to force
     45   *        overriding local symbols defined by the page with same-name console commands.
     46   * @param {boolean} options.evalInTracer: Set to true to store the evaluation result object
     47   *        actors within the Tracer Object Actor Pool.
     48   *        So that if the evaluation result object has already been processed by the JS Tracer,
     49   *        we will receive the same Object Actor.
     50   *        This helps later match the object when doing a search per value.
     51   *
     52   * @return {Promise}: A promise that resolves with the response.
     53   */
     54  async execute(expression, options = {}) {
     55    const {
     56      selectedObjectActor,
     57      selectedNodeActor,
     58      frameActor,
     59      selectedTargetFront,
     60    } = options;
     61 
     62    // Retrieve the right WebConsole front that relates either to (by order of priority):
     63    // - the currently selected target in the context selector
     64    //   (selectedTargetFront argument),
     65    // - the object picked in the console (when using store as global) (selectedObjectActor),
     66    // - the currently selected Node in the inspector (selectedNodeActor),
     67    // - the currently selected frame in the debugger (when paused) (frameActor),
     68    // - the currently selected target in the iframe dropdown
     69    //   (selectedTargetFront from the TargetCommand)
     70    let targetFront = this._commands.targetCommand.selectedTargetFront;
     71 
     72    const selectedActor =
     73      selectedObjectActor || selectedNodeActor || frameActor;
     74 
     75    if (selectedTargetFront) {
     76      targetFront = selectedTargetFront;
     77    } else if (selectedActor) {
     78      const selectedFront = this._commands.client.getFrontByID(selectedActor);
     79      if (selectedFront) {
     80        targetFront = selectedFront.targetFront;
     81      }
     82    }
     83 
     84    const consoleFront = await targetFront.getFront("console");
     85 
     86    // We call `evaluateJSAsync` RDP request, which immediately returns a simple `resultID`,
     87    // for which we later receive a related `evaluationResult` RDP event, with the same resultID.
     88    // The evaluation result will be contained in this RDP event.
     89    let resultID;
     90    const response = await new Promise(resolve => {
     91      const offEvaluationResult = consoleFront.on(
     92        "evaluationResult",
     93        async packet => {
     94          // In some cases, the evaluationResult event can be received before the call to
     95          // evaluationJSAsync completes. So make sure to wait for the corresponding promise
     96          // before handling the evaluationResult event.
     97          await onEvaluateJSAsync;
     98 
     99          if (packet.resultID === resultID) {
    100            resolve(packet);
    101            offEvaluationResult();
    102          }
    103        }
    104      );
    105 
    106      const onEvaluateJSAsync = consoleFront
    107        .evaluateJSAsync({
    108          text: expression,
    109          eager: options.eager,
    110          frameActor,
    111          innerWindowID: options.innerWindowID,
    112          mapped: options.mapped,
    113          selectedNodeActor,
    114          selectedObjectActor,
    115          url: options.url,
    116          disableBreaks: options.disableBreaks,
    117          preferConsoleCommandsOverLocalSymbols:
    118            options.preferConsoleCommandsOverLocalSymbols,
    119          evalInTracer: options.evalInTracer,
    120        })
    121        .then(packet => {
    122          resultID = packet.resultID;
    123        });
    124    });
    125 
    126    // `response` is the packet sent via `evaluationResult` RDP event.
    127    if (response.error) {
    128      throw response;
    129    }
    130 
    131    if (response.result) {
    132      response.result = getAdHocFrontOrPrimitiveGrip(
    133        response.result,
    134        consoleFront
    135      );
    136    }
    137 
    138    if (response.helperResult?.object) {
    139      response.helperResult.object = getAdHocFrontOrPrimitiveGrip(
    140        response.helperResult.object,
    141        consoleFront
    142      );
    143    }
    144 
    145    if (response.exception) {
    146      response.exception = getAdHocFrontOrPrimitiveGrip(
    147        response.exception,
    148        consoleFront
    149      );
    150    }
    151 
    152    if (response.exceptionMessage) {
    153      response.exceptionMessage = getAdHocFrontOrPrimitiveGrip(
    154        response.exceptionMessage,
    155        consoleFront
    156      );
    157    }
    158 
    159    return response;
    160  }
    161 }
    162 
    163 module.exports = ScriptCommand;