Complete Guide to DevTools Protocol Automation
The Chrome DevTools Protocol (CDP) provides powerful programmatic access to browser internals. Master CDP to build sophisticated automation, testing, and debugging tools.
What is the DevTools Protocol?
The Chrome DevTools Protocol is a JSON-RPC-based protocol that allows external tools to inspect, debug, and control Chrome-based browsers. It's the same protocol that powers Chrome DevTools itself.
Key Capabilities
- Page Control: Navigation, screenshots, PDF generation
- DOM Inspection: Query and modify the document
- Network: Monitor and intercept requests
- JavaScript: Execute code, set breakpoints
- Performance: Profiling and tracing
- Accessibility: Inspect accessibility tree
Getting Started
Enabling Remote Debugging
Launch Chrome with remote debugging enabled:
# Windows
chrome.exe --remote-debugging-port=9222
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
# Linux
google-chrome --remote-debugging-port=9222
Connecting to CDP
CDP communicates over WebSocket. First, get available targets:
// Get list of debuggable targets
GET http://localhost:9222/json
// Response:
[{
"id": "ABC123",
"type": "page",
"title": "New Tab",
"url": "chrome://newtab/",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/ABC123"
}]
Core Domains
Page Domain
Control page lifecycle and navigation:
// Navigate to URL
{ "method": "Page.navigate", "params": { "url": "https://example.com" } }
// Wait for load
{ "method": "Page.loadEventFired" } // Event
// Capture screenshot
{ "method": "Page.captureScreenshot", "params": { "format": "png" } }
// Returns: { "data": "base64-encoded-image" }
// Print to PDF
{ "method": "Page.printToPDF", "params": { "landscape": false } }
DOM Domain
Query and manipulate the document:
// Get document root
{ "method": "DOM.getDocument" }
// Returns: { "root": { "nodeId": 1, "nodeName": "#document", ... } }
// Query selector
{ "method": "DOM.querySelector",
"params": { "nodeId": 1, "selector": "#login-button" } }
// Get outer HTML
{ "method": "DOM.getOuterHTML", "params": { "nodeId": 5 } }
// Set attribute
{ "method": "DOM.setAttributeValue",
"params": { "nodeId": 5, "name": "class", "value": "active" } }
Network Domain
Monitor and intercept network activity:
// Enable network monitoring
{ "method": "Network.enable" }
// Events received:
{ "method": "Network.requestWillBeSent",
"params": { "requestId": "1", "request": { "url": "...", "method": "GET" } } }
{ "method": "Network.responseReceived",
"params": { "requestId": "1", "response": { "status": 200, ... } } }
// Enable request interception
{ "method": "Fetch.enable",
"params": { "patterns": [{ "urlPattern": "*" }] } }
// Modify intercepted request
{ "method": "Fetch.continueRequest",
"params": { "requestId": "1", "headers": [...] } }
Runtime Domain
Execute JavaScript and inspect objects:
// Execute expression
{ "method": "Runtime.evaluate",
"params": {
"expression": "document.title",
"returnByValue": true
}
}
// Returns: { "result": { "value": "Page Title" } }
// Call function on object
{ "method": "Runtime.callFunctionOn",
"params": {
"objectId": "...",
"functionDeclaration": "function() { return this.innerText; }"
}
}
Practical Examples
Full Page Screenshot
async function fullPageScreenshot(cdp) {
// Get page dimensions
const { contentSize } = await cdp.send('Page.getLayoutMetrics');
// Set viewport to full page
await cdp.send('Emulation.setDeviceMetricsOverride', {
width: contentSize.width,
height: contentSize.height,
deviceScaleFactor: 1,
mobile: false
});
// Capture screenshot
const { data } = await cdp.send('Page.captureScreenshot', {
format: 'png',
captureBeyondViewport: true
});
return Buffer.from(data, 'base64');
}
Network Request Logger
async function logRequests(cdp) {
await cdp.send('Network.enable');
cdp.on('Network.requestWillBeSent', (params) => {
console.log(`${params.request.method} ${params.request.url}`);
});
cdp.on('Network.responseReceived', (params) => {
console.log(` -> ${params.response.status}`);
});
}
Form Automation
async function fillForm(cdp, selector, value) {
// Get document
const { root } = await cdp.send('DOM.getDocument');
// Find element
const { nodeId } = await cdp.send('DOM.querySelector', {
nodeId: root.nodeId,
selector: selector
});
// Focus element
await cdp.send('DOM.focus', { nodeId });
// Type text
for (const char of value) {
await cdp.send('Input.dispatchKeyEvent', {
type: 'keyDown',
text: char
});
await cdp.send('Input.dispatchKeyEvent', {
type: 'keyUp',
text: char
});
}
}
CDP Libraries
Popular libraries that wrap CDP:
| Library | Language | Notes |
|---|---|---|
| Puppeteer | Node.js | Google's official high-level API |
| Playwright | Multi-language | Microsoft's cross-browser automation |
| chrome-remote-interface | Node.js | Low-level CDP client |
| PyCDP | Python | Python CDP client |
| chromedp | Go | Go CDP client |
Best Practices
- Wait for Events: Don't assume synchronous execution
- Handle Disconnections: Browser can close unexpectedly
- Session Management: Properly create and destroy targets
- Error Handling: CDP methods can fail for various reasons
- Resource Cleanup: Disable domains when done
CDP in Birds Engine
Tracy's Birds Engine provides native CDP integration with additional capabilities:
- Direct CDP access without launching separate Chrome instances
- Extended commands not available in standard CDP
- Integration with Birds Engine security and DLP features
- Managed browser lifecycle