tor-browser

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

commands-factory.js (8999B)


      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  createCommandsDictionary,
      9 } = require("resource://devtools/shared/commands/index.js");
     10 const { DevToolsLoader } = ChromeUtils.importESModule(
     11  "resource://devtools/shared/loader/Loader.sys.mjs"
     12 );
     13 loader.lazyRequireGetter(
     14  this,
     15  "DevToolsServer",
     16  "resource://devtools/server/devtools-server.js",
     17  true
     18 );
     19 // eslint-disable-next-line mozilla/reject-some-requires
     20 loader.lazyRequireGetter(
     21  this,
     22  "DevToolsClient",
     23  "resource://devtools/client/devtools-client.js",
     24  true
     25 );
     26 
     27 /**
     28 * Functions for creating Commands for all debuggable contexts.
     29 *
     30 * All methods of this `CommandsFactory` object receive argument to describe to
     31 * which particular context we want to debug. And all returns a new instance of `commands` object.
     32 * Commands are implemented by modules defined in devtools/shared/commands.
     33 */
     34 exports.CommandsFactory = {
     35  /**
     36   * Create commands for a given local tab.
     37   *
     38   * @param {Tab} tab: A local Firefox tab, running in this process.
     39   * @param {object} options
     40   * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
     41   *        a new one will be created.
     42   * @param {DevToolsClient} options.isWebExtension: An optional boolean to flag commands
     43   *        that are created for the WebExtension codebase.
     44   * @returns {object} Commands
     45   */
     46  async forTab(tab, { client, isWebExtension } = {}) {
     47    if (!client) {
     48      client = await createLocalClient();
     49    }
     50 
     51    const descriptor = await client.mainRoot.getTab({ tab, isWebExtension });
     52    descriptor.doNotAttachThreadActor = isWebExtension;
     53    const commands = await createCommandsDictionary(descriptor);
     54    return commands;
     55  },
     56 
     57  /**
     58   * Chrome mochitest don't have access to any "tab",
     59   * so that the only way to attach to a fake tab is call RootFront.getTab
     60   * without any argument.
     61   */
     62  async forCurrentTabInChromeMochitest() {
     63    const client = await createLocalClient();
     64    const descriptor = await client.mainRoot.getTab();
     65    const commands = await createCommandsDictionary(descriptor);
     66    return commands;
     67  },
     68 
     69  /**
     70   * Create commands for the main process.
     71   *
     72   * @param {object} options
     73   * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
     74   *        a new one will be created.
     75   * @param {boolean} enableWindowGlobalThreadActors: An optional boolean for on test.
     76   * @returns {object} Commands
     77   */
     78  async forMainProcess({
     79    client,
     80    enableWindowGlobalThreadActors = false,
     81  } = {}) {
     82    if (!client) {
     83      client = await createLocalClient();
     84    }
     85 
     86    const descriptor = await client.mainRoot.getMainProcess();
     87    const commands = await createCommandsDictionary(
     88      descriptor,
     89      enableWindowGlobalThreadActors
     90    );
     91    return commands;
     92  },
     93 
     94  /**
     95   * Create commands for a given remote tab.
     96   *
     97   * Note that it can also be used for local tab, but isLocalTab attribute
     98   * on commands.descriptorFront will be false.
     99   *
    100   * @param {number} browserId: Identify which tab we should create commands for.
    101   * @param {object} options
    102   * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
    103   *        a new one will be created.
    104   * @returns {object} Commands
    105   */
    106  async forRemoteTab(browserId, { client } = {}) {
    107    if (!client) {
    108      client = await createLocalClient();
    109    }
    110 
    111    const descriptor = await client.mainRoot.getTab({ browserId });
    112    const commands = await createCommandsDictionary(descriptor);
    113    return commands;
    114  },
    115 
    116  /**
    117   * Create commands for a given main process worker.
    118   *
    119   * @param {string} id: WorkerDebugger's id, which is a unique ID computed by the platform code.
    120   *        These ids are exposed via WorkerDescriptor's id attributes.
    121   *        WorkerDescriptors can be retrieved via MainFront.listAllWorkers()/listWorkers().
    122   * @param {object} options
    123   * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
    124   *        a new one will be created.
    125   * @returns {object} Commands
    126   */
    127  async forWorker(id, { client } = {}) {
    128    if (!client) {
    129      client = await createLocalClient();
    130    }
    131 
    132    const descriptor = await client.mainRoot.getWorker(id);
    133    const commands = await createCommandsDictionary(descriptor);
    134    return commands;
    135  },
    136 
    137  /**
    138   * Create commands for a Web Extension.
    139   *
    140   * @param {string} id The Web Extension ID to debug.
    141   * @param {object} options
    142   * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
    143   *        a new one will be created.
    144   * @returns {object} Commands
    145   */
    146  async forAddon(id, { client } = {}) {
    147    if (!client) {
    148      client = await createLocalClient();
    149    }
    150 
    151    const descriptor = await client.mainRoot.getAddon({ id });
    152    const commands = await createCommandsDictionary(descriptor);
    153    return commands;
    154  },
    155 
    156  /**
    157   * This method will spawn a special `DevToolsClient`
    158   * which is meant to debug the same Firefox instance
    159   * and especially be able to debug chrome code.
    160   * The chrome code typically runs in the system principal.
    161   * This principal is a singleton which is shared among most Firefox internal codebase
    162   * (JSM, privileged html documents, JS-XPCOM,...)
    163   * In order to be able to debug these script we need to connect to a special DevToolsServer
    164   * that runs in a dedicated and distinct system principal which is different from
    165   * the one shared with the rest of Firefox frontend codebase.
    166   */
    167  async spawnClientToDebugSystemPrincipal() {
    168    // The Browser console ends up using the debugger in autocomplete.
    169    // Because the debugger can't be running in the same compartment than its debuggee,
    170    // we have to load the server in a dedicated Loader, flagged with
    171    // `freshCompartment`, which will force it to be loaded in another compartment.
    172    const customLoader = new DevToolsLoader({
    173      freshCompartment: true,
    174    });
    175    const { DevToolsServer: customDevToolsServer } = customLoader.require(
    176      "resource://devtools/server/devtools-server.js"
    177    );
    178 
    179    customDevToolsServer.init();
    180 
    181    // We want all the actors (root, browser and target-scoped) to be registered on the
    182    // DevToolsServer. This is needed so the Browser Console can retrieve:
    183    // - the console actors, which are target-scoped (See Bug 1416105)
    184    // - the screenshotActor, which is browser-scoped (for the `:screenshot` command)
    185    customDevToolsServer.registerAllActors();
    186 
    187    customDevToolsServer.allowChromeProcess = true;
    188 
    189    const client = new DevToolsClient(customDevToolsServer.connectPipe());
    190    await client.connect();
    191 
    192    return client;
    193  },
    194 
    195  /**
    196   * One method to handle the whole setup sequence to connect to RDP backend for the Browser Console.
    197   *
    198   * This will instantiate a special DevTools module loader for the DevToolsServer.
    199   * Then spawn a DevToolsClient to connect to it.
    200   * Get a Main Process Descriptor from it.
    201   * Finally spawn a commands object for this descriptor.
    202   */
    203  async forBrowserConsole() {
    204    // The Browser console ends up using the debugger in autocomplete.
    205    // Because the debugger can't be running in the same compartment than its debuggee,
    206    // we have to load the server in a dedicated Loader and so spawn a special client
    207    const client = await this.spawnClientToDebugSystemPrincipal();
    208 
    209    const descriptor = await client.mainRoot.getMainProcess();
    210 
    211    descriptor.doNotAttachThreadActor = true;
    212 
    213    // Force fetching the first top level target right away.
    214    await descriptor.getTarget();
    215 
    216    const commands = await createCommandsDictionary(descriptor);
    217    return commands;
    218  },
    219 };
    220 
    221 async function createLocalClient() {
    222  // Make sure the DevTools server is started.
    223  ensureDevToolsServerInitialized();
    224 
    225  // Create the client and connect it to the local server.
    226  const client = new DevToolsClient(DevToolsServer.connectPipe());
    227  await client.connect();
    228 
    229  return client;
    230 }
    231 // Also expose this method for tests which would like to create a client
    232 // without involving commands. This would typically be tests against the Watcher actor
    233 // and requires to prevent having TargetCommand from running.
    234 // Or tests which are covering RootFront or global actor's fronts.
    235 exports.createLocalClientForTests = createLocalClient;
    236 
    237 function ensureDevToolsServerInitialized() {
    238  // Since a remote protocol connection will be made, let's start the
    239  // DevToolsServer here, once and for all tools.
    240  DevToolsServer.init();
    241 
    242  // Enable all the actors. We may not need all of them and registering
    243  // only root and target might be enough
    244  DevToolsServer.registerAllActors();
    245 
    246  // Enable being able to get child process actors
    247  // Same, this might not be useful
    248  DevToolsServer.allowChromeProcess = true;
    249 }