Bug 1880488 [wpt PR 44609] - Update wpt metadata, a=testonly
[gecko.git] / devtools / client / framework / commands-from-url.js
blobf9b5cec46cf69c8fba6468b1d3f134f808cf8711
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   DevToolsServer,
9 } = require("resource://devtools/server/devtools-server.js");
10 const {
11   DevToolsClient,
12 } = require("resource://devtools/client/devtools-client.js");
13 const {
14   remoteClientManager,
15 } = require("resource://devtools/client/shared/remote-debugging/remote-client-manager.js");
16 const {
17   CommandsFactory,
18 } = require("resource://devtools/shared/commands/commands-factory.js");
20 /**
21  * Construct a commands object for a given URL with various query parameters:
22  *
23  * - host, port & ws: See the documentation for clientFromURL
24  *
25  * - type: "tab", "extension", "worker" or "process"
26  *      {String} The type of target to connect to.
27  *
28  * If type == "tab":
29  * - id:
30  *      {Number} the tab browserId
31  *
32  * If type == "extension":
33  * - id:
34  *      {String} the addonID of the webextension to debug.
35  *
36  * If type == "worker":
37  * - id:
38  *      {String} the unique Worker id of the Worker to debug.
39  *
40  * If type == "process":
41  * - id:
42  *      {Number} the process id to debug. Default to 0, which is the parent process.
43  *
44  *
45  * @param {URL} url
46  *        The url to fetch query params from.
47  *
48  * @return A commands object
49  */
50 exports.commandsFromURL = async function commandsFromURL(url) {
51   const client = await clientFromURL(url);
52   const params = url.searchParams;
54   // Clients retrieved from the remote-client-manager are already connected.
55   const isCachedClient = params.get("remoteId");
56   if (!isCachedClient) {
57     // Connect any other client.
58     await client.connect();
59   }
61   const id = params.get("id");
62   const type = params.get("type");
64   let commands;
65   try {
66     commands = await _commandsFromURL(client, id, type);
67   } catch (e) {
68     if (!isCachedClient) {
69       // If the client was not cached, then the client was created here. If the target
70       // creation failed, we should close the client.
71       await client.close();
72     }
73     throw e;
74   }
76   // When opening about:debugging's toolboxes for remote runtimes,
77   // we create a new commands using a shared and cached client.
78   // Prevent closing the DevToolsClient on toolbox close and Commands destruction
79   // as this can be used by about:debugging and other toolboxes.
80   if (isCachedClient) {
81     commands.shouldCloseClient = false;
82   }
84   return commands;
87 async function _commandsFromURL(client, id, type) {
88   if (!type) {
89     throw new Error("commandsFromURL, missing type parameter");
90   }
92   let commands;
93   if (type === "tab") {
94     // Fetch target for a remote tab
95     id = parseInt(id, 10);
96     if (isNaN(id)) {
97       throw new Error(
98         `commandsFromURL, wrong tab id '${id}', should be a number`
99       );
100     }
101     try {
102       commands = await CommandsFactory.forRemoteTab(id, { client });
103     } catch (ex) {
104       if (ex.message.startsWith("Protocol error (noTab)")) {
105         throw new Error(
106           `commandsFromURL, tab with browserId '${id}' doesn't exist`
107         );
108       }
109       throw ex;
110     }
111   } else if (type === "extension") {
112     commands = await CommandsFactory.forAddon(id, { client });
114     if (!commands) {
115       throw new Error(
116         `commandsFromURL, extension with id '${id}' doesn't exist`
117       );
118     }
119   } else if (type === "worker") {
120     commands = await CommandsFactory.forWorker(id, { client });
122     if (!commands) {
123       throw new Error(`commandsFromURL, worker with id '${id}' doesn't exist`);
124     }
125   } else if (type == "process") {
126     // When debugging firefox itself, force the server to accept debugging processes.
127     DevToolsServer.allowChromeProcess = true;
128     commands = await CommandsFactory.forMainProcess({ client });
129   } else {
130     throw new Error(`commandsFromURL, unsupported type '${type}' parameter`);
131   }
133   return commands;
137  * Create a DevToolsClient for a given URL object having various query parameters:
139  * host:
140  *    {String} The hostname or IP address to connect to.
141  * port:
142  *    {Number} The TCP port to connect to, to use with `host` argument.
143  * remoteId:
144  *    {String} Remote client id, for runtimes from the remote-client-manager
145  * ws:
146  *    {Boolean} If true, connect via websocket instead of regular TCP connection.
148  * @param {URL} url
149  *        The url to fetch query params from.
150  * @return a promise that resolves a DevToolsClient object
151  */
152 async function clientFromURL(url) {
153   const params = url.searchParams;
155   // If a remote id was provided we should already have a connected client available.
156   const remoteId = params.get("remoteId");
157   if (remoteId) {
158     const client = remoteClientManager.getClientByRemoteId(remoteId);
159     if (!client) {
160       throw new Error(`Could not find client with remote id: ${remoteId}`);
161     }
162     return client;
163   }
165   const host = params.get("host");
166   const port = params.get("port");
167   const webSocket = !!params.get("ws");
169   let transport;
170   if (port) {
171     transport = await DevToolsClient.socketConnect({ host, port, webSocket });
172   } else {
173     // Setup a server if we don't have one already running
174     DevToolsServer.init();
175     DevToolsServer.registerAllActors();
176     transport = DevToolsServer.connectPipe();
177   }
178   return new DevToolsClient(transport);