fix: Juggler request interception event handling#4
Open
void0x14 wants to merge 8 commits into
Open
Conversation
…ntext routing When a subframe's execution context is destroyed, latestCtx was not cleared. This caused Runtime.evaluate (without contextId) to keep routing to the destroyed context, producing persistent 'Failed to find execution context' errors that never recovered. Fix: - executionContextDestroyed: clear latestCtx if it matches the destroyed context - executionContextsCleared: clear latestCtx for the session (navigation case) - Add test assertion verifying latestCtx is cleared after context destruction
BinaryMessage (opcode 0x02) was always sent regardless of compression state. CDP wire format is JSON text — engine recvWsTextAlloc only handles TEXT (0x01) and CONTINUATION (0x00) frames, causing WsFrameError. - Send: TextMessage when compress=false, BinaryMessage when compress=true - SendBatch: same fix SOURCE: Chrome DevTools Protocol — wire format is JSON text frames SOURCE: RFC 6455 Section 5.2 — opcode 0x01 = text, 0x02 = binary
When a subframe's execution context is destroyed, latestCtx is cleared (fix VulpineOS#1). But Juggler requires executionContextId in Runtime.evaluate. Without a fallback, foxbridge sends undefined → Juggler rejects. Add mainCtx map that tracks the main frame's execution context per session. Unlike latestCtx, mainCtx is updated on every main frame context creation (surviving navigation) and only cleared on executionContextsCleared (full navigation reset). Fallback chain: latestCtx → mainCtx → empty (Juggler error) This ensures Runtime.evaluate always has a valid contextId. Also fix binary framing: Send() now uses TextMessage (opcode 0x01) for uncompressed CDP frames instead of always BinaryMessage (0x02). Fixes VulpineOS#1
Instead of hardcoding type="undefined" when Juggler omits the type
field, infer the actual type from the JSON value:
- String value → type="string"
- Boolean value → type="boolean"
- Number value → type="number"
This fixes "Runtime.evaluate expected string, got type=undefined"
errors in CDP clients that check the type field.
SOURCE: Chrome DevTools Protocol — Runtime.evaluate returns {result:{type,value}}
…tIntercepted
Juggler does not emit Browser.requestIntercepted events. Instead, it uses
Network.requestWillBeSent with isIntercepted: true for intercepted requests.
- Remove Browser.requestIntercepted subscription (never fires)
- Add isIntercepted check in Network.requestWillBeSent handler
- Emit Fetch.requestPaused when isIntercepted is true
SOURCE: Juggler Protocol.js — Network.requestWillBeSent { isIntercepted: Boolean }
…Interception Browser.setRequestInterception only sets a flag on the browser context but does NOT enable request interception. Network.setRequestInterception is a PAGE-level handler that calls enableRequestInterception() on the page's NetworkObserver, which actually enables request interception. - Fetch.enable → Network.setRequestInterception (was Browser.setRequestInterception) - Fetch.disable → Network.setRequestInterception (was Browser.setRequestInterception) - Network.setRequestInterception passes through directly to Juggler SOURCE: Juggler PageHandler.js — Network.setRequestInterception calls enableRequestInterception SOURCE: Juggler BrowserHandler.js — Browser.setRequestInterception only sets a flag SOURCE: Juggler NetworkObserver.js — enableRequestInterception sets _requestInterceptionEnabled
Juggler's Network.setRequestInterception only accepts {enabled: Boolean}.
Extra parameters like browserContextId cause CDP error -32000.
SOURCE: Juggler Protocol.js — Network.setRequestInterception params: {enabled: Boolean}
Juggler sends headers as [{name:"Host",value:"github.com"}] but the
Network.requestWillBeSent handler expected map[string]string. This caused
json.Unmarshal to silently fail, preventing Fetch.requestPaused from
ever being emitted to CDP clients.
Parse headers as []struct{Name,Value string} and convert to map.
Also removes accidentally committed .opencode/ files and foxbridge-fixed
binary from earlier development.
SOURCE: Juggler NetworkObserver.js — _sendOnRequest sends headers array
SOURCE: Juggler Protocol.js — Network.requestWillBeSent type definitions
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.
Problem
foxbridge could not deliver
Fetch.requestPausedevents to CDP clients (e.g. doggystyle engine). This blocked request interception workflows like Arkose CAPTCHA audio URL detection.Root Causes (3 bugs found and fixed)
Bug 1: Wrong interception method
Browser.setRequestInterceptionwas used, but in Juggler this only sets a flag — it does NOT enable actual request interception.Fix: Use
Network.setRequestInterception({enabled: true})which callsenableRequestInterception()on the page's NetworkObserver.Bug 2: Wrong event subscription
foxbridge subscribed to
Browser.requestIntercepted, which does not exist in Juggler.Fix: Subscribe to
Network.requestWillBeSentand check theisInterceptedboolean field.Bug 3: Headers type mismatch (silent failure)
Juggler sends headers as
[{name:"Host",value:"github.com"}]array, but the handler expectedmap[string]string.json.Unmarshalsilently failed, causing the entire handler to return without emittingFetch.requestPaused.Fix: Parse headers as
[]struct{Name, Value string}and convert to map.Additional fixes
recvWsTextAlloconly handles TEXT (0x01). Fixed to use TextMessage for uncompressed frames.latestCtxwas not cleared onexecutionContextDestroyed, causing commands to be sent to destroyed contexts. AddedmainCtxfallback and proper cleanup.normalizeRuntimeResulthardcoded "undefined" for non-boolean/string/number results. Now infers type from JSON value.Testing
go test ./... -count=1)Fetch.requestPausedevents are now correctly received (62 events in test run)Commits
db73abe— clear latestCtx on executionContextDestroyed78e5bfe— use TextMessage for uncompressed CDP frames4629a89— mainCtx fallback when latestCtx cleared32ac70c— normalizeRuntimeResult infers type from JSON9c1297a— subscribe to Network.requestWillBeSentc1bf3f2— use Network.setRequestInterception545744e— remove browserContextId paramb26fe4d— parse Juggler headers as array