tor-browser

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

process.js (7985B)


      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 /*
      8 * Represents any process running in Firefox.
      9 * This can be:
     10 *  - the parent process, where all top level chrome window runs:
     11 *    like browser.xhtml, sidebars, devtools iframes, the browser console, ...
     12 *  - any content process
     13 *
     14 * There is some special cases in the class around:
     15 *  - xpcshell, where there is only one process which doesn't expose any DOM document
     16 *    And instead of exposing a ParentProcessTargetActor, getTarget will return
     17 *    a ContentProcessTargetActor.
     18 *  - background task, similarly to xpcshell, they don't expose any DOM document
     19 *    and this also works with a ContentProcessTargetActor.
     20 *
     21 * See devtools/docs/backend/actor-hierarchy.md for more details.
     22 */
     23 
     24 const { Actor } = require("resource://devtools/shared/protocol.js");
     25 const {
     26  processDescriptorSpec,
     27 } = require("resource://devtools/shared/specs/descriptors/process.js");
     28 
     29 const {
     30  DevToolsServer,
     31 } = require("resource://devtools/server/devtools-server.js");
     32 
     33 const {
     34  createBrowserSessionContext,
     35  createContentProcessSessionContext,
     36 } = require("resource://devtools/server/actors/watcher/session-context.js");
     37 
     38 loader.lazyRequireGetter(
     39  this,
     40  "ContentProcessTargetActor",
     41  "resource://devtools/server/actors/targets/content-process.js",
     42  true
     43 );
     44 loader.lazyRequireGetter(
     45  this,
     46  "ParentProcessTargetActor",
     47  "resource://devtools/server/actors/targets/parent-process.js",
     48  true
     49 );
     50 loader.lazyRequireGetter(
     51  this,
     52  "connectToContentProcess",
     53  "resource://devtools/server/connectors/content-process-connector.js",
     54  true
     55 );
     56 loader.lazyRequireGetter(
     57  this,
     58  "WatcherActor",
     59  "resource://devtools/server/actors/watcher.js",
     60  true
     61 );
     62 
     63 class ProcessDescriptorActor extends Actor {
     64  constructor(connection, options = {}) {
     65    super(connection, processDescriptorSpec);
     66 
     67    if ("id" in options && typeof options.id != "number") {
     68      throw Error("process connect requires a valid `id` attribute.");
     69    }
     70 
     71    this.id = options.id;
     72    this._windowGlobalTargetActor = null;
     73    this.isParent = options.parent;
     74    this.destroy = this.destroy.bind(this);
     75  }
     76 
     77  get browsingContextID() {
     78    if (this._windowGlobalTargetActor) {
     79      return this._windowGlobalTargetActor.docShell.browsingContext.id;
     80    }
     81    return null;
     82  }
     83 
     84  get isWindowlessParent() {
     85    return this.isParent && (this.isXpcshell || this.isBackgroundTaskMode);
     86  }
     87 
     88  get isXpcshell() {
     89    return Services.env.exists("XPCSHELL_TEST_PROFILE_DIR");
     90  }
     91 
     92  get isBackgroundTaskMode() {
     93    const bts = Cc["@mozilla.org/backgroundtasks;1"]?.getService(
     94      Ci.nsIBackgroundTasks
     95    );
     96    return bts && bts.isBackgroundTaskMode;
     97  }
     98 
     99  _parentProcessConnect() {
    100    let targetActor;
    101    if (this.isWindowlessParent) {
    102      // Check if we are running on xpcshell or in background task mode.
    103      // In these modes, there is no valid browsing context to attach to
    104      // and so ParentProcessTargetActor doesn't make sense as it inherits from
    105      // WindowGlobalTargetActor. So instead use ContentProcessTargetActor, which
    106      // matches the needs of these modes.
    107      targetActor = new ContentProcessTargetActor(this.conn, {
    108        isXpcShellTarget: true,
    109        sessionContext: createContentProcessSessionContext(),
    110      });
    111    } else {
    112      // Create the target actor for the parent process, which is in the same process
    113      // as this target. Because we are in the same process, we have a true actor that
    114      // should be managed by the ProcessDescriptorActor.
    115      targetActor = new ParentProcessTargetActor(this.conn, {
    116        // This target actor is special and will stay alive as long
    117        // as the toolbox/client is alive. It is the original top level target for
    118        // the BrowserToolbox and isTopLevelTarget should always be true here.
    119        // (It isn't the typical behavior of WindowGlobalTargetActor's base class)
    120        isTopLevelTarget: true,
    121        sessionContext: createBrowserSessionContext(),
    122      });
    123      // this is a special field that only parent process with a browsing context
    124      // have, as they are the only processes at the moment that have child
    125      // browsing contexts
    126      this._windowGlobalTargetActor = targetActor;
    127    }
    128    this.manage(targetActor);
    129    // to be consistent with the return value of the _childProcessConnect, we are returning
    130    // the form here. This might be memoized in the future
    131    return targetActor.form();
    132  }
    133 
    134  /**
    135   * Connect to a remote process actor, always a ContentProcess target.
    136   */
    137  async _childProcessConnect() {
    138    const { id } = this;
    139    const mm = this._lookupMessageManager(id);
    140    if (!mm) {
    141      return {
    142        error: "noProcess",
    143        message: "There is no process with id '" + id + "'.",
    144      };
    145    }
    146    const childTargetForm = await connectToContentProcess(
    147      this.conn,
    148      mm,
    149      this.destroy
    150    );
    151    return childTargetForm;
    152  }
    153 
    154  _lookupMessageManager(id) {
    155    for (let i = 0; i < Services.ppmm.childCount; i++) {
    156      const mm = Services.ppmm.getChildAt(i);
    157 
    158      // A zero id is used for the parent process, instead of its actual pid.
    159      if (id ? mm.osPid == id : mm.isInProcess) {
    160        return mm;
    161      }
    162    }
    163    return null;
    164  }
    165 
    166  /**
    167   * Connect the a process actor.
    168   */
    169  async getTarget() {
    170    if (!DevToolsServer.allowChromeProcess) {
    171      return {
    172        error: "forbidden",
    173        message: "You are not allowed to debug processes.",
    174      };
    175    }
    176    if (this.isParent) {
    177      return this._parentProcessConnect();
    178    }
    179    // This is a remote process we are connecting to
    180    return this._childProcessConnect();
    181  }
    182 
    183  /**
    184   * Return a Watcher actor, allowing to keep track of targets which
    185   * already exists or will be created. It also helps knowing when they
    186   * are destroyed.
    187   */
    188  getWatcher({ enableWindowGlobalThreadActors = false } = {}) {
    189    if (!this.watcher) {
    190      this.watcher = new WatcherActor(
    191        this.conn,
    192        createBrowserSessionContext({
    193          enableWindowGlobalThreadActors,
    194        })
    195      );
    196      this.manage(this.watcher);
    197    }
    198    return this.watcher;
    199  }
    200 
    201  form() {
    202    return {
    203      actor: this.actorID,
    204      id: this.id,
    205      isParent: this.isParent,
    206      isWindowlessParent: this.isWindowlessParent,
    207      traits: {
    208        // Supports the Watcher actor. Can be removed as part of Bug 1680280.
    209        // Bug 1687461: WatcherActor only supports the parent process, where we debug everything.
    210        // For the "Browser Content Toolbox", where we debug only one content process,
    211        // we will still be using legacy listeners.
    212        watcher: this.isParent,
    213        // ParentProcessTargetActor can be reloaded.
    214        supportsReloadDescriptor: this.isParent && !this.isWindowlessParent,
    215      },
    216    };
    217  }
    218 
    219  async reloadDescriptor() {
    220    if (!this.isParent || this.isWindowlessParent) {
    221      throw new Error(
    222        "reloadDescriptor is only available for parent process descriptors"
    223      );
    224    }
    225 
    226    // Reload for the parent process will restart the whole browser
    227    //
    228    // This aims at replicate `DevelopmentHelpers.quickRestart`
    229    // This allows a user to do a full firefox restart + session restore
    230    // Via Ctrl+Alt+R on the Browser Console/Toolbox
    231 
    232    // Maximize the chance of fetching new source content by clearing the cache
    233    Services.obs.notifyObservers(null, "startupcache-invalidate");
    234 
    235    // Avoid safemode popup from appearing on restart
    236    Services.env.set("MOZ_DISABLE_SAFE_MODE_KEY", "1");
    237 
    238    Services.startup.quit(
    239      Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
    240    );
    241  }
    242 
    243  destroy() {
    244    this.emit("descriptor-destroyed");
    245 
    246    this._windowGlobalTargetActor = null;
    247    super.destroy();
    248  }
    249 }
    250 
    251 exports.ProcessDescriptorActor = ProcessDescriptorActor;