tor-browser

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

thread-states.js (5278B)


      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 Targets = require("resource://devtools/server/actors/targets/index.js");
      8 
      9 const {
     10  PAUSE_REASONS,
     11  STATES: THREAD_STATES,
     12 } = require("resource://devtools/server/actors/thread.js");
     13 
     14 // Possible values of breakpoint's resource's `state` attribute
     15 const STATES = {
     16  PAUSED: "paused",
     17  RESUMED: "resumed",
     18 };
     19 
     20 /**
     21 * Emit THREAD_STATE resources, which is emitted each time the target's thread pauses or resumes.
     22 * So that there is two distinct values for this resource: pauses and resumes.
     23 * These values are distinguished by `state` attribute which can be either "paused" or "resumed".
     24 *
     25 * Resume events, won't expose any other attribute other than `resourceType` and `state`.
     26 *
     27 * Pause events will expose the following attributes:
     28 * - why {Object}: Description of why the thread pauses. See ThreadActor's PAUSE_REASONS definition for more information.
     29 * - frame {Object}: Description of the frame where we just paused. This is a FrameActor's form.
     30 */
     31 class BreakpointWatcher {
     32  constructor() {
     33    this.onPaused = this.onPaused.bind(this);
     34    this.onResumed = this.onResumed.bind(this);
     35  }
     36 
     37  /**
     38   * Start watching for state changes of the thread actor.
     39   * This will notify whenever the thread actor pause and resume.
     40   *
     41   * @param TargetActor targetActor
     42   *        The target actor from which we should observe breakpoints
     43   * @param Object options
     44   *        Dictionary object with following attributes:
     45   *        - onAvailable: mandatory function
     46   *          This will be called for each resource.
     47   */
     48  async watch(targetActor, { onAvailable }) {
     49    // The Browser Toolbox uses the Content Process target's Thread actor to debug all scripts
     50    // running into a given process. This includes WindowGlobal scripts.
     51    // Because of this, and in such configuration, we have to ignore the WindowGlobal targets.
     52    if (
     53      targetActor.sessionContext.type == "all" &&
     54      !targetActor.sessionContext.enableWindowGlobalThreadActors &&
     55      targetActor.targetType === Targets.TYPES.FRAME &&
     56      targetActor.typeName != "parentProcessTarget"
     57    ) {
     58      return;
     59    }
     60 
     61    const { threadActor } = targetActor;
     62    this.threadActor = threadActor;
     63    this.onAvailable = onAvailable;
     64 
     65    // If this watcher is created during target creation, attach the thread actor automatically.
     66    // Otherwise it would not pause on anything (especially debugger statements).
     67    // However, do not attach the thread actor for Workers. They use a codepath
     68    // which releases the worker on `attach`. For them, the client will call `attach`. (bug 1691986)
     69    const isTargetCreation = this.threadActor.state == THREAD_STATES.DETACHED;
     70    if (isTargetCreation && !targetActor.targetType.endsWith("worker")) {
     71      await this.threadActor.attach({});
     72    }
     73 
     74    this.isInterrupted = false;
     75 
     76    threadActor.on("paused", this.onPaused);
     77    threadActor.on("resumed", this.onResumed);
     78 
     79    // For top-level targets, the thread actor may have been attached by the frontend
     80    // on toolbox opening, and we start observing for thread state updates much later.
     81    // In which case, the thread actor may already be paused and we handle this here.
     82    // It will also occurs for all other targets once bug 1681698 lands,
     83    // as the thread actor will be initialized before the target starts loading.
     84    // And it will occur for all targets once bug 1686748 lands.
     85    //
     86    // Note that we have to check if we have a "lastPausedPacket",
     87    // because the thread Actor is immediately set as being paused,
     88    // but the pause packet is built asynchronously and available slightly later.
     89    // If the "lastPausedPacket" is null, while the thread actor is paused,
     90    // it is fine to ignore as the "paused" event will be fire later.
     91    if (threadActor.isPaused() && threadActor.lastPausedPacket()) {
     92      this.onPaused(threadActor.lastPausedPacket());
     93    }
     94  }
     95 
     96  /**
     97   * Stop watching for breakpoints
     98   */
     99  destroy() {
    100    if (!this.threadActor) {
    101      return;
    102    }
    103    this.threadActor.off("paused", this.onPaused);
    104    this.threadActor.off("resumed", this.onResumed);
    105  }
    106 
    107  onPaused(packet) {
    108    // If paused by an explicit interrupt, which are generated by the
    109    // slow script dialog and internal events such as setting
    110    // breakpoints, ignore the event.
    111    const { why } = packet;
    112    if (why.type === PAUSE_REASONS.INTERRUPTED && !why.onNext) {
    113      this.isInterrupted = true;
    114      return;
    115    }
    116 
    117    // Ignore attached events because they are not useful to the user.
    118    if (why.type == PAUSE_REASONS.ALREADY_PAUSED) {
    119      return;
    120    }
    121 
    122    this.onAvailable([
    123      {
    124        state: STATES.PAUSED,
    125        why,
    126        frame: packet.frame.form(),
    127      },
    128    ]);
    129  }
    130 
    131  onResumed() {
    132    // NOTE: resumed events are suppressed while interrupted
    133    // to prevent unintentional behavior.
    134    if (this.isInterrupted) {
    135      this.isInterrupted = false;
    136      return;
    137    }
    138 
    139    this.onAvailable([
    140      {
    141        state: STATES.RESUMED,
    142      },
    143    ]);
    144  }
    145 }
    146 
    147 module.exports = BreakpointWatcher;