@@ -6,16 +6,17 @@ import * as i18n from '../../core/i18n/i18n.js';
66import type * as Platform from '../../core/platform/platform.js' ;
77import type * as ProtocolClient from '../../core/protocol_client/protocol_client.js' ;
88import * as SDK from '../../core/sdk/sdk.js' ;
9+ import type * as Protocol from '../../generated/protocol.js' ;
910
1011import type * as ReportRenderer from './LighthouseReporterTypes.js' ;
1112
1213/**
1314 * @file
14- * ┌ ────────────┐
15- * │CDP Backend │
16- * └ ────────────┘
15+ * ┌─── ────────────┐
16+ * │ CDPConnection │
17+ * └─── ────────────┘
1718 * │ ▲
18- * │ │ parallelConnection
19+ * │ │
1920 * ┌┐ ▼ │ ┌┐
2021 * ││ dispatchProtocolMessage sendProtocolMessage ││
2122 * ││ │ ▲ ││
@@ -28,7 +29,10 @@ import type * as ReportRenderer from './LighthouseReporterTypes.js';
2829 * ││ onFrontendMessage notifyFrontendViaWorkerMessage ││
2930 * ││ │ ▲ ││
3031 * ││ ▼ │ ││
31- * LighthouseWorkerService ││ Either ConnectionProxy or LegacyPort ││
32+ * LighthouseWorkerService ││ WorkerConnectionTransport ││
33+ * ││ │ ▲ ││
34+ * ││ ▼ │ ││
35+ * ││ CDPConnection ││
3236 * ││ │ ▲ ││
3337 * ││ ┌─────────────────────┼─┼───────────────────────┐ ││
3438 * ││ │ Lighthouse ┌────▼──────┐ │ ││
@@ -37,10 +41,10 @@ import type * as ReportRenderer from './LighthouseReporterTypes.js';
3741 * └┘ └───────────────────────────────────────────────┘ └┘
3842 *
3943 * - All messages traversing the worker boundary are action-wrapped.
40- * - All messages over the parallelConnection speak pure CDP.
41- * - All messages within ConnectionProxy/LegacyPort speak pure CDP.
42- * - The foundational CDP connection is `parallelConnection` .
43- * - All connections within the worker are not actual ParallelConnection's .
44+ * - All messages over the CDPConnection speak pure CDP.
45+ * - Within the worker we also use a 'CDPConnection' but with a custom
46+ * transport called WorkerConnectionTransport .
47+ * - All messages within WorkerConnectionTransport/LegacyPort speak pure CDP .
4448 */
4549
4650let lastId = 1 ;
@@ -57,14 +61,15 @@ export interface LighthouseRun {
5761/**
5862 * ProtocolService manages a connection between the frontend (Lighthouse panel) and the Lighthouse worker.
5963 */
60- export class ProtocolService {
61- private mainSessionId ?: string ;
64+ export class ProtocolService implements ProtocolClient . CDPConnection . CDPConnectionObserver {
65+ private mainSessionId ?: Protocol . Target . SessionID ;
6266 private rootTargetId ?: string ;
63- private parallelConnection ?: ProtocolClient . ConnectionTransport . ConnectionTransport ;
67+ private rootTarget ?: SDK . Target . Target ;
6468 private lighthouseWorkerPromise ?: Promise < Worker > ;
6569 private lighthouseMessageUpdateCallback ?: ( ( arg0 : string ) => void ) ;
6670 private removeDialogHandler ?: ( ) => void ;
6771 private configForTesting ?: object ;
72+ private connection ?: ProtocolClient . CDPConnection . CDPConnection ;
6873
6974 async attach ( ) : Promise < void > {
7075 await SDK . TargetManager . TargetManager . instance ( ) . suspendAllTargets ( ) ;
@@ -90,12 +95,15 @@ export class ProtocolService {
9095 throw new Error ( 'Could not find the child target manager class for the root target' ) ;
9196 }
9297
93- const { connection, sessionId} = await rootChildTargetManager . createParallelConnection ( message => {
94- if ( typeof message === 'string' ) {
95- message = JSON . parse ( message ) ;
96- }
97- this . dispatchProtocolMessage ( message ) ;
98- } ) ;
98+ const router = rootTarget . router ( ) ;
99+ if ( ! router ) {
100+ throw new Error ( 'Expected root target to have a session router' ) ;
101+ }
102+
103+ const rootTargetId = await rootChildTargetManager . getParentTargetId ( ) ;
104+ const { sessionId} = await rootTarget . targetAgent ( ) . invoke_attachToTarget ( { targetId : rootTargetId , flatten : true } ) ;
105+ this . connection = router ;
106+ this . connection . observe ( this ) ;
99107
100108 // Lighthouse implements its own dialog handler like this, however its lifecycle ends when
101109 // the internal Lighthouse session is disposed.
@@ -114,8 +122,8 @@ export class ProtocolService {
114122 this . removeDialogHandler = ( ) =>
115123 resourceTreeModel . removeEventListener ( SDK . ResourceTreeModel . Events . JavaScriptDialogOpening , dialogHandler ) ;
116124
117- this . parallelConnection = connection ;
118- this . rootTargetId = await rootChildTargetManager . getParentTargetId ( ) ;
125+ this . rootTargetId = rootTargetId ;
126+ this . rootTarget = rootTarget ;
119127 this . mainSessionId = sessionId ;
120128 }
121129
@@ -166,20 +174,22 @@ export class ProtocolService {
166174
167175 async detach ( ) : Promise < void > {
168176 const oldLighthouseWorker = this . lighthouseWorkerPromise ;
169- const oldParallelConnection = this . parallelConnection ;
177+ const oldRootTarget = this . rootTarget ;
170178
171179 // When detaching, make sure that we remove the old promises, before we
172180 // perform any async cleanups. That way, if there is a message coming from
173181 // lighthouse while we are in the process of cleaning up, we shouldn't deliver
174182 // them to the backend.
175183 this . lighthouseWorkerPromise = undefined ;
176- this . parallelConnection = undefined ;
184+ this . rootTarget = undefined ;
185+ this . connection ?. unobserve ( this ) ;
186+ this . connection = undefined ;
177187
178188 if ( oldLighthouseWorker ) {
179189 ( await oldLighthouseWorker ) . terminate ( ) ;
180190 }
181- if ( oldParallelConnection ) {
182- await oldParallelConnection . disconnect ( ) ;
191+ if ( oldRootTarget && this . mainSessionId ) {
192+ await oldRootTarget . targetAgent ( ) . invoke_detachFromTarget ( { sessionId : this . mainSessionId } ) ;
183193 }
184194 await SDK . TargetManager . TargetManager . instance ( ) . resumeAllTargets ( ) ;
185195 this . removeDialogHandler ?.( ) ;
@@ -189,7 +199,11 @@ export class ProtocolService {
189199 this . lighthouseMessageUpdateCallback = callback ;
190200 }
191201
192- private dispatchProtocolMessage ( message : string | object ) : void {
202+ onEvent < T extends ProtocolClient . CDPConnection . Event > ( event : ProtocolClient . CDPConnection . CDPEvent < T > ) : void {
203+ this . dispatchProtocolMessage ( event ) ;
204+ }
205+
206+ private dispatchProtocolMessage ( message : ProtocolClient . CDPConnection . CDPReceivableMessage ) : void {
193207 // A message without a sessionId is the main session of the main target (call it "Main session").
194208 // A parallel connection and session was made that connects to the same main target (call it "Lighthouse session").
195209 // Messages from the "Lighthouse session" have a sessionId.
@@ -199,15 +213,15 @@ export class ProtocolService {
199213 // * the message has a sessionId (is not for the "Main session")
200214 // * the message does not have a sessionId (is for the "Main session"), but only for the Target domain
201215 // (to kickstart autoAttach in LH).
202- const protocolMessage = message as {
203- sessionId ?: string ,
204- method ?: string ,
205- } ;
206- if ( protocolMessage . sessionId || ( protocolMessage . method ?. startsWith ( 'Target' ) ) ) {
216+ if ( message . sessionId || ( 'method' in message && message . method ?. startsWith ( 'Target' ) ) ) {
207217 void this . send ( 'dispatchProtocolMessage' , { message} ) ;
208218 }
209219 }
210220
221+ onDisconnect ( ) : void {
222+ // Do nothing.
223+ }
224+
211225 private initWorker ( ) : Promise < Worker > {
212226 this . lighthouseWorkerPromise = new Promise < Worker > ( resolve => {
213227 const workerUrl = new URL ( '../../entrypoints/lighthouse_worker/lighthouse_worker.js' , import . meta. url ) ;
@@ -255,9 +269,19 @@ export class ProtocolService {
255269 }
256270
257271 private sendProtocolMessage ( message : string ) : void {
258- if ( this . parallelConnection ) {
259- this . parallelConnection . sendRawMessage ( message ) ;
260- }
272+ const { id, method, params, sessionId} = JSON . parse ( message ) ;
273+ // CDPConnection manages it's own message IDs and it's important, otherwise we'd clash
274+ // with the rest of the DevTools traffic.
275+ // Instead, we ignore the ID coming from the worker when sending the command, but
276+ // patch it back in when sending the response back to the worker.
277+ void this . connection ?. send ( method , params , sessionId ) . then ( response => {
278+ this . dispatchProtocolMessage ( {
279+ id,
280+ sessionId,
281+ result : 'result' in response ? response . result : undefined ,
282+ error : 'error' in response ? response . error : undefined ,
283+ } ) ;
284+ } ) ;
261285 }
262286
263287 private async send ( action : string , args : Record < string , string | string [ ] | object > = { } ) : Promise < void > {
0 commit comments