UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

209 lines 9.62 kB
// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../common/common.js'; import * as Host from '../host/host.js'; import { ParallelConnection } from './Connections.js'; import { Capability, Type } from './Target.js'; import { SDKModel } from './SDKModel.js'; import { Events as TargetManagerEvents, TargetManager } from './TargetManager.js'; import { ResourceTreeModel } from './ResourceTreeModel.js'; export class ChildTargetManager extends SDKModel { #targetManager; #parentTarget; #targetAgent; #targetInfosInternal = new Map(); #childTargetsBySessionId = new Map(); #childTargetsById = new Map(); #parallelConnections = new Map(); #parentTargetId = null; constructor(parentTarget) { super(parentTarget); this.#targetManager = parentTarget.targetManager(); this.#parentTarget = parentTarget; this.#targetAgent = parentTarget.targetAgent(); parentTarget.registerTargetDispatcher(this); const browserTarget = this.#targetManager.browserTarget(); if (browserTarget) { if (browserTarget !== parentTarget) { void browserTarget.targetAgent().invoke_autoAttachRelated({ targetId: parentTarget.id(), waitForDebuggerOnStart: true }); } } else { void this.#targetAgent.invoke_setAutoAttach({ autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); } if (parentTarget.parentTarget()?.type() !== Type.Frame && !Host.InspectorFrontendHost.isUnderTest()) { void this.#targetAgent.invoke_setDiscoverTargets({ discover: true }); void this.#targetAgent.invoke_setRemoteLocations({ locations: [{ host: 'localhost', port: 9229 }] }); } } static install(attachCallback) { ChildTargetManager.attachCallback = attachCallback; SDKModel.register(ChildTargetManager, { capabilities: Capability.Target, autostart: true }); } childTargets() { return Array.from(this.#childTargetsBySessionId.values()); } async suspendModel() { await this.#targetAgent.invoke_setAutoAttach({ autoAttach: true, waitForDebuggerOnStart: false, flatten: true }); } async resumeModel() { await this.#targetAgent.invoke_setAutoAttach({ autoAttach: true, waitForDebuggerOnStart: true, flatten: true }); } dispose() { for (const sessionId of this.#childTargetsBySessionId.keys()) { this.detachedFromTarget({ sessionId, targetId: undefined }); } } targetCreated({ targetInfo }) { this.#targetInfosInternal.set(targetInfo.targetId, targetInfo); this.fireAvailableTargetsChanged(); this.dispatchEventToListeners(Events.TargetCreated, targetInfo); } targetInfoChanged({ targetInfo }) { this.#targetInfosInternal.set(targetInfo.targetId, targetInfo); const target = this.#childTargetsById.get(targetInfo.targetId); if (target) { if (target.targetInfo()?.subtype === 'prerender' && !targetInfo.subtype) { const resourceTreeModel = target.model(ResourceTreeModel); target.updateTargetInfo(targetInfo); if (resourceTreeModel && resourceTreeModel.mainFrame) { resourceTreeModel.primaryPageChanged(resourceTreeModel.mainFrame, "Activation" /* PrimaryPageChangeType.Activation */); } } else { target.updateTargetInfo(targetInfo); } } this.fireAvailableTargetsChanged(); this.dispatchEventToListeners(Events.TargetInfoChanged, targetInfo); } targetDestroyed({ targetId }) { this.#targetInfosInternal.delete(targetId); this.fireAvailableTargetsChanged(); this.dispatchEventToListeners(Events.TargetDestroyed, targetId); } // eslint-disable-next-line @typescript-eslint/no-unused-vars targetCrashed({ targetId, status, errorCode }) { } fireAvailableTargetsChanged() { TargetManager.instance().dispatchEventToListeners(TargetManagerEvents.AvailableTargetsChanged, [...this.#targetInfosInternal.values()]); } async getParentTargetId() { if (!this.#parentTargetId) { this.#parentTargetId = (await this.#parentTarget.targetAgent().invoke_getTargetInfo({})).targetInfo.targetId; } return this.#parentTargetId; } async attachedToTarget({ sessionId, targetInfo, waitingForDebugger }) { if (this.#parentTargetId === targetInfo.targetId) { return; } let type = Type.Browser; let targetName = ''; if (targetInfo.type === 'worker' && targetInfo.title && targetInfo.title !== targetInfo.url) { targetName = targetInfo.title; } else if (!['page', 'iframe', 'webview'].includes(targetInfo.type)) { const KNOWN_FRAME_PATTERNS = [ '^chrome://print/$', '^chrome://file-manager/', '^chrome://feedback/', '^chrome://.*\\.top-chrome/$', '^devtools://', ]; if (KNOWN_FRAME_PATTERNS.some(p => targetInfo.url.match(p))) { type = Type.Frame; } else { const parsedURL = Common.ParsedURL.ParsedURL.fromString(targetInfo.url); targetName = parsedURL ? parsedURL.lastPathComponentWithFragment() : '#' + (++ChildTargetManager.lastAnonymousTargetId); } } if (targetInfo.type === 'iframe' || targetInfo.type === 'webview') { type = Type.Frame; } else if (targetInfo.type === 'background_page' || targetInfo.type === 'app' || targetInfo.type === 'popup_page') { type = Type.Frame; } // TODO(lfg): ensure proper capabilities for child pages (e.g. portals). else if (targetInfo.type === 'page') { type = Type.Frame; } else if (targetInfo.type === 'worker') { type = Type.Worker; } else if (targetInfo.type === 'shared_worker') { type = Type.SharedWorker; } else if (targetInfo.type === 'service_worker') { type = Type.ServiceWorker; } else if (targetInfo.type === 'auction_worklet') { type = Type.AuctionWorklet; } const target = this.#targetManager.createTarget(targetInfo.targetId, targetName, type, this.#parentTarget, sessionId, undefined, undefined, targetInfo); this.#childTargetsBySessionId.set(sessionId, target); this.#childTargetsById.set(target.id(), target); if (ChildTargetManager.attachCallback) { await ChildTargetManager.attachCallback({ target, waitingForDebugger }); } // [crbug/1423096] Invoking this on a worker session that is not waiting for the debugger can force the worker // to resume even if there is another session waiting for the debugger. if (waitingForDebugger) { void target.runtimeAgent().invoke_runIfWaitingForDebugger(); } } detachedFromTarget({ sessionId }) { if (this.#parallelConnections.has(sessionId)) { this.#parallelConnections.delete(sessionId); } else { const target = this.#childTargetsBySessionId.get(sessionId); if (target) { target.dispose('target terminated'); this.#childTargetsBySessionId.delete(sessionId); this.#childTargetsById.delete(target.id()); } } } receivedMessageFromTarget({}) { // We use flatten protocol. } async createParallelConnection(onMessage) { // The main Target id is actually just `main`, instead of the real targetId. // Get the real id (requires an async operation) so that it can be used synchronously later. const targetId = await this.getParentTargetId(); const { connection, sessionId } = await this.createParallelConnectionAndSessionForTarget(this.#parentTarget, targetId); connection.setOnMessage(onMessage); this.#parallelConnections.set(sessionId, connection); return { connection, sessionId }; } async createParallelConnectionAndSessionForTarget(target, targetId) { const targetAgent = target.targetAgent(); const targetRouter = target.router(); const sessionId = (await targetAgent.invoke_attachToTarget({ targetId, flatten: true })).sessionId; const connection = new ParallelConnection(targetRouter.connection(), sessionId); targetRouter.registerSession(target, sessionId, connection); connection.setOnDisconnect(() => { targetRouter.unregisterSession(sessionId); void targetAgent.invoke_detachFromTarget({ sessionId }); }); return { connection, sessionId }; } targetInfos() { return Array.from(this.#targetInfosInternal.values()); } static lastAnonymousTargetId = 0; static attachCallback; } // TODO(crbug.com/1167717): Make this a const enum again // eslint-disable-next-line rulesdir/const_enum export var Events; (function (Events) { Events["TargetCreated"] = "TargetCreated"; Events["TargetDestroyed"] = "TargetDestroyed"; Events["TargetInfoChanged"] = "TargetInfoChanged"; })(Events || (Events = {})); //# sourceMappingURL=ChildTargetManager.js.map