Source code
Revision control
Copy as Markdown
Other Tools
/**
* @license
* Copyright 2017 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
import {
type CDPEvents,
CDPSession,
CDPSessionEvent,
type CommandOptions,
} from '../api/CDPSession.js';
import {CallbackRegistry} from '../common/CallbackRegistry.js';
import {TargetCloseError} from '../common/Errors.js';
import {assert} from '../util/assert.js';
import {createProtocolErrorMessage} from '../util/ErrorLike.js';
import type {Connection} from './Connection.js';
import type {CdpTarget} from './Target.js';
/**
* @internal
*/
export class CdpCDPSession extends CDPSession {
#sessionId: string;
#targetType: string;
#callbacks = new CallbackRegistry();
#connection?: Connection;
#parentSessionId?: string;
#target?: CdpTarget;
/**
* @internal
*/
constructor(
connection: Connection,
targetType: string,
sessionId: string,
parentSessionId: string | undefined
) {
super();
this.#connection = connection;
this.#targetType = targetType;
this.#sessionId = sessionId;
this.#parentSessionId = parentSessionId;
}
/**
* Sets the {@link CdpTarget} associated with the session instance.
*
* @internal
*/
_setTarget(target: CdpTarget): void {
this.#target = target;
}
/**
* Gets the {@link CdpTarget} associated with the session instance.
*
* @internal
*/
_target(): CdpTarget {
assert(this.#target, 'Target must exist');
return this.#target;
}
override connection(): Connection | undefined {
return this.#connection;
}
override parentSession(): CDPSession | undefined {
if (!this.#parentSessionId) {
// To make it work in Firefox that does not have parent (tab) sessions.
return this;
}
const parent = this.#connection?.session(this.#parentSessionId);
return parent ?? undefined;
}
override send<T extends keyof ProtocolMapping.Commands>(
method: T,
params?: ProtocolMapping.Commands[T]['paramsType'][0],
options?: CommandOptions
): Promise<ProtocolMapping.Commands[T]['returnType']> {
if (!this.#connection) {
return Promise.reject(
new TargetCloseError(
`Protocol error (${method}): Session closed. Most likely the ${this.#targetType} has been closed.`
)
);
}
return this.#connection._rawSend(
this.#callbacks,
method,
params,
this.#sessionId,
options
);
}
/**
* @internal
*/
_onMessage(object: {
id?: number;
method: keyof CDPEvents;
params: CDPEvents[keyof CDPEvents];
error: {message: string; data: any; code: number};
result?: any;
}): void {
if (object.id) {
if (object.error) {
this.#callbacks.reject(
object.id,
createProtocolErrorMessage(object),
object.error.message
);
} else {
this.#callbacks.resolve(object.id, object.result);
}
} else {
assert(!object.id);
this.emit(object.method, object.params);
}
}
/**
* Detaches the cdpSession from the target. Once detached, the cdpSession object
* won't emit any events and can't be used to send messages.
*/
override async detach(): Promise<void> {
if (!this.#connection) {
throw new Error(
`Session already detached. Most likely the ${this.#targetType} has been closed.`
);
}
await this.#connection.send('Target.detachFromTarget', {
sessionId: this.#sessionId,
});
}
/**
* @internal
*/
_onClosed(): void {
this.#callbacks.clear();
this.#connection = undefined;
this.emit(CDPSessionEvent.Disconnected, undefined);
}
/**
* Returns the session's id.
*/
override id(): string {
return this.#sessionId;
}
/**
* @internal
*/
getPendingProtocolErrors(): Error[] {
return this.#callbacks.getPendingProtocolErrors();
}
}