After some false starts, my ShowDevToolsAsync
extension method shown below seems to be working reliably.
Note I'm using a modification of https://stackoverflow.com/a/7809413/3063273 to detect when DevTools has finished loading. I'm not sure what a more reliable alternative would be. Perhaps you could hook into ILoadHandler.OnFrameLoadEnd
like the comments to the OP suggest and work from there. I tried that path but didn't get far enough down it before I was able to get this solution working which I think is more direct and obvious.
Important: your instance of ChromiumWebBrowser
needs to already have loaded a web page (or at least started loading one), otherwise DevTools
will never hook into it but will just sit there with this message:

If you want to ensure that DevTools is up before loading your page (for example to guarantee that its Javascript debugger is ready to catch even the earliest errors), then make your ChromiumWebBrowser
load a dummy page, like <html>Loading DevTools...</html>
, then wait for DevTools to come up, then load your page.
using System;
using System.Threading;
using System.Threading.Tasks;
using CefSharp;
using CefSharp.WinForms;
public static class ChromiumWebBrowserExtensions
{
public static async Task ShowDevToolsAsync(
this ChromiumWebBrowser browser,
CancellationToken cancellationToken)
{
browser.ShowDevTools();
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
// Detect if console is open
var result = await browser.EvaluateScriptAsync(@"
(function(){
// Inspired by https://stackoverflow.com/a/7809413/3063273
var detector = function(){};
detector.isOpened = false;
detector.toString = function(){
this.isOpened = true;
};
console.log('%c', detector);
return detector.isOpened;
})()
", timeout: TimeSpan.FromSeconds(1));
if (!result.Success)
{
throw new Exception(result.Message);
}
if (!(result.Result is bool isOpened))
continue;
if (isOpened)
break;
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
}
}
}
Example usage (using an additional extension method I don't show above, but you should get the gist):
public async Task DemonstrateAsync(ChromiumWebBrowser browser, CancellationToken cancellationToken)
{
// Give DevTools something to hook into
await browser.LoadDataUrlAsync("<html>Waiting for DevTools to load...</html>", cancellationToken);
// Show DevTools and wait until it's up
await browser.ShowDevToolsAsync(cancellationToken);
// Give the Javascript debugger a breakpoint
await browser.LoadDataUrlAsync("<html><script>debugger;</script></html>", cancellationToken);
}
Edit: added async delay to reduce the iteration count of the while
loop. 100ms is an arbitrary number that for me reduced the iteration count from >300 to ~3.