I'd love to play around with the Page.captureScreenshot request for an experimental VS Code extension.
It would be awesome if the VS Code Debug Adapter would expose some request that would allow to call these CDB requests through debugSession.customRequest.
1) This would allow for some cool (experimental) VS Code extensions. The captureScreenshot request could be used to show a screenshot of variables that point to an HTML element. I guess there are also some other cool methods exposed by chrome that could further push the debugging experience.
1) User experience could be degraded if such extensions bring the debug adapter in an inconsistent state by calling some CDP requests they are not supposed to call.
I guess to mitigate 1), the exposed CDP requests could be allow-listed (e.g. only when using the non-insiders build to enable exploring ideas). Page.captureScreenshot should be pretty risk free.
What do you think?
I'd be open to that. Feel free to toss in a PR, or I can get to it eventually. Code pointers:
npm run generateapisThanks - I'll try to submit a PR this week!
~Sorry for the delay. I had a look today, but I was not able to debug the debug-adapter. I opened the debug-adapter in the latest vscode insider build, ran npm run watch and launched the "Extension" launch config. The entry point of the extension called. However, my breakpoints in the constructor of the DebugAdapter are not hit.~
~I also get this error in the Extension Development Host:~

~I guess its a trivial mistake, but I'm stuck.~
I didn't figure out that I had to uninstall the bundled JavaScript Debugger Extension. I didn't even know this is possible...
My breakpoints are being hit now 馃槈
It works nicely:

I'll try to prepare a PR soon that exposes this cdp method.
I think a hover provider can be implemented that shows a screenshot of HTML elements when hovering on an HtmlElement while debugging the code.
I would like to piggyback on vscode-js-debugs CDP connection in my own extension/debug adapter. As I understand your proposal @hediet, you try to expose very specific commands through the customRequest, whereas I would like to proxy "everything" to my own adapter.
What do you think about this and how is your PR coming? Maybe we can collaborate on this?
whereas I would like to proxy "everything" to my own adapter
I would prefer a design where you don't actually proxy 'everything'. For example, you may not care about scriptParsed events for your use case and only request/response pairs and output events. CDP is chatty and latency is important, I don't want to add extra work unnecessarily.
I've started working with the Edge Devtools team who would like to integrate with js-debug using this mechanism too. I'm not sure how far you've gone, my initial thought for a design would be something like this:
js-debug.requestCdpProxy with the debug session id. The first time this is executed, js-debug stands up a websocket server for debugging.{ remotePort: number, localPort: number }. These may be different if the user had something running on the local port.cdpSubscribe(event: string) -> void asks that js-debug proxy the given event. Prefix-matching via stars allowed, e.g. Debugger.*cdpSend(method: string, params: object, sessionId?: string) -> object sends a request to CDP, returns the resulting response or errorIf you've got something working, I'd like to help bring that in, let me know 馃檪
I like that!
But couldn't js-debug expose a typescript API (rathar than a websocket server with an rpc protocol), similiar to the typescript extension or liveshare extension?
I think this might simplify consumption of the API and might even make the API more stable. A light npm package might provide types/helper functions to consume this API.
Also, it would be very nice if this design allows multiple extensions to use this API, not just exclusive access. I guess this is unproblematic as cdbSubscribe can only subscribe to events but not requests issued by the debuggee?
I got something working, but it was just a technical proof of concept, nothing that could help yet. Due to university exams and my masters thesis, I had to put my idea on hold, but I will resume in a couple of months.
But couldn't js-debug expose a typescript API (rathar than a websocket server with an rpc protocol), similiar to the typescript extension or liveshare extension?
There's a small difficulty is that the typescript api is only accessible by extensions running in the same extension host. js-debug will always run in the workspace, and this would mean ui extensions don't have access. However I'm not sure if m/any extensions will run into this, there are not many ui extensions.
I guess this is unproblematic as cdbSubscribe can only subscribe to events but not requests issued by the debuggee?
Yep
I had to put my idea on hold, but I will resume in a couple of months.
Ok, thanks for letting me know. Hope your thesis goes well!
There's a small difficulty is that the typescript api is only accessible by extensions running in the same extension host. js-debug will always run in the workspace, and this would mean ui extensions don't have access. However I'm not sure if m/any extensions will run into this, there are not many ui extensions.
I see. Anyways, I think it might make sense to make this a hidden implementation detail of some vscode-js-debug-api npm package, what do you think? I feel like a js debug adapter protocol behind the debug adapter protocol might be quite confusing.
Ok, thanks for letting me know. Hope your thesis goes well!
Thanks! I was lucky and found a nice employer who will let me work on VS Code, I hope I can focus more on VS Code/extensions then ;)
I've started working with the Edge Devtools team who would like to integrate with js-debug using this mechanism too. I'm not sure how far you've gone, my initial thought for a design would be something like this ...
I am digging my way through a similar solution right now, with three differences:
vscode-js-debug does not expose a requestProxy command (yet; I like this idea!)vscode-js-debug tries to skip as much internal processing as possible when the proxy forwards/receives data. I.e., it hooks into the communication stream as early as possible. As I understand your sequence diagram, js-debug takes a more active role?Good to hear that my approach was not that much off so far 馃槄 @connor4312 I will adapt my solution to your considerations and keep you posted.
do we need any other introspection ability?
For my project personally, I feel like this is enough.
I am currently implementing the WebSocket server to proxy the calls from another extension.
I create the CDPProxy instance tied to a DebugAdapter once the first requestCDPProxy DAP message is received. As far as I understand it, the DebugAdapter has access to an instance of the "high-level" CDP API, created by CDPSession (https://github.com/microsoft/vscode-js-debug/blob/main/src/cdp/connection.ts#L225), only. This high-level API is nice to use within js-debugs debug adapter, but makes a more general proxying a bit more involved.
If there would be a way to expose the underlying CDPSession (https://github.com/microsoft/vscode-js-debug/blob/main/src/cdp/connection.ts#L46) to the DebugAdapter, we could use the sessions connection more efficiently, because we would not have to work with the high-level abstractions of it.
@connor4312 Is exposing the CDPSession something you could agree with from a design perspective, or do you have an even better idea how we could do the proxying as simple and fast as possible?
EDIT: I opted for a different approach for now... Please ignore this message :)
A first implementation for a CDP proxy is available here: https://github.com/swissmanu/vscode-js-debug/tree/bde7c1bbcdfc312b876cbf24f5a4790921d7d896
The communication protocol is available in a separate repository: https://github.com/swissmanu/vscode-js-debug-cdp-proxy-api
Even though this version is still a bit rough around the edges, it already allows other extensions to interact with js-debugs CDP connection. @connor4312 @hediet what do you think?
Once you had a look at my changes, maybe you guys have an idea how I could solve the following, ugly any casts?
I tried a few things, but could not come up with something more satisfying so far.
ps. @connor4312 let me know if you prefer a draft pull request for my changes; I was not sure yet since it's still more kind of a proposal than a "ready to merge change" at its state :)
Thanks for your work, that looks like a great start!
Once you had a look at my changes, maybe you guys have an idea how I could solve the following, ugly any casts?
There is, but you may not like it :smile:
type DomainMethods<D extends keyof Cdp.Api> = {
[M in keyof Cdp.Api[D]]: Cdp.Api[D][M] extends (params: infer P) => Promise<infer R>
? [P, R]
: never;
};
private async sendToCDP<
Domain extends keyof Cdp.Api,
Method extends keyof DomainMethods<Domain>,
Signature extends DomainMethods<Domain>[Method]
>(domain: Domain, method: Method, params: Signature[0]): Promise<Signature[1]> {
const agent = this.cdp[domain];
if (agent) {
const fn = agent[method];
if (typeof fn === 'function') {
return await fn(params);
} else {
throw new Error(`Unknown method for domain "${method}"`);
}
} else {
throw new Error(`Unknown domain "${domain}"`);
}
}
As far as I know there isn't a way to do that for the second case, however, since the overloaded on does not play nicely with this object mapping trick. I'm fine with any's here.
Feel free to put in a PR, that will give me write access and I can help put in tweaks to get this ready to ship 馃殺
Thank you for your input! I tried to do something similar, but obviously missed the infer part. Nice to have another example how infer can be applied 馃挭
As far as I know there isn't a way to do that for the second case, however, since the overloaded on does not play nicely with this object mapping trick. I'm fine with any's here.
Let's keep it simple then. Even with the suggested DomainMethods type, we would have to cast the strings received from the socket somewhere. Without introducing a proper parsing, we would not really have improved anything then.
Feel free to put in a PR, that will give me write access and I can help put in tweaks to get this ready to ship 馃殺
Nice! I just opened #964. Please let me know how you want to proceed and where I can jump in.
Thanks for your work and input, I've documented this here
fyi @swissmanu @hediet please be aware of this breaking change in the upcoming nightly https://github.com/microsoft/vscode-js-debug/issues/987
thx! will keep that in mind 馃憤
Most helpful comment
I would prefer a design where you don't actually proxy 'everything'. For example, you may not care about
scriptParsedevents for your use case and only request/response pairs andoutputevents. CDP is chatty and latency is important, I don't want to add extra work unnecessarily.