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 if let Some(dir) = &opts.download_dir {
26 if let Err(e) = super::download_prefs::apply_chromium(&opts.profile_dir, dir) {
29 tracing::warn!(error = %e, "writing Chromium download prefs failed");
30 }
31 }
32
33 let log_path = opts.profile_dir.join("browser.log");
34 let log_file =
35 File::create(&log_path).with_context(|| format!("creating {}", log_path.display()))?;
36 let log_clone = log_file
37 .try_clone()
38 .context("cloning log file handle for stderr")?;
39
40 let mut cmd = Command::new(&installed.executable);
41 cmd.arg(format!("--remote-debugging-port={port}"))
42 .arg(format!("--user-data-dir={}", opts.profile_dir.display()))
43 .arg("--no-first-run")
44 .arg("--no-default-browser-check")
45 .arg("--disable-component-update");
46 if opts.headless {
47 cmd.arg("--headless=new");
48 } else {
49 cmd.arg("--start-minimized");
50 }
51 cmd.arg("about:blank");
52
53 cmd.stdin(Stdio::null())
54 .stdout(Stdio::from(log_file))
55 .stderr(Stdio::from(log_clone))
56 .kill_on_drop(false);
57 configure_session_detachment(&mut cmd);
58
59 let mut child = cmd
60 .spawn()
61 .with_context(|| format!("spawning {}", installed.executable.display()))?;
62 let pid = child.id().context("child has no pid")?;
63
64 let endpoint = wait_for_endpoint(port, &mut child, &log_path).await?;
65 if !opts.headless {
66 background_after_launch(pid);
67 }
68
69 Ok(LaunchedHandle {
70 pid,
71 port,
72 endpoint,
73 engine: Engine::Cdp,
74 profile_dir: opts.profile_dir,
75 child: Some(child),
76 })
77}