CdpSession.ts (4376B)
1 /** 2 * @license 3 * Copyright 2017 Google Inc. 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js'; 8 9 import { 10 type CDPEvents, 11 CDPSession, 12 CDPSessionEvent, 13 type CommandOptions, 14 } from '../api/CDPSession.js'; 15 import {CallbackRegistry} from '../common/CallbackRegistry.js'; 16 import {TargetCloseError} from '../common/Errors.js'; 17 import {assert} from '../util/assert.js'; 18 import {createProtocolErrorMessage} from '../util/ErrorLike.js'; 19 20 import type {Connection} from './Connection.js'; 21 import type {CdpTarget} from './Target.js'; 22 23 /** 24 * @internal 25 */ 26 27 export class CdpCDPSession extends CDPSession { 28 #sessionId: string; 29 #targetType: string; 30 #callbacks = new CallbackRegistry(); 31 #connection: Connection; 32 #parentSessionId?: string; 33 #target?: CdpTarget; 34 #rawErrors = false; 35 #detached = false; 36 /** 37 * @internal 38 */ 39 constructor( 40 connection: Connection, 41 targetType: string, 42 sessionId: string, 43 parentSessionId: string | undefined, 44 rawErrors: boolean, 45 ) { 46 super(); 47 this.#connection = connection; 48 this.#targetType = targetType; 49 this.#sessionId = sessionId; 50 this.#parentSessionId = parentSessionId; 51 this.#rawErrors = rawErrors; 52 } 53 54 /** 55 * Sets the {@link CdpTarget} associated with the session instance. 56 * 57 * @internal 58 */ 59 setTarget(target: CdpTarget): void { 60 this.#target = target; 61 } 62 63 /** 64 * Gets the {@link CdpTarget} associated with the session instance. 65 * 66 * @internal 67 */ 68 target(): CdpTarget { 69 assert(this.#target, 'Target must exist'); 70 return this.#target; 71 } 72 73 override connection(): Connection | undefined { 74 return this.#connection; 75 } 76 77 override get detached(): boolean { 78 return this.#connection._closed || this.#detached; 79 } 80 81 override parentSession(): CDPSession | undefined { 82 if (!this.#parentSessionId) { 83 // In some cases, e.g., DevTools pages there is no parent session. In this 84 // case, we treat the current session as the parent session. 85 return this; 86 } 87 const parent = this.#connection?.session(this.#parentSessionId); 88 return parent ?? undefined; 89 } 90 91 override send<T extends keyof ProtocolMapping.Commands>( 92 method: T, 93 params?: ProtocolMapping.Commands[T]['paramsType'][0], 94 options?: CommandOptions, 95 ): Promise<ProtocolMapping.Commands[T]['returnType']> { 96 if (this.detached) { 97 return Promise.reject( 98 new TargetCloseError( 99 `Protocol error (${method}): Session closed. Most likely the ${this.#targetType} has been closed.`, 100 ), 101 ); 102 } 103 return this.#connection._rawSend( 104 this.#callbacks, 105 method, 106 params, 107 this.#sessionId, 108 options, 109 ); 110 } 111 112 /** 113 * @internal 114 */ 115 onMessage(object: { 116 id?: number; 117 method: keyof CDPEvents; 118 params: CDPEvents[keyof CDPEvents]; 119 error: {message: string; data: any; code: number}; 120 result?: any; 121 }): void { 122 if (object.id) { 123 if (object.error) { 124 if (this.#rawErrors) { 125 this.#callbacks.rejectRaw(object.id, object.error); 126 } else { 127 this.#callbacks.reject( 128 object.id, 129 createProtocolErrorMessage(object), 130 object.error.message, 131 ); 132 } 133 } else { 134 this.#callbacks.resolve(object.id, object.result); 135 } 136 } else { 137 assert(!object.id); 138 this.emit(object.method, object.params); 139 } 140 } 141 142 /** 143 * Detaches the cdpSession from the target. Once detached, the cdpSession object 144 * won't emit any events and can't be used to send messages. 145 */ 146 override async detach(): Promise<void> { 147 if (this.detached) { 148 throw new Error( 149 `Session already detached. Most likely the ${this.#targetType} has been closed.`, 150 ); 151 } 152 await this.#connection.send('Target.detachFromTarget', { 153 sessionId: this.#sessionId, 154 }); 155 this.#detached = true; 156 } 157 158 /** 159 * @internal 160 */ 161 onClosed(): void { 162 this.#callbacks.clear(); 163 this.#detached = true; 164 this.emit(CDPSessionEvent.Disconnected, undefined); 165 } 166 167 /** 168 * Returns the session's id. 169 */ 170 override id(): string { 171 return this.#sessionId; 172 } 173 174 /** 175 * @internal 176 */ 177 getPendingProtocolErrors(): Error[] { 178 return this.#callbacks.getPendingProtocolErrors(); 179 } 180 }