diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index d62341a77..686449850 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -640,6 +640,7 @@ impl DaemonState { .await; if let Ok(attach) = attach_result { let _ = mgr.enable_domains_pub(&attach.session_id).await; + mgr.apply_session_settings(&attach.session_id, None).await; // Install domain filter on new pages let df = self.domain_filter.read().await; @@ -3928,37 +3929,9 @@ async fn handle_recording_start(cmd: &Value, state: &mut DaemonState) -> Result< let new_session_id = attach_result.session_id.clone(); mgr.enable_domains_pub(&new_session_id).await?; - // Re-apply download behavior to the recording context. - // Without this, downloads in the recording context are silently dropped - // because Browser.setDownloadBehavior at launch only applies to the default context. - if let Some(ref dl_path) = mgr.download_path { - let _ = mgr - .client - .send_command( - "Browser.setDownloadBehavior", - Some(json!({ - "behavior": "allow", - "downloadPath": dl_path, - "browserContextId": context_id, - "eventsEnabled": true - })), - None, - ) - .await; - } - - // Re-apply HTTPS error ignore to the recording context. - // Security.setIgnoreCertificateErrors at launch only applies to the session it was sent on. - if mgr.ignore_https_errors { - let _ = mgr - .client - .send_command( - "Security.setIgnoreCertificateErrors", - Some(json!({ "ignore": true })), - Some(&new_session_id), - ) - .await; - } + // Re-apply all session-scoped settings to the recording context. + mgr.apply_session_settings(&new_session_id, Some(&context_id)) + .await; // Transfer cookies to new context if let Some(ref cr) = cookies_result { @@ -5882,6 +5855,9 @@ async fn handle_window_new(cmd: &Value, state: &mut DaemonState) -> Result, /// Whether to ignore HTTPS certificate errors, re-applied to new contexts (e.g., recording) pub ignore_https_errors: bool, + /// Stored user-agent override from launch options, re-applied to new contexts (e.g., recording) + pub user_agent: Option, + /// Stored color-scheme override from launch options, re-applied to new contexts (e.g., recording) + pub color_scheme: Option, /// Origins visited during this session, used by save_state to collect cross-origin localStorage. visited_origins: HashSet, } @@ -276,6 +280,8 @@ impl BrowserManager { default_timeout_ms: 25_000, download_path: download_path.clone(), ignore_https_errors, + user_agent: user_agent.clone(), + color_scheme: color_scheme.clone(), visited_origins: HashSet::new(), }; manager.discover_and_attach_targets().await?; @@ -283,52 +289,66 @@ impl BrowserManager { }; let session_id = manager.active_session_id()?.to_string(); + manager.apply_session_settings(&session_id, None).await; - if ignore_https_errors { - let _ = manager + Ok(manager) + } + + /// Apply all session-scoped CDP settings to the given session. + /// + /// Called after initial target discovery in `launch()` and after attaching to + /// new sessions (e.g., recording contexts in `handle_recording_start`). + /// + /// Any new session-scoped CDP setting should be added here so it is + /// automatically applied to every new session/context. + /// + /// `browser_context_id` is required for settings like `Browser.setDownloadBehavior` + /// that are scoped to a browser context rather than a session. + pub async fn apply_session_settings(&self, session_id: &str, browser_context_id: Option<&str>) { + if self.ignore_https_errors { + let _ = self .client .send_command( "Security.setIgnoreCertificateErrors", Some(json!({ "ignore": true })), - Some(&session_id), + Some(session_id), ) .await; } - if let Some(ref ua) = user_agent { - let _ = manager + if let Some(ref ua) = self.user_agent { + let _ = self .client .send_command( "Emulation.setUserAgentOverride", Some(json!({ "userAgent": ua })), - Some(&session_id), + Some(session_id), ) .await; } - if let Some(ref scheme) = color_scheme { - let _ = manager + if let Some(ref scheme) = self.color_scheme { + let _ = self .client .send_command( "Emulation.setEmulatedMedia", Some(json!({ "features": [{ "name": "prefers-color-scheme", "value": scheme }] })), - Some(&session_id), + Some(session_id), ) .await; } - if let Some(ref path) = download_path { - let _ = manager + if let Some(ref path) = self.download_path { + let mut params = json!({ "behavior": "allow", "downloadPath": path }); + if let Some(ctx_id) = browser_context_id { + params["browserContextId"] = json!(ctx_id); + params["eventsEnabled"] = json!(true); + } + let _ = self .client - .send_command( - "Browser.setDownloadBehavior", - Some(json!({ "behavior": "allow", "downloadPath": path })), - None, - ) + .send_command("Browser.setDownloadBehavior", Some(params), None) .await; } - - Ok(manager) } pub async fn connect_cdp(url: &str) -> Result { @@ -364,6 +384,8 @@ impl BrowserManager { default_timeout_ms: 25_000, download_path: None, ignore_https_errors: false, + user_agent: None, + color_scheme: None, visited_origins: HashSet::new(), }; @@ -810,6 +832,8 @@ impl BrowserManager { }); self.active_page_index = 0; self.enable_domains(&attach_result.session_id).await?; + self.apply_session_settings(&attach_result.session_id, None) + .await; Ok(()) } @@ -873,6 +897,7 @@ impl BrowserManager { .await?; self.enable_domains(&attach.session_id).await?; + self.apply_session_settings(&attach.session_id, None).await; let index = self.pages.len(); self.pages.push(PageInfo { @@ -1369,6 +1394,8 @@ async fn initialize_lightpanda_manager( default_timeout_ms: 25_000, download_path: None, ignore_https_errors: false, + user_agent: None, + color_scheme: None, visited_origins: HashSet::new(), };