browser_control/launch/
chromium.rs1use 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, configure_session_detachment, wait_for_endpoint, LaunchOpts, LaunchedHandle,
13};
14
15pub async fn launch(installed: &Installed, opts: LaunchOpts) -> Result<LaunchedHandle> {
16 let port = allocate_free_port().context("allocating CDP port")?;
17
18 if let Some(parent) = opts.profile_dir.parent() {
19 let _ = std::fs::create_dir_all(parent);
20 }
21 std::fs::create_dir_all(&opts.profile_dir)
22 .with_context(|| format!("creating profile dir {}", opts.profile_dir.display()))?;
23
24 let log_path = opts.profile_dir.join("browser.log");
25 let log_file =
26 File::create(&log_path).with_context(|| format!("creating {}", log_path.display()))?;
27 let log_clone = log_file
28 .try_clone()
29 .context("cloning log file handle for stderr")?;
30
31 let mut cmd = Command::new(&installed.executable);
32 cmd.arg(format!("--remote-debugging-port={port}"))
33 .arg(format!("--user-data-dir={}", opts.profile_dir.display()))
34 .arg("--no-first-run")
35 .arg("--no-default-browser-check")
36 .arg("--disable-component-update");
37 if opts.headless {
38 cmd.arg("--headless=new");
39 }
40 cmd.arg("about:blank");
41
42 cmd.stdin(Stdio::null())
43 .stdout(Stdio::from(log_file))
44 .stderr(Stdio::from(log_clone))
45 .kill_on_drop(false);
46 configure_session_detachment(&mut cmd);
47
48 let mut child = cmd
49 .spawn()
50 .with_context(|| format!("spawning {}", installed.executable.display()))?;
51 let pid = child.id().context("child has no pid")?;
52
53 let endpoint = wait_for_endpoint(port, &mut child, &log_path).await?;
54
55 Ok(LaunchedHandle {
56 pid,
57 port,
58 endpoint,
59 engine: Engine::Cdp,
60 profile_dir: opts.profile_dir,
61 child: Some(child),
62 })
63}