tor-browser

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

thread.js (6582B)


      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 { ThreadStateTypes } = require("resource://devtools/client/constants.js");
      8 const {
      9  FrontClassWithSpec,
     10  registerFront,
     11 } = require("resource://devtools/shared/protocol.js");
     12 
     13 const { threadSpec } = require("resource://devtools/shared/specs/thread.js");
     14 
     15 loader.lazyRequireGetter(
     16  this,
     17  "ObjectFront",
     18  "resource://devtools/client/fronts/object.js",
     19  true
     20 );
     21 loader.lazyRequireGetter(
     22  this,
     23  "FrameFront",
     24  "resource://devtools/client/fronts/frame.js"
     25 );
     26 loader.lazyRequireGetter(
     27  this,
     28  "SourceFront",
     29  "resource://devtools/client/fronts/source.js",
     30  true
     31 );
     32 
     33 /**
     34 * Creates a thread front for the remote debugging protocol server. This client
     35 * is a front to the thread actor created in the server side, hiding the
     36 * protocol details in a traditional JavaScript API.
     37 *
     38 * @param client DevToolsClient
     39 * @param actor string
     40 *        The actor ID for this thread.
     41 */
     42 class ThreadFront extends FrontClassWithSpec(threadSpec) {
     43  constructor(client, targetFront, parentFront) {
     44    super(client, targetFront, parentFront);
     45    this.client = client;
     46    this._threadGrips = {};
     47    // Note that this isn't matching ThreadActor state field.
     48    // ThreadFront is only using two values: paused or attached.
     49    this._state = "attached";
     50 
     51    this._beforePaused = this._beforePaused.bind(this);
     52    this._beforeResumed = this._beforeResumed.bind(this);
     53    this.before("paused", this._beforePaused);
     54    this.before("resumed", this._beforeResumed);
     55    this.targetFront.on("will-navigate", this._onWillNavigate.bind(this));
     56    // Attribute name from which to retrieve the actorID out of the target actor's form
     57    this.formAttributeName = "threadActor";
     58  }
     59 
     60  get state() {
     61    return this._state;
     62  }
     63 
     64  get paused() {
     65    return this._state === "paused";
     66  }
     67 
     68  get actor() {
     69    return this.actorID;
     70  }
     71 
     72  _assertPaused(command) {
     73    if (!this.paused) {
     74      throw Error(
     75        command + " command sent while not paused. Currently " + this._state
     76      );
     77    }
     78  }
     79 
     80  getFrames(start, count) {
     81    return super.frames(start, count);
     82  }
     83 
     84  /**
     85   * Resume a paused thread. If the optional limit parameter is present, then
     86   * the thread will also pause when that limit is reached.
     87   *
     88   * @param [optional] object limit
     89   *        An object with a type property set to the appropriate limit (next,
     90   *        step, or finish) per the remote debugging protocol specification.
     91   *        Use null to specify no limit.
     92   */
     93  async _doResume(resumeLimit, frameActorID) {
     94    this._assertPaused("resume");
     95 
     96    // Put the client in a tentative "resuming" state so we can prevent
     97    // further requests that should only be sent in the paused state.
     98    this._previousState = this._state;
     99    this._state = "resuming";
    100    try {
    101      await super.resume(resumeLimit, frameActorID);
    102    } catch (e) {
    103      if (this._state == "resuming") {
    104        // There was an error resuming, update the state to the new one
    105        // reported by the server, if given (only on wrongState), otherwise
    106        // reset back to the previous state.
    107        if (e.state) {
    108          this._state = ThreadStateTypes[e.state];
    109        } else {
    110          this._state = this._previousState;
    111        }
    112      }
    113    }
    114 
    115    delete this._previousState;
    116  }
    117 
    118  /**
    119   * Resume a paused thread.
    120   */
    121  resume() {
    122    return this._doResume(null);
    123  }
    124 
    125  /**
    126   * Resume then pause without stepping.
    127   *
    128   */
    129  resumeThenPause() {
    130    return this._doResume({ type: "break" });
    131  }
    132 
    133  /**
    134   * Step over a function call.
    135   */
    136  stepOver(frameActorID) {
    137    return this._doResume({ type: "next" }, frameActorID);
    138  }
    139 
    140  /**
    141   * Step into a function call.
    142   */
    143  stepIn(frameActorID) {
    144    return this._doResume({ type: "step" }, frameActorID);
    145  }
    146 
    147  /**
    148   * Step out of a function call.
    149   */
    150  stepOut(frameActorID) {
    151    return this._doResume({ type: "finish" }, frameActorID);
    152  }
    153 
    154  /**
    155   * Restart selected frame.
    156   */
    157  restart(frameActorID) {
    158    return this._doResume({ type: "restart" }, frameActorID);
    159  }
    160 
    161  /**
    162   * Immediately interrupt a running thread.
    163   */
    164  interrupt() {
    165    return this._doInterrupt(null);
    166  }
    167 
    168  /**
    169   * Pause execution right before the next JavaScript bytecode is executed.
    170   */
    171  breakOnNext() {
    172    return this._doInterrupt("onNext");
    173  }
    174 
    175  /**
    176   * Interrupt a running thread.
    177   */
    178  _doInterrupt(when) {
    179    return super.interrupt(when);
    180  }
    181 
    182  /**
    183   * Request the loaded sources for the current thread.
    184   */
    185  async getSources() {
    186    let sources = [];
    187    try {
    188      sources = await super.sources();
    189    } catch (e) {
    190      // we may have closed the connection
    191      console.log(`getSources failed. Connection may have closed: ${e}`);
    192    }
    193    return { sources };
    194  }
    195 
    196  /**
    197   * This is only used by tests, which should be migrated to DevToolsClient.createObjectFront
    198   */
    199  pauseGrip(grip) {
    200    return new ObjectFront(this.conn, this.targetFront, this, grip);
    201  }
    202 
    203  /**
    204   * Clear and invalidate all the grip fronts from the given cache.
    205   *
    206   * @param gripCacheName
    207   *        The property name of the grip cache we want to clear.
    208   */
    209  _clearObjectFronts(gripCacheName) {
    210    for (const id in this[gripCacheName]) {
    211      this[gripCacheName][id].valid = false;
    212    }
    213    this[gripCacheName] = {};
    214  }
    215 
    216  _beforePaused(packet) {
    217    this._state = "paused";
    218    this._onThreadState(packet);
    219  }
    220 
    221  _beforeResumed() {
    222    this._state = "attached";
    223    this._onThreadState(null);
    224    this.unmanageChildren(FrameFront);
    225  }
    226 
    227  _onWillNavigate() {
    228    this.unmanageChildren(SourceFront);
    229  }
    230 
    231  /**
    232   * Handle thread state change by doing necessary cleanup
    233   */
    234  _onThreadState(packet) {
    235    // The debugger UI may not be initialized yet so we want to keep
    236    // the packet around so it knows what to pause state to display
    237    // when it's initialized
    238    this._lastPausePacket = packet;
    239  }
    240 
    241  getLastPausePacket() {
    242    return this._lastPausePacket;
    243  }
    244 
    245  /**
    246   * Return an instance of SourceFront for the given source actor form.
    247   */
    248  source(form) {
    249    if (form.actor in this._threadGrips) {
    250      return this._threadGrips[form.actor];
    251    }
    252 
    253    const sourceFront = new SourceFront(this.client, form);
    254    this.manage(sourceFront);
    255    this._threadGrips[form.actor] = sourceFront;
    256    return sourceFront;
    257  }
    258 }
    259 
    260 exports.ThreadFront = ThreadFront;
    261 registerFront(ThreadFront);