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/. */
6 const { rootSpec
} = require("resource://devtools/shared/specs/root.js");
10 } = require("resource://devtools/shared/protocol.js");
12 loader
.lazyRequireGetter(
15 "resource://devtools/shared/protocol.js",
19 class RootFront
extends FrontClassWithSpec(rootSpec
) {
20 constructor(client
, targetFront
, parentFront
) {
21 super(client
, targetFront
, parentFront
);
23 // Cache root form as this will always be the same value.
24 Object
.defineProperty(this, "rootForm", {
27 this.rootForm
= this.getRoot();
33 // Cache of already created global scoped fronts
34 // [typeName:string => Front instance]
35 this.fronts
= new Map();
37 this._client
= client
;
41 // Root Front is a special Front. It is the only one to set its actor ID manually
42 // out of the form object returned by RootActor.sayHello which is called when calling
43 // DevToolsClient.connect().
44 this.actorID
= form
.from;
46 this.applicationType
= form
.applicationType
;
47 this.traits
= form
.traits
;
50 * Retrieve all service worker registrations with their corresponding workers.
51 * @param {Array} [workerTargets] (optional)
52 * Array containing the result of a call to `listAllWorkerTargets`.
53 * (this exists to avoid duplication of calls to that method)
54 * @return {Object[]} result - An Array of Objects with the following format
55 * - {result[].registration} - The registration front
56 * - {result[].workers} Array of form-like objects for service workers
58 async
listAllServiceWorkers(workerTargets
) {
60 const { registrations
} = await
this.listServiceWorkerRegistrations();
61 const allWorkers
= workerTargets
63 : await
this.listAllWorkerTargets();
65 for (const registrationFront
of registrations
) {
66 // workers from the registration, ordered from most recent to older
68 registrationFront
.activeWorker
,
69 registrationFront
.waitingWorker
,
70 registrationFront
.installingWorker
,
71 registrationFront
.evaluatingWorker
,
73 // filter out non-existing workers
75 // build a worker object with its WorkerDescriptorFront
77 const workerDescriptorFront
= allWorkers
.find(
78 targetFront
=> targetFront
.id
=== workerFront
.id
83 name
: workerFront
.url
,
84 state
: workerFront
.state
,
85 stateText
: workerFront
.stateText
,
87 origin
: workerFront
.origin
,
88 workerDescriptorFront
,
92 // TODO: return only the worker targets. See Bug 1620605
94 registration
: registrationFront
,
103 * Retrieve all service worker registrations as well as workers from the parent and
104 * content processes. Listing service workers involves merging information coming from
105 * registrations and workers, this method will combine this information to present a
106 * unified array of serviceWorkers. If you are only interested in other workers, use
111 * array of form-like objects for serviceworkers
113 * Array of WorkerTargetActor forms, containing shared workers.
115 * Array of WorkerTargetActor forms, containing other workers.
117 async
listAllWorkers() {
118 const allWorkers
= await
this.listAllWorkerTargets();
119 const serviceWorkers
= await
this.listAllServiceWorkers(allWorkers
);
121 // NOTE: listAllServiceWorkers() now returns all the workers belonging to
122 // a registration. To preserve the usual behavior at about:debugging,
123 // in which we show only the most recent one, we grab the first
124 // worker in the array only.
126 service
: serviceWorkers
127 .map(({ registration
, workers
}) => {
128 return workers
.slice(0, 1).map(worker
=> {
129 return Object
.assign(worker
, {
130 registrationFront
: registration
,
131 fetch
: registration
.fetch
,
140 allWorkers
.forEach(front
=> {
144 origin
: front
.origin
,
146 workerDescriptorFront
: front
,
149 switch (front
.type
) {
150 case Ci
.nsIWorkerDebugger
.TYPE_SERVICE
:
151 // do nothing, since we already fetched them in `serviceWorkers`
153 case Ci
.nsIWorkerDebugger
.TYPE_SHARED
:
154 result
.shared
.push(worker
);
157 result
.other
.push(worker
);
164 /** Get the target fronts for all worker threads running in any process. */
165 async
listAllWorkerTargets() {
166 const listParentWorkers
= async () => {
167 const { workers
} = await
this.listWorkers();
170 const listChildWorkers
= async () => {
171 const processes
= await
this.listProcesses();
172 const processWorkers
= await Promise
.all(
173 processes
.map(async processDescriptorFront
=> {
174 // Ignore parent process
175 if (processDescriptorFront
.isParentProcessDescriptor
) {
179 const front
= await processDescriptorFront
.getTarget();
183 const response
= await front
.listWorkers();
184 return response
.workers
;
186 if (e
.message
.includes("Connection closed")) {
194 return processWorkers
.flat();
197 const [parentWorkers
, childWorkers
] = await Promise
.all([
202 return parentWorkers
.concat(childWorkers
);
206 * Fetch the ProcessDescriptorFront for the main process.
208 * `getProcess` requests allows to fetch the descriptor for any process and
209 * the main process is having the process ID zero.
212 return this.getProcess(0);
216 * Fetch the tab descriptor for the currently selected tab, or for a specific
217 * tab given as first parameter.
219 * @param [optional] object filter
220 * A dictionary object with following optional attributes:
221 * - browserId: use to match any tab
222 * - tab: a reference to xul:tab element (used for local tab debugging)
223 * - isWebExtension: an optional boolean to flag TabDescriptors
224 * If nothing is specified, returns the actor for the currently
227 async
getTab(filter
) {
230 if (typeof filter
.browserId
== "number") {
231 packet
.browserId
= filter
.browserId
;
232 } else if ("tab" in filter
) {
233 const browser
= filter
.tab
.linkedBrowser
;
234 packet
.browserId
= browser
.browserId
;
236 // Throw if a filter object have been passed but without
237 // any clearly idenfified filter.
238 throw new Error("Unsupported argument given to getTab request");
242 const descriptorFront
= await
super.getTab(packet
);
244 // Will flag TabDescriptor used by WebExtension codebase.
245 if (filter
?.isWebExtension
) {
246 descriptorFront
.setIsForWebExtension(true);
249 // If the tab is a local tab, forward it to the descriptor.
250 if (filter
?.tab
?.tagName
== "tab") {
251 // Ignore the fake `tab` object we receive, where there is only a
252 // `linkedBrowser` attribute, but this isn't a real <tab> element.
253 // devtools/client/framework/test/browser_toolbox_target.js is passing such
255 descriptorFront
.setLocalTab(filter
.tab
);
258 return descriptorFront
;
262 * Fetch the target front for a given add-on.
263 * This is just an helper on top of `listAddons` request.
265 * @param object filter
266 * A dictionary object with following attribute:
267 * - id: used to match the add-on to connect to.
269 async
getAddon({ id
}) {
270 const addons
= await
this.listAddons();
271 const webextensionDescriptorFront
= addons
.find(addon
=> addon
.id
=== id
);
272 return webextensionDescriptorFront
;
276 * Fetch the target front for a given worker.
277 * This is just an helper on top of `listAllWorkers` request.
281 async
getWorker(id
) {
282 const { service
, shared
, other
} = await
this.listAllWorkers();
283 const worker
= [...service
, ...shared
, ...other
].find(w
=> w
.id
=== id
);
287 return worker
.workerDescriptorFront
|| worker
.registrationFront
;
291 * Test request that returns the object passed as first argument.
293 * `echo` is special as all the property of the given object have to be passed
294 * on the packet object. That's not something that can be achieve by requester helper.
298 packet
.type
= "echo";
299 return this.request(packet
);
303 * This function returns a protocol.js Front for any root actor.
304 * i.e. the one directly served from RootActor.listTabs or getRoot.
306 * @param String typeName
307 * The type name used in protocol.js's spec for this actor.
309 async
getFront(typeName
) {
310 let front
= this.fronts
.get(typeName
);
314 const rootForm
= await
this.rootForm
;
315 front
= getFront(this._client
, typeName
, rootForm
);
316 this.fronts
.set(typeName
, front
);
321 * This function returns true if the root actor has a registered global actor
323 * @param {String} actorName
324 * The name of a global actor.
328 async
hasActor(actorName
) {
329 const rootForm
= await
this.rootForm
;
330 return !!rootForm
[actorName
+ "Actor"];
333 exports
.RootFront
= RootFront
;
334 registerFront(RootFront
);