fix: detect externally opened tabs in --cdp mode (#1042)
* fix: detect externally opened tabs in --cdp mode (#1037) Tabs opened outside of agent-browser (e.g. by the user or another CDP client) were invisible to `tab list` because: 1. `Target.targetCreated` with chrome://newtab/ was filtered by `is_internal_chrome_target`, and the subsequent `targetInfoChanged` with the real URL could not update a target that was never tracked. 2. The background drain loop only ran when `request_tracking || har_recording` was active, so target events between commands were silently dropped from the broadcast channel. Fix: promote untracked targets in `targetInfoChanged` to new targets, run the background drain unconditionally (guarded by browser presence), and extract `apply_drained_events` to share target lifecycle processing (attach, domain filter, iframe sessions) between execute_command and the background drain. * refactor: clean up HashSet import and remove call-site duplication - Import HashSet alongside HashMap instead of using fully-qualified path - Replace duplicated drain+apply sequence in execute_command with drain_cdp_events_background call * style: apply cargo fmt --------- Co-authored-by: hyunjinee <leehj0110@kakao.com>
This commit is contained in:
@@ -3599,3 +3599,77 @@ async fn e2e_headers_case_insensitive_no_duplicates() {
|
||||
let resp = execute_command(&json!({ "id": "99", "action": "close" }), &mut state).await;
|
||||
assert_success(&resp);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Regression: externally opened tabs must appear in tab_list (#1037)
|
||||
//
|
||||
// When connected to Chrome (launched or via --cdp), a tab opened outside of
|
||||
// agent-browser (e.g. by the user or another CDP client) should be detected
|
||||
// and listed. Previously, chrome://newtab/ was filtered by
|
||||
// is_internal_chrome_target, and Target.targetInfoChanged for untracked
|
||||
// targets was silently ignored.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn e2e_externally_opened_tab_detected() {
|
||||
let mut state = DaemonState::new();
|
||||
|
||||
// Launch headless Chrome
|
||||
let resp = execute_command(
|
||||
&json!({ "id": "1", "action": "launch", "headless": true }),
|
||||
&mut state,
|
||||
)
|
||||
.await;
|
||||
assert_success(&resp);
|
||||
|
||||
// Verify initial tab count
|
||||
let resp = execute_command(&json!({ "id": "2", "action": "tab_list" }), &mut state).await;
|
||||
assert_success(&resp);
|
||||
let initial_count = get_data(&resp)["tabs"].as_array().unwrap().len();
|
||||
|
||||
// Simulate an external client opening a new tab via the browser-level CDP
|
||||
// session (no sessionId). This mirrors what happens when a user manually
|
||||
// opens a tab while agent-browser is connected via --cdp.
|
||||
let browser = state.browser.as_ref().expect("browser should be launched");
|
||||
let _: Value = browser
|
||||
.client
|
||||
.send_command(
|
||||
"Target.createTarget",
|
||||
Some(json!({ "url": "data:text/html,<h1>External Tab</h1>" })),
|
||||
None, // browser-level session
|
||||
)
|
||||
.await
|
||||
.expect("Target.createTarget should succeed");
|
||||
|
||||
// Give Chrome a moment to fire targetCreated / targetInfoChanged events
|
||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
|
||||
// Drain events by issuing tab_list — this triggers execute_command's
|
||||
// drain_cdp_events path which processes new and changed targets.
|
||||
let resp = execute_command(&json!({ "id": "3", "action": "tab_list" }), &mut state).await;
|
||||
assert_success(&resp);
|
||||
let tabs = get_data(&resp)["tabs"].as_array().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
tabs.len(),
|
||||
initial_count + 1,
|
||||
"Externally opened tab should appear in tab_list, got: {:?}",
|
||||
tabs,
|
||||
);
|
||||
|
||||
// Verify the new tab's URL is the data URL we navigated to
|
||||
let new_tab = tabs.iter().find(|t| {
|
||||
t["url"]
|
||||
.as_str()
|
||||
.is_some_and(|u| u.starts_with("data:text/html"))
|
||||
});
|
||||
assert!(
|
||||
new_tab.is_some(),
|
||||
"Should find the externally opened tab by URL, tabs: {:?}",
|
||||
tabs,
|
||||
);
|
||||
|
||||
let resp = execute_command(&json!({ "id": "99", "action": "close" }), &mut state).await;
|
||||
assert_success(&resp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user