puppeteer-core
Version:
A high-level API to control headless Chrome over the DevTools Protocol
195 lines (184 loc) • 5.1 kB
text/typescript
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ConnectionTransport} from '../common/ConnectionTransport.js';
const tabTargetInfo = {
targetId: 'tabTargetId',
type: 'tab',
title: 'tab',
url: 'about:blank',
attached: false,
canAccessOpener: false,
};
const pageTargetInfo = {
targetId: 'pageTargetId',
type: 'page',
title: 'page',
url: 'about:blank',
attached: false,
canAccessOpener: false,
};
/**
* Experimental ExtensionTransport allows establishing a connection via
* chrome.debugger API if Puppeteer runs in an extension. Since Chrome
* DevTools Protocol is restricted for extensions, the transport
* implements missing commands and events.
*
* @experimental
* @public
*/
export class ExtensionTransport implements ConnectionTransport {
static async connectTab(tabId: number): Promise<ExtensionTransport> {
await chrome.debugger.attach({tabId}, '1.3');
return new ExtensionTransport(tabId);
}
onmessage?: (message: string) => void;
onclose?: () => void;
/**
* @internal
*/
constructor(tabId: number) {
this.
chrome.debugger.onEvent.addListener(this.
}
source: chrome.debugger.Debuggee,
method: string,
params?: object | undefined,
): void => {
if (source.tabId !== this.
return;
}
this.
// @ts-expect-error sessionId is not in stable yet.
sessionId: source.sessionId ?? 'pageTargetSessionId',
method: method,
params: params,
});
};
this.onmessage?.(JSON.stringify(message));
}
send(message: string): void {
const parsed = JSON.parse(message);
switch (parsed.method) {
case 'Browser.getVersion': {
this.
id: parsed.id,
sessionId: parsed.sessionId,
method: parsed.method,
result: {
protocolVersion: '1.3',
product: 'chrome',
revision: 'unknown',
userAgent: 'chrome',
jsVersion: 'unknown',
},
});
return;
}
case 'Target.getBrowserContexts': {
this.
id: parsed.id,
sessionId: parsed.sessionId,
method: parsed.method,
result: {
browserContextIds: [],
},
});
return;
}
case 'Target.setDiscoverTargets': {
this.
method: 'Target.targetCreated',
params: {
targetInfo: tabTargetInfo,
},
});
this.
method: 'Target.targetCreated',
params: {
targetInfo: pageTargetInfo,
},
});
this.
id: parsed.id,
sessionId: parsed.sessionId,
method: parsed.method,
result: {},
});
return;
}
case 'Target.setAutoAttach': {
if (parsed.sessionId === 'tabTargetSessionId') {
this.
method: 'Target.attachedToTarget',
params: {
targetInfo: pageTargetInfo,
sessionId: 'pageTargetSessionId',
},
});
this.
id: parsed.id,
sessionId: parsed.sessionId,
method: parsed.method,
result: {},
});
return;
} else if (!parsed.sessionId) {
this.
method: 'Target.attachedToTarget',
params: {
targetInfo: tabTargetInfo,
sessionId: 'tabTargetSessionId',
},
});
this.
id: parsed.id,
sessionId: parsed.sessionId,
method: parsed.method,
result: {},
});
return;
}
}
}
if (parsed.sessionId === 'pageTargetSessionId') {
delete parsed.sessionId;
}
chrome.debugger
.sendCommand(
// @ts-expect-error sessionId is not in stable yet.
{tabId: this.
parsed.method,
parsed.params,
)
.then(response => {
this.
id: parsed.id,
sessionId: parsed.sessionId ?? 'pageTargetSessionId',
method: parsed.method,
result: response,
});
})
.catch(err => {
this.
id: parsed.id,
sessionId: parsed.sessionId ?? 'pageTargetSessionId',
method: parsed.method,
error: {
code: err?.code,
data: err?.data,
message: err?.message ?? 'CDP error had no message',
},
});
});
}
close(): void {
chrome.debugger.onEvent.removeListener(this.
void chrome.debugger.detach({tabId: this.
}
}