Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / devtools / shared / commands / commands-factory.js
blob257f50ce2faf379851c02b372a84adf3de88fb79
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/. */
5 "use strict";
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"
13 loader.lazyRequireGetter(
14 this,
15 "DevToolsServer",
16 "resource://devtools/server/devtools-server.js",
17 true
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
27 /**
28 * Functions for creating Commands for all debuggable contexts.
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.
34 exports.CommandsFactory = {
35 /**
36 * Create commands for a given local tab.
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
46 async forTab(tab, { client, isWebExtension } = {}) {
47 if (!client) {
48 client = await createLocalClient();
51 const descriptor = await client.mainRoot.getTab({ tab, isWebExtension });
52 descriptor.doNotAttachThreadActor = isWebExtension;
53 const commands = await createCommandsDictionary(descriptor);
54 return commands;
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.
62 async forCurrentTabInChromeMochitest() {
63 const client = await createLocalClient();
64 const descriptor = await client.mainRoot.getTab();
65 const commands = await createCommandsDictionary(descriptor);
66 return commands;
69 /**
70 * Create commands for the main process.
72 * @param {Object} options
73 * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
74 * a new one will be created.
75 * @returns {Object} Commands
77 async forMainProcess({ client } = {}) {
78 if (!client) {
79 client = await createLocalClient();
82 const descriptor = await client.mainRoot.getMainProcess();
83 const commands = await createCommandsDictionary(descriptor);
84 return commands;
87 /**
88 * Create commands for a given remote tab.
90 * Note that it can also be used for local tab, but isLocalTab attribute
91 * on commands.descriptorFront will be false.
93 * @param {Number} browserId: Identify which tab we should create commands for.
94 * @param {Object} options
95 * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
96 * a new one will be created.
97 * @returns {Object} Commands
99 async forRemoteTab(browserId, { client } = {}) {
100 if (!client) {
101 client = await createLocalClient();
104 const descriptor = await client.mainRoot.getTab({ browserId });
105 const commands = await createCommandsDictionary(descriptor);
106 return commands;
110 * Create commands for a given main process worker.
112 * @param {String} id: WorkerDebugger's id, which is a unique ID computed by the platform code.
113 * These ids are exposed via WorkerDescriptor's id attributes.
114 * WorkerDescriptors can be retrieved via MainFront.listAllWorkers()/listWorkers().
115 * @param {Object} options
116 * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
117 * a new one will be created.
118 * @returns {Object} Commands
120 async forWorker(id, { client } = {}) {
121 if (!client) {
122 client = await createLocalClient();
125 const descriptor = await client.mainRoot.getWorker(id);
126 const commands = await createCommandsDictionary(descriptor);
127 return commands;
131 * Create commands for a Web Extension.
133 * @param {String} id The Web Extension ID to debug.
134 * @param {Object} options
135 * @param {DevToolsClient} options.client: An optional DevToolsClient. If none is passed,
136 * a new one will be created.
137 * @returns {Object} Commands
139 async forAddon(id, { client } = {}) {
140 if (!client) {
141 client = await createLocalClient();
144 const descriptor = await client.mainRoot.getAddon({ id });
145 const commands = await createCommandsDictionary(descriptor);
146 return commands;
150 * This method will spawn a special `DevToolsClient`
151 * which is meant to debug the same Firefox instance
152 * and especially be able to debug chrome code.
153 * The chrome code typically runs in the system principal.
154 * This principal is a singleton which is shared among most Firefox internal codebase
155 * (JSM, privileged html documents, JS-XPCOM,...)
156 * In order to be able to debug these script we need to connect to a special DevToolsServer
157 * that runs in a dedicated and distinct system principal which is different from
158 * the one shared with the rest of Firefox frontend codebase.
160 async spawnClientToDebugSystemPrincipal() {
161 // The Browser console ends up using the debugger in autocomplete.
162 // Because the debugger can't be running in the same compartment than its debuggee,
163 // we have to load the server in a dedicated Loader, flagged with
164 // `freshCompartment`, which will force it to be loaded in another compartment.
165 // We aren't using `invisibleToDebugger` in order to allow the Browser toolbox to
166 // debug the Browser console. This is fine as they will spawn distinct Loaders and
167 // so distinct `DevToolsServer` and actor modules.
168 const customLoader = new DevToolsLoader({
169 freshCompartment: true,
171 const { DevToolsServer: customDevToolsServer } = customLoader.require(
172 "resource://devtools/server/devtools-server.js"
175 customDevToolsServer.init();
177 // We want all the actors (root, browser and target-scoped) to be registered on the
178 // DevToolsServer. This is needed so the Browser Console can retrieve:
179 // - the console actors, which are target-scoped (See Bug 1416105)
180 // - the screenshotActor, which is browser-scoped (for the `:screenshot` command)
181 customDevToolsServer.registerAllActors();
183 customDevToolsServer.allowChromeProcess = true;
185 const client = new DevToolsClient(customDevToolsServer.connectPipe());
186 await client.connect();
188 return client;
192 * One method to handle the whole setup sequence to connect to RDP backend for the Browser Console.
194 * This will instantiate a special DevTools module loader for the DevToolsServer.
195 * Then spawn a DevToolsClient to connect to it.
196 * Get a Main Process Descriptor from it.
197 * Finally spawn a commands object for this descriptor.
199 async forBrowserConsole() {
200 // The Browser console ends up using the debugger in autocomplete.
201 // Because the debugger can't be running in the same compartment than its debuggee,
202 // we have to load the server in a dedicated Loader and so spawn a special client
203 const client = await this.spawnClientToDebugSystemPrincipal();
205 const descriptor = await client.mainRoot.getMainProcess();
207 descriptor.doNotAttachThreadActor = true;
209 // Force fetching the first top level target right away.
210 await descriptor.getTarget();
212 const commands = await createCommandsDictionary(descriptor);
213 return commands;
217 async function createLocalClient() {
218 // Make sure the DevTools server is started.
219 ensureDevToolsServerInitialized();
221 // Create the client and connect it to the local server.
222 const client = new DevToolsClient(DevToolsServer.connectPipe());
223 await client.connect();
225 return client;
227 // Also expose this method for tests which would like to create a client
228 // without involving commands. This would typically be tests against the Watcher actor
229 // and requires to prevent having TargetCommand from running.
230 // Or tests which are covering RootFront or global actor's fronts.
231 exports.createLocalClientForTests = createLocalClient;
233 function ensureDevToolsServerInitialized() {
234 // Since a remote protocol connection will be made, let's start the
235 // DevToolsServer here, once and for all tools.
236 DevToolsServer.init();
238 // Enable all the actors. We may not need all of them and registering
239 // only root and target might be enough
240 DevToolsServer.registerAllActors();
242 // Enable being able to get child process actors
243 // Same, this might not be useful
244 DevToolsServer.allowChromeProcess = true;