/* 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"; const { createCommandsDictionary } = require("devtools/shared/commands/index"); const ChromeUtils = require("ChromeUtils"); const { DevToolsLoader } = ChromeUtils.import( "resource://devtools/shared/Loader.jsm" ); loader.lazyRequireGetter( this, "DevToolsServer", "devtools/server/devtools-server", true ); // eslint-disable-next-line mozilla/reject-some-requires loader.lazyRequireGetter( this, "DevToolsClient", "devtools/client/devtools-client", true ); /** * Functions for creating Commands for all debuggable contexts. * * All methods of this `CommandsFactory` object receive argument to describe to * which particular context we want to debug. And all returns a new instance of `commands` object. * Commands are implemented by modules defined in devtools/shared/commands. */ exports.CommandsFactory = { async forTab(tab) { const client = await createLocalClient(); const descriptor = await client.mainRoot.getTab({ tab }); const commands = await createCommandsDictionary(descriptor); return commands; }, async forMainProcess() { const client = await createLocalClient(); const descriptor = await client.mainRoot.getMainProcess(); const commands = await createCommandsDictionary(descriptor); return commands; }, /** * For now, this method is only used by browser_target_list_various_descriptors.js * in order to cover about:debugging codepath, where we connect to remote tabs via * their current outerWindowID. * But: * 1) this can also be used to debug local tab, but TabDescriptor.localTab/isLocalTab will be null/false. * 2) beyond this test, this isn't used to connect to remote tab just yet. * Bug 1700909 should start using this from toolbox-init/descriptor-from-url * and will finaly be used to connect to remote tabs. */ async forRemoteTabInTest({ outerWindowID }) { const client = await createLocalClient(); const descriptor = await client.mainRoot.getTab({ outerWindowID }); const commands = await createCommandsDictionary(descriptor); return commands; }, /** * `id` is the WorkerDebugger's id, which is a unique ID computed by the platform code. * These ids are exposed via WorkerDescriptor's id attributes. * WorkerDescritpors can be retrieved via MainFront.listAllWorkers()/listWorkers(). */ async forWorker(id) { const client = await createLocalClient(); const descriptor = await client.mainRoot.getWorker(id); const commands = await createCommandsDictionary(descriptor); return commands; }, async forAddon(id) { const client = await createLocalClient(); const descriptor = await client.mainRoot.getAddon({ id }); const commands = await createCommandsDictionary(descriptor); return commands; }, async forProcess(osPid) { const client = await createLocalClient(); const descriptor = await client.mainRoot.getProcess(osPid); const commands = await createCommandsDictionary(descriptor); return commands; }, /** * One method to handle the whole setup sequence to connect to RDP backend for the Browser Console. * * This will instantiate a special DevTools module loader for the DevToolsServer. * Then spawn a DevToolsClient to connect to it. * Get a Main Process Descriptor from it. * Finally spawn a commands object for this descriptor. */ async forBrowserConsole() { // The Browser console ends up using the debugger in autocomplete. // Because the debugger can't be running in the same compartment than its debuggee, // we have to load the server in a dedicated Loader, flagged with // `freshCompartment`, which will force it to be loaded in another compartment. // We aren't using `invisibleToDebugger` in order to allow the Browser toolbox to // debug the Browser console. This is fine as they will spawn distinct Loaders and // so distinct `DevToolsServer` and actor modules. const customLoader = new DevToolsLoader({ freshCompartment: true, }); const { DevToolsServer: customDevToolsServer } = customLoader.require( "devtools/server/devtools-server" ); customDevToolsServer.init(); // We want all the actors (root, browser and target-scoped) to be registered on the // DevToolsServer. This is needed so the Browser Console can retrieve: // - the console actors, which are target-scoped (See Bug 1416105) // - the screenshotActor, which is browser-scoped (for the `:screenshot` command) customDevToolsServer.registerAllActors(); customDevToolsServer.allowChromeProcess = true; const client = new DevToolsClient(customDevToolsServer.connectPipe()); await client.connect(); const descriptor = await client.mainRoot.getMainProcess(); // Hack something in order to help TargetMixinFront to distinguish the BrowserConsole descriptor.createdForBrowserConsole = true; const target = await descriptor.getTarget(); await target.attach(); const commands = await createCommandsDictionary(descriptor); return commands; }, }; async function createLocalClient() { // Make sure the DevTools server is started. ensureDevToolsServerInitialized(); // Create the client and connect it to the local server. const client = new DevToolsClient(DevToolsServer.connectPipe()); await client.connect(); return client; } // Also expose this method for tests which would like to create a client // without involving commands. This would typically be tests against the Watcher actor // and requires to prevent having TargetCommand from running. // Or tests which are covering RootFront or global actor's fronts. exports.createLocalClientForTests = createLocalClient; function ensureDevToolsServerInitialized() { // Since a remote protocol connection will be made, let's start the // DevToolsServer here, once and for all tools. DevToolsServer.init(); // Enable all the actors. We may not need all of them and registering // only root and target might be enough DevToolsServer.registerAllActors(); // Enable being able to get child process actors // Same, this might not be useful DevToolsServer.allowChromeProcess = true; }