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, 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}