Skip to main content

browser_control/launch/
chromium.rs

1//! Chromium-family browser launcher (Chrome, Edge, Chromium, Brave).
2
3use std::fs::File;
4use std::process::Stdio;
5
6use anyhow::{Context, Result};
7use tokio::process::Command;
8
9use crate::detect::{Engine, Installed};
10
11use super::{
12    allocate_free_port, background_after_launch, configure_session_detachment, wait_for_endpoint,
13    LaunchOpts, LaunchedHandle,
14};
15
16pub async fn launch(installed: &Installed, opts: LaunchOpts) -> Result<LaunchedHandle> {
17    let port = allocate_free_port().context("allocating CDP port")?;
18
19    if let Some(parent) = opts.profile_dir.parent() {
20        let _ = std::fs::create_dir_all(parent);
21    }
22    std::fs::create_dir_all(&opts.profile_dir)
23        .with_context(|| format!("creating profile dir {}", opts.profile_dir.display()))?;
24
25    let log_path = opts.profile_dir.join("browser.log");
26    let log_file =
27        File::create(&log_path).with_context(|| format!("creating {}", log_path.display()))?;
28    let log_clone = log_file
29        .try_clone()
30        .context("cloning log file handle for stderr")?;
31
32    let mut cmd = Command::new(&installed.executable);
33    cmd.arg(format!("--remote-debugging-port={port}"))
34        .arg(format!("--user-data-dir={}", opts.profile_dir.display()))
35        .arg("--no-first-run")
36        .arg("--no-default-browser-check")
37        .arg("--disable-component-update");
38    if opts.headless {
39        cmd.arg("--headless=new");
40    } else {
41        cmd.arg("--start-minimized");
42    }
43    cmd.arg("about:blank");
44
45    cmd.stdin(Stdio::null())
46        .stdout(Stdio::from(log_file))
47        .stderr(Stdio::from(log_clone))
48        .kill_on_drop(false);
49    configure_session_detachment(&mut cmd);
50
51    let mut child = cmd
52        .spawn()
53        .with_context(|| format!("spawning {}", installed.executable.display()))?;
54    let pid = child.id().context("child has no pid")?;
55
56    let endpoint = wait_for_endpoint(port, &mut child, &log_path).await?;
57    if !opts.headless {
58        background_after_launch(pid);
59    }
60
61    Ok(LaunchedHandle {
62        pid,
63        port,
64        endpoint,
65        engine: Engine::Cdp,
66        profile_dir: opts.profile_dir,
67        child: Some(child),
68    })
69}