/* 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 { processDescriptorSpec, } = require("resource://devtools/shared/specs/descriptors/process.js"); const { WindowGlobalTargetFront, } = require("resource://devtools/client/fronts/targets/window-global.js"); const { ContentProcessTargetFront, } = require("resource://devtools/client/fronts/targets/content-process.js"); const { FrontClassWithSpec, registerFront, } = require("resource://devtools/shared/protocol.js"); const { DescriptorMixin, } = require("resource://devtools/client/fronts/descriptors/descriptor-mixin.js"); const DESCRIPTOR_TYPES = require("resource://devtools/client/fronts/descriptors/descriptor-types.js"); class ProcessDescriptorFront extends DescriptorMixin( FrontClassWithSpec(processDescriptorSpec) ) { constructor(client, targetFront, parentFront) { super(client, targetFront, parentFront); this._isParent = false; this._processTargetFront = null; this._targetFrontPromise = null; } descriptorType = DESCRIPTOR_TYPES.PROCESS; form(json) { this.id = json.id; this._isParent = json.isParent; this._isWindowlessParent = json.isWindowlessParent; this.traits = json.traits || {}; } async _createProcessTargetFront(form) { let front = null; // the request to getTarget may return a ContentProcessTargetActor or a // ParentProcessTargetActor. In most cases getProcess(0) will return the // main process target actor, which is a ParentProcessTargetActor, but // not in xpcshell, which uses a ContentProcessTargetActor. So select // the right front based on the actor ID. if (form.actor.includes("parentProcessTarget")) { // ParentProcessTargetActor doesn't have a specific front, instead it uses // WindowGlobalTargetFront on the client side. front = new WindowGlobalTargetFront(this._client, null, this); } else { front = new ContentProcessTargetFront(this._client, null, this); } // As these fronts aren't instantiated by protocol.js, we have to set their actor ID // manually like that: front.actorID = form.actor; front.form(form); // @backward-compat { version 84 } Older server don't send the processID in the form if (!front.processID) { front.processID = this.id; } this.manage(front); return front; } /** * This flag should be true for parent process descriptors of a regular * browser instance, where you can expect the target to be associated with a * window global. * * This will typically be true for the descriptor used by the Browser Toolbox * or the Browser Console opened against a regular Firefox instance. * * On the contrary this will be false for parent process descriptors created * for xpcshell debugging or for background task debugging. */ get isBrowserProcessDescriptor() { return this._isParent && !this._isWindowlessParent; } get isParentProcessDescriptor() { return this._isParent; } get isProcessDescriptor() { return true; } getCachedTarget() { return this._processTargetFront; } async getTarget() { // Only return the cached Target if it is still alive. if (this._processTargetFront && !this._processTargetFront.isDestroyed()) { return this._processTargetFront; } // Otherwise, ensure that we don't try to spawn more than one Target by // returning the pending promise if (this._targetFrontPromise) { return this._targetFrontPromise; } this._targetFrontPromise = (async () => { let targetFront = null; try { const targetForm = await super.getTarget(); targetFront = await this._createProcessTargetFront(targetForm); } catch (e) { // This is likely to happen if we get a lot of events which drop previous // processes. console.log( `Request to connect to ProcessDescriptor "${this.id}" failed: ${e}` ); } // Save the reference to the target only after the call to attach // so that getTarget always returns the attached target in case of concurrent calls this._processTargetFront = targetFront; // clear the promise if we are finished so that we can re-connect if // necessary this._targetFrontPromise = null; return targetFront; })(); return this._targetFrontPromise; } destroy() { if (this._processTargetFront) { this._processTargetFront.destroy(); this._processTargetFront = null; } this._targetFrontPromise = null; super.destroy(); } } exports.ProcessDescriptorFront = ProcessDescriptorFront; registerFront(ProcessDescriptorFront);