tor-browser

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

frame.js (6116B)


      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 { Actor } = require("resource://devtools/shared/protocol/Actor.js");
      8 const { Pool } = require("resource://devtools/shared/protocol/Pool.js");
      9 const { frameSpec } = require("resource://devtools/shared/specs/frame.js");
     10 
     11 const Debugger = require("Debugger");
     12 const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
     13 
     14 function formatDisplayName(frame) {
     15  if (frame.type === "call") {
     16    const callee = frame.callee;
     17    return callee.name || callee.userDisplayName || callee.displayName;
     18  }
     19 
     20  return `(${frame.type})`;
     21 }
     22 
     23 function isDeadSavedFrame(savedFrame) {
     24  return Cu && Cu.isDeadWrapper(savedFrame);
     25 }
     26 function isValidSavedFrame(threadActor, savedFrame) {
     27  return (
     28    !isDeadSavedFrame(savedFrame) &&
     29    // If the frame's source is unknown to the debugger, then we ignore it
     30    // since the frame likely does not belong to a realm that is marked
     31    // as a debuggee.
     32    // This check will also fail if the frame would have been known but was
     33    // GCed before the debugger was opened on the page.
     34    // TODO: Use SavedFrame's security principal to limit non-debuggee frames
     35    // and pass all unknown frames to the debugger as a URL with no sourceID.
     36    getSavedFrameSource(threadActor, savedFrame)
     37  );
     38 }
     39 function getSavedFrameSource(threadActor, savedFrame) {
     40  return threadActor.sourcesManager.getSourceActorByInternalSourceId(
     41    savedFrame.sourceId
     42  );
     43 }
     44 function getSavedFrameParent(threadActor, savedFrame) {
     45  if (isDeadSavedFrame(savedFrame)) {
     46    return null;
     47  }
     48 
     49  while (true) {
     50    savedFrame = savedFrame.parent || savedFrame.asyncParent;
     51 
     52    // If the saved frame is a dead wrapper, we don't have any way to keep
     53    // stepping through parent frames.
     54    if (!savedFrame || isDeadSavedFrame(savedFrame)) {
     55      savedFrame = null;
     56      break;
     57    }
     58 
     59    if (isValidSavedFrame(threadActor, savedFrame)) {
     60      break;
     61    }
     62  }
     63  return savedFrame;
     64 }
     65 
     66 /**
     67 * An actor for a specified stack frame.
     68 */
     69 class FrameActor extends Actor {
     70  /**
     71   * Creates the Frame actor.
     72   *
     73   * @param frame Debugger.Frame|SavedFrame
     74   *        The debuggee frame.
     75   * @param threadActor ThreadActor
     76   *        The parent thread actor for this frame.
     77   */
     78  constructor(frame, threadActor, depth) {
     79    super(threadActor.conn, frameSpec);
     80 
     81    this.frame = frame;
     82    this.threadActor = threadActor;
     83    this.depth = depth;
     84  }
     85 
     86  /**
     87   * A pool that contains frame-lifetime objects, like the environment.
     88   */
     89  _frameLifetimePool = null;
     90  get frameLifetimePool() {
     91    if (!this._frameLifetimePool) {
     92      this._frameLifetimePool = new Pool(this.conn, "frame");
     93    }
     94    return this._frameLifetimePool;
     95  }
     96 
     97  /**
     98   * Finalization handler that is called when the actor is being evicted from
     99   * the pool.
    100   */
    101  destroy() {
    102    if (this._frameLifetimePool) {
    103      this._frameLifetimePool.destroy();
    104      this._frameLifetimePool = null;
    105    }
    106    super.destroy();
    107  }
    108 
    109  getEnvironment() {
    110    try {
    111      if (!this.frame.environment) {
    112        return {};
    113      }
    114    } catch (e) {
    115      // |this.frame| might not be live. FIXME Bug 1477030 we shouldn't be
    116      // using frames we derived from a point where we are not currently
    117      // paused at.
    118      return {};
    119    }
    120 
    121    const envActor = this.threadActor.createEnvironmentActor(
    122      this.frame.environment,
    123      this.frameLifetimePool
    124    );
    125 
    126    return envActor.form();
    127  }
    128 
    129  /**
    130   * Returns a frame form for use in a protocol message.
    131   */
    132  form() {
    133    // SavedFrame actors have their own frame handling.
    134    if (!(this.frame instanceof Debugger.Frame)) {
    135      // The Frame actor shouldn't be used after evaluation is resumed, so
    136      // there shouldn't be an easy way for the saved frame to be referenced
    137      // once it has died.
    138      assert(!isDeadSavedFrame(this.frame));
    139 
    140      const obj = {
    141        actor: this.actorID,
    142        // TODO: Bug 1610418 - Consider updating SavedFrame to have a type.
    143        type: "dead",
    144        asyncCause: this.frame.asyncCause,
    145        state: "dead",
    146        displayName: this.frame.functionDisplayName,
    147        arguments: [],
    148        where: {
    149          // The frame's source should always be known because
    150          // getSavedFrameParent will skip over frames with unknown sources.
    151          actor: getSavedFrameSource(this.threadActor, this.frame).actorID,
    152          line: this.frame.line,
    153          // SavedFrame objects have a 1-based column number, but this API and
    154          // Debugger API objects use a 0-based column value.
    155          column: this.frame.column - 1,
    156        },
    157        oldest: !getSavedFrameParent(this.threadActor, this.frame),
    158      };
    159 
    160      return obj;
    161    }
    162 
    163    const threadActor = this.threadActor;
    164    const form = {
    165      actor: this.actorID,
    166      type: this.frame.type,
    167      asyncCause: this.frame.onStack ? null : "await",
    168      state: this.frame.onStack ? "on-stack" : "suspended",
    169    };
    170 
    171    if (this.depth) {
    172      form.depth = this.depth;
    173    }
    174 
    175    if (this.frame.type != "wasmcall") {
    176      form.this = threadActor.createValueGrip(this.frame.this);
    177    }
    178 
    179    form.displayName = formatDisplayName(this.frame);
    180    form.arguments = this._args();
    181 
    182    if (this.frame.script) {
    183      const location = this.threadActor.sourcesManager.getFrameLocation(
    184        this.frame
    185      );
    186      form.where = {
    187        actor: location.sourceActor.actorID,
    188        line: location.line,
    189        column: location.column,
    190      };
    191    }
    192 
    193    if (!this.frame.older) {
    194      form.oldest = true;
    195    }
    196 
    197    return form;
    198  }
    199 
    200  _args() {
    201    if (!this.frame.onStack || !this.frame.arguments) {
    202      return [];
    203    }
    204 
    205    return this.frame.arguments.map(arg =>
    206      this.threadActor.createValueGrip(arg)
    207    );
    208  }
    209 }
    210 
    211 exports.FrameActor = FrameActor;
    212 exports.formatDisplayName = formatDisplayName;
    213 exports.getSavedFrameParent = getSavedFrameParent;
    214 exports.isValidSavedFrame = isValidSavedFrame;