summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/index.js
blob: a9a35bedf75ca90c607d97c99c4284cabec507d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// List of all command modules
// (please try to keep the list alphabetically sorted)
/* eslint sort-keys: "error" */
/* eslint-enable sort-keys */
const Commands = {
  inspectedWindowCommand:
    "devtools/shared/commands/inspected-window/inspected-window-command",
  inspectorCommand: "devtools/shared/commands/inspector/inspector-command",
  networkCommand: "devtools/shared/commands/network/network-command",
  objectCommand: "devtools/shared/commands/object/object-command",
  resourceCommand: "devtools/shared/commands/resource/resource-command",
  rootResourceCommand:
    "devtools/shared/commands/root-resource/root-resource-command",
  scriptCommand: "devtools/shared/commands/script/script-command",
  targetCommand: "devtools/shared/commands/target/target-command",
  targetConfigurationCommand:
    "devtools/shared/commands/target-configuration/target-configuration-command",
  threadConfigurationCommand:
    "devtools/shared/commands/thread-configuration/thread-configuration-command",
  tracerCommand: "devtools/shared/commands/tracer/tracer-command",
};
/* eslint-disable sort-keys */

/**
 * For a given descriptor and its related Targets, already initialized,
 * return the dictionary with all command instances.
 * This dictionary is lazy and commands will be loaded and instanciated on-demand.
 */
async function createCommandsDictionary(descriptorFront) {
  // Bug 1675763: Watcher actor is not available in all situations yet.
  let watcherFront;
  const supportsWatcher = descriptorFront.traits?.watcher;
  if (supportsWatcher) {
    watcherFront = await descriptorFront.getWatcher();
  }
  const { client } = descriptorFront;

  const allInstantiatedCommands = new Set();

  const dictionary = {
    // Expose both client and descriptor for legacy codebases, or tests.
    // But ideally only commands should interact with these two objects
    client,
    descriptorFront,
    watcherFront,

    // Expose for tests
    waitForRequestsToSettle() {
      return descriptorFront.client.waitForRequestsToSettle();
    },

    // Boolean flag to know if the DevtoolsClient should be closed
    // when this commands happens to be destroyed.
    // This is set by:
    // * commands-from-url in case we are opening a toolbox
    //   with a dedicated DevToolsClient (mostly from about:debugging, when the client isn't "cached").
    // * CommandsFactory, when we are connecting to a local tab and expect
    //   the client, toolbox and descriptor to all follow the same lifecycle.
    shouldCloseClient: true,

    /**
     * Destroy the commands which will destroy:
     * - all inner commands,
     * - the related descriptor,
     * - the related DevToolsClient (not always)
     */
    async destroy() {
      descriptorFront.off("descriptor-destroyed", this.destroy);

      // Destroy all inner command modules
      for (const command of allInstantiatedCommands) {
        if (typeof command.destroy == "function") {
          command.destroy();
        }
      }
      allInstantiatedCommands.clear();

      // Destroy the descriptor front, and all its children fronts.
      // Watcher, targets,...
      //
      // Note that DescriptorFront.destroy will be null because of Pool.destroy
      // when this function is called while the descriptor front itself is being
      // destroyed.
      if (!descriptorFront.isDestroyed()) {
        await descriptorFront.destroy();
      }

      // Close the DevToolsClient. Shutting down the connection
      // to the debuggable context and its DevToolsServer.
      //
      // See shouldCloseClient jsdoc about this condition.
      if (this.shouldCloseClient) {
        await client.close();
      }
    },
  };
  dictionary.destroy = dictionary.destroy.bind(dictionary);

  // Automatically destroy the commands object if the descriptor
  // happens to be destroyed. Which means that the debuggable context
  // is no longer debuggable.
  descriptorFront.on("descriptor-destroyed", dictionary.destroy);

  for (const name in Commands) {
    loader.lazyGetter(dictionary, name, () => {
      const Constructor = require(Commands[name]);
      const command = new Constructor({
        // Commands can use other commands
        commands: dictionary,

        // The context to inspect identified by this descriptor
        descriptorFront,

        // The front for the Watcher Actor, related to the given descriptor
        // This is a key actor to watch for targets and resources and pull global actors running in the parent process
        watcherFront,

        // From here, we could pass DevToolsClient, or any useful protocol classes...
        // so that we abstract where and how to fetch all necessary interfaces
        // and avoid having to know that you might pull the client via descriptorFront.client
      });
      allInstantiatedCommands.add(command);
      return command;
    });
  }

  return dictionary;
}
exports.createCommandsDictionary = createCommandsDictionary;