Add programmatic CDP message passing API for BrowserView#148
Open
uxfreak wants to merge 5 commits intoblackboardsh:mainfrom
Open
Add programmatic CDP message passing API for BrowserView#148uxfreak wants to merge 5 commits intoblackboardsh:mainfrom
uxfreak wants to merge 5 commits intoblackboardsh:mainfrom
Conversation
… API
Add BrowserView.cdpSend() / cdpOn() / cdpOnAll() methods that enable
sending CDP commands and receiving events without a remote debugging port.
Uses CEF's built-in CefBrowserHost::SendDevToolsMessage() and
AddDevToolsMessageObserver() APIs (stable since CEF 86+), which work
entirely in-process — no network port, no security exposure.
This enables embedding apps to programmatically:
- Capture screenshots (Page.captureScreenshot)
- Evaluate JavaScript (Runtime.evaluate)
- Inspect DOM (DOM.getDocument)
- Monitor network (Network.enable)
- Access the full CDP protocol surface
Implementation:
- New CefDevToolsMessageObserver subclass (ElectrobunDevToolsObserver)
- 4 new extern "C" FFI exports (setDevToolsCDPCallbacks,
webviewSendDevToolsMessage, webviewAddDevToolsObserver,
webviewRemoveDevToolsObserver)
- Shared callback types header (devtools_cdp.h)
- Promise-based TypeScript API on BrowserView with event subscriptions
- Stub implementations for Windows and Linux (macOS fully implemented)
Usage:
const view = mainWindow.webview;
view.cdpAttach();
const result = await view.cdpSend('Runtime.evaluate', {
expression: 'document.title'
});
view.cdpOn('Page.loadEventFired', (params) => { ... });
…wserView layout APIs Sandboxed views now bypass the HTML response filter and preload script injection in CEF. This prevents SIGTRAP crashes when CDP DevTools agent is attached — the response filter was conflicting with CDP's internal plumbing on navigation events. Also adds setFrame(), remove(), and setHidden() public methods to BrowserView for programmatic layout management (split-pane UIs).
CloseBrowser(false) sends [window performClose:] to the host NSWindow, which closes the entire window when multiple BrowserViews share it. Replace with DetachBrowser() — removes from handler tracking list and releases the reference without triggering the window close lifecycle.
Bun FFI silently drops boolean parameters at position 19+ in function calls with many arguments. The sandbox flag (the last param of initWebview) was always arriving as false in the native layer, causing sandboxed BrowserViews to get response filters that crash with SIGTRAP when CDP DevTools agent is attached. Move sandbox into the existing setNextWebviewFlags pre-set mechanism (which already worked around this for startTransparent/startPassthrough) so it's reliably passed to all three platforms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…P crashes JSCallback with threadsafe:true uses TCC-generated trampolines that lack ARM64 PAC instructions, causing intermittent SIGTRAP crashes when CEF calls back from its UI thread. This replaces the callback approach with thread-safe std::deque queues polled from JS via setInterval(1ms). C++ side: cdpEnableQueue(), cdpDequeueResult(), cdpDequeueEvent() with packed binary buffers. JS side: 1ms poll loop draining both queues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a TypeScript API on
BrowserViewfor sending Chrome DevTools Protocol commands and receiving events, using CEF's built-inSendDevToolsMessage/AddDevToolsMessageObserverAPIs. Includes follow-up fixes for ARM64 stability, sandbox safety, and BrowserView layout management.This works entirely in-process — no remote debugging port required, no network exposure.
Motivation
Apps embedding webviews often need programmatic access to CDP for tasks like capturing screenshots, evaluating JavaScript, inspecting the DOM, or monitoring network activity. The current approach requires enabling
remote-debugging-portvia chromium flags, which opens a network port that's unsuitable for production.CEF has supported in-process CDP message passing since CEF 86+ via
CefBrowserHost::SendDevToolsMessage()andAddDevToolsMessageObserver(). This PR exposes those APIs through ElectroBun's FFI layer.API
BrowserView Layout APIs
Commits
1.
feat: add programmatic CDP message passing APINative layer (macOS):
ElectrobunDevToolsObserver—CefDevToolsMessageObserversubclass that routes method results and events to Bun via threadsafe callbackscdpFreeBuffer()for deterministic cleanup from JS after readingsetDevToolsCDPCallbacks,webviewSendDevToolsMessage,webviewAddDevToolsObserver,webviewRemoveDevToolsObserver,cdpFreeBufferFFI layer (
native.ts):toArrayBufferfor safe pointer-to-string conversioncdpAttach()call)TypeScript API (
BrowserView.ts):2.
fix: replace JSCallback with poll-based CDP queue to eliminate SIGTRAP crashesProblem: JSCallback with
threadsafe: trueuses TCC-generated trampolines that lack ARM64 PAC (Pointer Authentication Code) instructions. When CEF calls back from its UI thread on Apple Silicon, this causes intermittent SIGTRAP crashes.Solution: Replace callback-based delivery with thread-safe
std::dequequeues (mutex-protected) polled from JavaScript at 1ms intervals.CDPQueuedResult,CDPQueuedEventcdpEnableQueue,cdpDequeueResult,cdpDequeueEvent,cdpResultQueueSize,cdpEventQueueSize,cdpFreeBuffersetInterval(cdpPollTick, 1)drains both queues3.
fix: detach CEF browser on view removal instead of CloseBrowserCalling
CloseBrowser()on view removal caused cleanup order issues. Changed towebviewRemove()with proper observer detachment for safe lifecycle management.4.
fix: pass sandbox flag via setNextWebviewFlags to avoid FFI param lossBun FFI has parameter count limits that were silently dropping the
sandboxboolean. Fixed by usingsetNextWebviewFlags()to pass the flag beforeinitWebview().5.
feat: skip response filter + preload for sandboxed CEF views, add BrowserView layout APIssetFrame(),setHidden(),remove()Files Changed
src/native/shared/devtools_cdp.hsrc/native/macos/nativeWrapper.mmsrc/native/win/nativeWrapper.cppsrc/native/linux/nativeWrapper.cppsrc/bun/proc/native.tscdpNativeexportsrc/bun/core/BrowserView.tscdpAttach/Detach/Send/On/OnAll,setFrame,setHidden,removeWindows/Linux: Stub implementations that allow the FFI layer to load without link errors. The same CEF APIs (
SendDevToolsMessage,AddDevToolsMessageObserver) are available on all platforms — the macOS implementation serves as reference.Testing
Tested in a CEF-based ElectroBun app on macOS arm64 (Apple Silicon M-series). Verified:
Runtime.evaluate("document.title")→ correct string resultDOM.getDocument({depth: 1})→ full DOM tree with node IDsPage.captureScreenshot({format: "png"})→ valid base64 PNG (576KB)Runtime.evaluate("2 + 2")→{type: "number", value: 4}setFrame()/setHidden()/remove()lifecycle with proper CDP observer cleanup