Skip to content

Commit 7152cf5

Browse files
committed
fix: map main frame ids to page targets
1 parent 2747aaa commit 7152cf5

7 files changed

Lines changed: 192 additions & 33 deletions

File tree

pkg/bridge/bridge.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,59 @@ func (b *Bridge) latestContextForSession(cdpSessionID string) string {
202202
return b.latestCtx[jugglerSessionID]
203203
}
204204

205+
func (b *Bridge) cdpFrameIDForSession(cdpSessionID, frameID string) string {
206+
if frameID == "" {
207+
return frameID
208+
}
209+
info, ok := b.sessions.Get(cdpSessionID)
210+
if !ok {
211+
return frameID
212+
}
213+
return b.cdpFrameIDForInfo(info, frameID)
214+
}
215+
216+
func (b *Bridge) cdpFrameIDForJugglerSession(jugglerSessionID, frameID string) string {
217+
if frameID == "" {
218+
return frameID
219+
}
220+
if info, ok := b.sessions.GetByJugglerSession(jugglerSessionID); ok {
221+
return b.cdpFrameIDForInfo(info, frameID)
222+
}
223+
b.autoAttach.mu.Lock()
224+
pair, ok := b.autoAttach.pairs[jugglerSessionID]
225+
b.autoAttach.mu.Unlock()
226+
if ok {
227+
if info, ok := b.sessions.Get(pair.pageSessionID); ok {
228+
return b.cdpFrameIDForInfo(info, frameID)
229+
}
230+
}
231+
return frameID
232+
}
233+
234+
func (b *Bridge) cdpFrameIDForInfo(info *cdp.SessionInfo, frameID string) string {
235+
if info == nil || frameID == "" {
236+
return frameID
237+
}
238+
if info.Type == "page" && info.FrameID != "" && info.TargetID != "" && frameID == info.FrameID {
239+
return info.TargetID
240+
}
241+
return frameID
242+
}
243+
244+
func (b *Bridge) jugglerFrameIDForSession(cdpSessionID, frameID string) string {
245+
if frameID == "" {
246+
return frameID
247+
}
248+
info, ok := b.sessions.Get(cdpSessionID)
249+
if !ok {
250+
return frameID
251+
}
252+
if info.Type == "page" && info.FrameID != "" && info.TargetID != "" && frameID == info.TargetID {
253+
return info.FrameID
254+
}
255+
return frameID
256+
}
257+
205258
// emitEvent sends a CDP event to all connected clients.
206259
func (b *Bridge) emitEvent(method string, params interface{}, sessionID string) {
207260
var raw json.RawMessage

pkg/bridge/bridge_test.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,20 @@ func TestCallJuggler_SessionResolution(t *testing.T) {
279279

280280
func TestHandlePage_Navigate(t *testing.T) {
281281
b, mb := newTestBridge()
282+
b.sessions.Add(&cdp.SessionInfo{
283+
SessionID: "page-s1",
284+
TargetID: "target-1",
285+
FrameID: "mainframe-1",
286+
Type: "page",
287+
})
282288

283289
mb.SetResponse("", "Page.navigate", json.RawMessage(`{"navigationId":"nav-1","frameId":"frame-1"}`), nil)
284290

285291
msg := &cdp.Message{
286-
ID: 1,
287-
Method: "Page.navigate",
288-
Params: json.RawMessage(`{"url":"https://example.com"}`),
292+
ID: 1,
293+
Method: "Page.navigate",
294+
SessionID: "page-s1",
295+
Params: json.RawMessage(`{"url":"https://example.com"}`),
289296
}
290297

291298
result, cdpErr := b.handlePage(nil, msg)
@@ -304,6 +311,50 @@ func TestHandlePage_Navigate(t *testing.T) {
304311
}
305312
}
306313

314+
func TestHandlePage_Navigate_TranslatesMainFrameID(t *testing.T) {
315+
b, mb := newTestBridge()
316+
b.sessions.Add(&cdp.SessionInfo{
317+
SessionID: "page-s1",
318+
JugglerSessionID: "jug-page-1",
319+
TargetID: "target-1",
320+
FrameID: "mainframe-1",
321+
Type: "page",
322+
})
323+
324+
mb.SetResponse("jug-page-1", "Page.navigate", json.RawMessage(`{"navigationId":"nav-1","frameId":"mainframe-1"}`), nil)
325+
326+
msg := &cdp.Message{
327+
ID: 1,
328+
Method: "Page.navigate",
329+
SessionID: "page-s1",
330+
Params: json.RawMessage(`{"url":"https://example.com","frameId":"target-1"}`),
331+
}
332+
333+
result, cdpErr := b.handlePage(nil, msg)
334+
if cdpErr != nil {
335+
t.Fatalf("handlePage error: %s", cdpErr.Message)
336+
}
337+
338+
var res map[string]interface{}
339+
json.Unmarshal(result, &res)
340+
341+
if res["frameId"] != "target-1" {
342+
t.Errorf("frameId = %v, want target-1", res["frameId"])
343+
}
344+
345+
last, err := mb.LastCall()
346+
if err != nil {
347+
t.Fatalf("last call: %v", err)
348+
}
349+
var params map[string]interface{}
350+
if err := json.Unmarshal(last.Params, &params); err != nil {
351+
t.Fatalf("unmarshal params: %v", err)
352+
}
353+
if params["frameId"] != "mainframe-1" {
354+
t.Errorf("navigate frameId = %v, want mainframe-1", params["frameId"])
355+
}
356+
}
357+
307358
func TestHandlePage_NavigateInvalidParams(t *testing.T) {
308359
b, _ := newTestBridge()
309360

pkg/bridge/events.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ func (b *Bridge) SetupEventSubscriptions() {
242242
}
243243

244244
cdpSessionID := b.resolveCDPSession(jugglerSessionID)
245+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.FrameID)
245246

246247
// Skip intermediate about:blank navigations during reload/redirect.
247248
// Juggler emits navigation to about:blank before navigating to the real URL.
@@ -285,21 +286,21 @@ func (b *Bridge) SetupEventSubscriptions() {
285286

286287
// Emit lifecycle events in Chrome's order
287288
b.emitEvent("Page.lifecycleEvent", map[string]interface{}{
288-
"frameId": ev.FrameID,
289+
"frameId": cdpFrameID,
289290
"loaderId": loaderId,
290291
"name": "init",
291292
"timestamp": 0,
292293
}, cdpSessionID)
293294
b.emitEvent("Page.lifecycleEvent", map[string]interface{}{
294-
"frameId": ev.FrameID,
295+
"frameId": cdpFrameID,
295296
"loaderId": loaderId,
296297
"name": "commit",
297298
"timestamp": 0,
298299
}, cdpSessionID)
299300

300301
b.emitEvent("Page.frameNavigated", map[string]interface{}{
301302
"frame": map[string]interface{}{
302-
"id": ev.FrameID,
303+
"id": cdpFrameID,
303304
"url": ev.URL,
304305
"loaderId": loaderId,
305306
"securityOrigin": "",
@@ -324,6 +325,7 @@ func (b *Bridge) SetupEventSubscriptions() {
324325
}
325326

326327
cdpSessionID := b.resolveCDPSession(jugglerSessionID)
328+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.FrameID)
327329

328330
// Use the same loaderId as the navigation that triggered this event
329331
b.loaderMapMu.RLock()
@@ -339,13 +341,13 @@ func (b *Bridge) SetupEventSubscriptions() {
339341
"timestamp": 0,
340342
}, cdpSessionID)
341343
b.emitEvent("Page.lifecycleEvent", map[string]interface{}{
342-
"frameId": ev.FrameID,
344+
"frameId": cdpFrameID,
343345
"loaderId": loaderId,
344346
"name": "load",
345347
"timestamp": 0,
346348
}, cdpSessionID)
347349
b.emitEvent("Page.frameStoppedLoading", map[string]interface{}{
348-
"frameId": ev.FrameID,
350+
"frameId": cdpFrameID,
349351
}, cdpSessionID)
350352

351353
// NOTE: Isolated worlds are NOT re-emitted here.
@@ -355,7 +357,7 @@ func (b *Bridge) SetupEventSubscriptions() {
355357
"timestamp": 0,
356358
}, cdpSessionID)
357359
b.emitEvent("Page.lifecycleEvent", map[string]interface{}{
358-
"frameId": ev.FrameID,
360+
"frameId": cdpFrameID,
359361
"loaderId": loaderId,
360362
"name": "DOMContentLoaded",
361363
"timestamp": 0,
@@ -424,6 +426,8 @@ func (b *Bridge) SetupEventSubscriptions() {
424426
b.latestCtx[jugglerSessionID] = ev.ExecutionContextID
425427
b.latestCtxMu.Unlock()
426428

429+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.AuxData.FrameID)
430+
427431
b.emitEvent("Runtime.executionContextCreated", map[string]interface{}{
428432
"context": map[string]interface{}{
429433
"id": ctxID,
@@ -433,7 +437,7 @@ func (b *Bridge) SetupEventSubscriptions() {
433437
"auxData": map[string]interface{}{
434438
"isDefault": true,
435439
"type": "default",
436-
"frameId": ev.AuxData.FrameID,
440+
"frameId": cdpFrameID,
437441
},
438442
},
439443
}, cdpSessionID)
@@ -446,7 +450,7 @@ func (b *Bridge) SetupEventSubscriptions() {
446450
worlds := b.isolatedWorlds[cdpSessionID]
447451
b.isolatedWorldsMu.RUnlock()
448452

449-
frameID := ev.AuxData.FrameID
453+
frameID := cdpFrameID
450454
for _, w := range worlds {
451455
isoCtxID := b.nextCtxID()
452456
b.ctxMapMu.Lock()
@@ -570,10 +574,12 @@ func (b *Bridge) SetupEventSubscriptions() {
570574
}
571575

572576
cdpSessionID := b.resolveCDPSession(jugglerSessionID)
577+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.FrameID)
578+
cdpParentFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.ParentFrameID)
573579

574580
b.emitEvent("Page.frameAttached", map[string]interface{}{
575-
"frameId": ev.FrameID,
576-
"parentFrameId": ev.ParentFrameID,
581+
"frameId": cdpFrameID,
582+
"parentFrameId": cdpParentFrameID,
577583
}, cdpSessionID)
578584
})
579585

@@ -588,9 +594,10 @@ func (b *Bridge) SetupEventSubscriptions() {
588594
}
589595

590596
cdpSessionID := b.resolveCDPSession(jugglerSessionID)
597+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.FrameID)
591598

592599
b.emitEvent("Page.frameDetached", map[string]interface{}{
593-
"frameId": ev.FrameID,
600+
"frameId": cdpFrameID,
594601
"reason": "remove",
595602
}, cdpSessionID)
596603
})
@@ -657,6 +664,7 @@ func (b *Bridge) SetupEventSubscriptions() {
657664
}
658665

659666
cdpSessionID := b.resolveCDPSession(jugglerSessionID)
667+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.FrameID)
660668

661669
cdpHeaders := map[string]string{}
662670
for k, v := range ev.Headers {
@@ -698,7 +706,7 @@ func (b *Bridge) SetupEventSubscriptions() {
698706
"type": "other",
699707
},
700708
"type": resourceType,
701-
"frameId": ev.FrameID,
709+
"frameId": cdpFrameID,
702710
}, cdpSessionID)
703711
})
704712

@@ -719,6 +727,7 @@ func (b *Bridge) SetupEventSubscriptions() {
719727
}
720728

721729
cdpSessionID := b.resolveCDPSession(jugglerSessionID)
730+
cdpFrameID := b.cdpFrameIDForJugglerSession(jugglerSessionID, ev.FrameID)
722731

723732
b.emitEvent("Network.responseReceived", map[string]interface{}{
724733
"requestId": ev.RequestID,
@@ -739,7 +748,7 @@ func (b *Bridge) SetupEventSubscriptions() {
739748
"fromPrefetchCache": false,
740749
"securityState": "secure",
741750
},
742-
"frameId": ev.FrameID,
751+
"frameId": cdpFrameID,
743752
}, cdpSessionID)
744753
})
745754

@@ -927,6 +936,11 @@ func (b *Bridge) SetupEventSubscriptions() {
927936
}
928937
}
929938

939+
cdpFrameID := ev.FrameID
940+
if cdpSessionID != "" {
941+
cdpFrameID = b.cdpFrameIDForSession(cdpSessionID, ev.FrameID)
942+
}
943+
930944
log.Printf("[event] Browser.requestIntercepted → Fetch.requestPaused requestId=%s url=%s cdpSession=%s", ev.RequestID, url, cdpSessionID)
931945

932946
// Emit Network.requestWillBeSent BEFORE Fetch.requestPaused.
@@ -946,7 +960,7 @@ func (b *Bridge) SetupEventSubscriptions() {
946960
"wallTime": 0,
947961
"initiator": map[string]interface{}{"type": "other"},
948962
"type": resourceType,
949-
"frameId": ev.FrameID,
963+
"frameId": cdpFrameID,
950964
}, cdpSessionID)
951965

952966
b.emitEvent("Fetch.requestPaused", map[string]interface{}{
@@ -959,7 +973,7 @@ func (b *Bridge) SetupEventSubscriptions() {
959973
"initialPriority": "High",
960974
"referrerPolicy": "strict-origin-when-cross-origin",
961975
},
962-
"frameId": ev.FrameID,
976+
"frameId": cdpFrameID,
963977
"resourceType": resourceType,
964978
}, cdpSessionID)
965979
})

pkg/bridge/events_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,41 @@ func TestResolveCDPSession_NotFound(t *testing.T) {
5454
}
5555
}
5656

57+
func TestCDPFrameIDForJugglerSession_MainFrameUsesTargetID(t *testing.T) {
58+
b, _ := newTestBridge()
59+
b.sessions.Add(&cdp.SessionInfo{
60+
SessionID: "page-s1",
61+
JugglerSessionID: "jug-s1",
62+
TargetID: "target-1",
63+
FrameID: "mainframe-1",
64+
Type: "page",
65+
})
66+
67+
if got := b.cdpFrameIDForJugglerSession("jug-s1", "mainframe-1"); got != "target-1" {
68+
t.Fatalf("cdpFrameIDForJugglerSession(main frame) = %q, want target-1", got)
69+
}
70+
if got := b.cdpFrameIDForJugglerSession("jug-s1", "child-frame-1"); got != "child-frame-1" {
71+
t.Fatalf("cdpFrameIDForJugglerSession(child frame) = %q, want child-frame-1", got)
72+
}
73+
}
74+
75+
func TestJugglerFrameIDForSession_MainFrameUsesStoredFrameID(t *testing.T) {
76+
b, _ := newTestBridge()
77+
b.sessions.Add(&cdp.SessionInfo{
78+
SessionID: "page-s1",
79+
TargetID: "target-1",
80+
FrameID: "mainframe-1",
81+
Type: "page",
82+
})
83+
84+
if got := b.jugglerFrameIDForSession("page-s1", "target-1"); got != "mainframe-1" {
85+
t.Fatalf("jugglerFrameIDForSession(main frame) = %q, want mainframe-1", got)
86+
}
87+
if got := b.jugglerFrameIDForSession("page-s1", "child-frame-1"); got != "child-frame-1" {
88+
t.Fatalf("jugglerFrameIDForSession(child frame) = %q, want child-frame-1", got)
89+
}
90+
}
91+
5792
func TestSetupEventSubscriptions_AttachedToTarget(t *testing.T) {
5893
b, mb := newTestBridge()
5994
b.SetupEventSubscriptions()

0 commit comments

Comments
 (0)