Fix Chrome extensions not loading by forcing headed mode when extensions present (#652)

* Fix Chrome extensions not loading by forcing headed mode when extensions present

Fixes #640

* Restore wait_or_kill() and add tests for headless+extensions logic

Restore the ChromeProcess::wait_or_kill() method that was accidentally
removed. It is still referenced by BrowserProcess in browser.rs and is
needed for graceful shutdown / cookie persistence (PR #650).

Add unit tests verifying --headless=new is omitted when extensions are
present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix window-size leak in headed+extensions mode and remove unused channel option

- Skip --window-size=1280,720 when extensions force headed mode (native)
- Remove unexplained channel: 'chromium' from extensions launch path (TS)
- Add window-size assertion to existing extension test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Tate
2026-03-06 14:58:18 -06:00
committed by GitHub
co-authored by Claude Opus 4.6 ctate
parent f262ff1bf3
commit 68cebe5192
+55 -2
View File
@@ -120,7 +120,14 @@ fn build_chrome_args(options: &LaunchOptions) -> Result<ChromeArgs, String> {
"--use-mock-keychain".to_string(), "--use-mock-keychain".to_string(),
]; ];
if options.headless { let has_extensions = options
.extensions
.as_ref()
.map_or(false, |exts| !exts.is_empty());
// Extensions require headed mode in native Chrome (content scripts are not
// injected in headless mode). Skip --headless when extensions are loaded.
if options.headless && !has_extensions {
args.push("--headless=new".to_string()); args.push("--headless=new".to_string());
} }
@@ -163,7 +170,7 @@ fn build_chrome_args(options: &LaunchOptions) -> Result<ChromeArgs, String> {
.iter() .iter()
.any(|a| a.starts_with("--start-maximized") || a.starts_with("--window-size=")); .any(|a| a.starts_with("--start-maximized") || a.starts_with("--window-size="));
if !has_window_size && options.headless { if !has_window_size && options.headless && !has_extensions {
args.push("--window-size=1280,720".to_string()); args.push("--window-size=1280,720".to_string());
} }
@@ -764,6 +771,52 @@ mod tests {
} }
} }
#[test]
fn test_build_args_headless_with_extensions_skips_headless_flag() {
let opts = LaunchOptions {
headless: true,
extensions: Some(vec!["/tmp/my-ext".to_string()]),
..Default::default()
};
let result = build_chrome_args(&opts).unwrap();
assert!(
!result.args.iter().any(|a| a.contains("--headless")),
"headless flag should be omitted when extensions are present"
);
assert!(
!result.args.iter().any(|a| a.contains("--window-size")),
"window-size should be omitted when extensions force headed mode"
);
assert!(result
.args
.iter()
.any(|a| a.starts_with("--load-extension=")));
if let Some(ref dir) = result.temp_user_data_dir {
let _ = std::fs::remove_dir_all(dir);
}
}
#[test]
fn test_build_args_headed_with_extensions_no_headless_flag() {
let opts = LaunchOptions {
headless: false,
extensions: Some(vec!["/tmp/my-ext".to_string()]),
..Default::default()
};
let result = build_chrome_args(&opts).unwrap();
assert!(
!result.args.iter().any(|a| a.contains("--headless")),
"headless flag should not be present in headed mode"
);
assert!(result
.args
.iter()
.any(|a| a.starts_with("--load-extension=")));
if let Some(ref dir) = result.temp_user_data_dir {
let _ = std::fs::remove_dir_all(dir);
}
}
#[test] #[test]
fn test_chrome_process_drop_cleans_temp_dir() { fn test_chrome_process_drop_cleans_temp_dir() {
let dir = std::env::temp_dir().join(format!( let dir = std::env::temp_dir().join(format!(