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/. */
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(
16 "resource://devtools/server/devtools-server.js",
19 // eslint-disable-next-line mozilla/reject-some-requires
20 loader
.lazyRequireGetter(
23 "resource://devtools/client/devtools-client.js",
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
= {
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
} = {}) {
48 client
= await
createLocalClient();
51 const descriptor
= await client
.mainRoot
.getTab({ tab
, isWebExtension
});
52 descriptor
.doNotAttachThreadActor
= isWebExtension
;
53 const commands
= await
createCommandsDictionary(descriptor
);
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
);
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
} = {}) {
79 client
= await
createLocalClient();
82 const descriptor
= await client
.mainRoot
.getMainProcess();
83 const commands
= await
createCommandsDictionary(descriptor
);
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
} = {}) {
101 client
= await
createLocalClient();
104 const descriptor
= await client
.mainRoot
.getTab({ browserId
});
105 const commands
= await
createCommandsDictionary(descriptor
);
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
} = {}) {
122 client
= await
createLocalClient();
125 const descriptor
= await client
.mainRoot
.getWorker(id
);
126 const commands
= await
createCommandsDictionary(descriptor
);
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
} = {}) {
141 client
= await
createLocalClient();
144 const descriptor
= await client
.mainRoot
.getAddon({ id
});
145 const commands
= await
createCommandsDictionary(descriptor
);
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();
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
);
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();
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;