millipede_browser/provider.rs
1//! Integration surface for concrete browser providers.
2
3use std::fmt;
4
5use millipede_core::proxy::ProxyInfo;
6
7use crate::{BrowserError, BrowserPage};
8
9/// Process-level context applied when launching a browser.
10///
11/// # Provider contract
12///
13/// Providers **must apply both fields at launch**: [`Self::proxy`] is the process-level browser
14/// proxy because browser proxies are per-process rather than per-page, and [`Self::extra_args`]
15/// must be appended to the browser command line. Together with the default lifecycle hooks, this
16/// implements ROADMAP Phase 6's `pre_launch` proxy and launch-argument behavior.
17#[derive(Clone, Default)]
18#[non_exhaustive]
19pub struct LaunchContext {
20 /// Resolved process-level browser proxy.
21 pub proxy: Option<ProxyInfo>,
22 /// Additional arguments appended to the browser command line.
23 pub extra_args: Vec<String>,
24}
25
26impl LaunchContext {
27 /// Creates an empty launch context.
28 pub fn new() -> Self {
29 Self::default()
30 }
31
32 /// Sets the process-level browser proxy.
33 pub fn proxy(mut self, proxy: ProxyInfo) -> Self {
34 self.proxy = Some(proxy);
35 self
36 }
37
38 /// Replaces the additional browser command-line arguments.
39 pub fn extra_args(mut self, extra_args: Vec<String>) -> Self {
40 self.extra_args = extra_args;
41 self
42 }
43}
44
45impl fmt::Debug for LaunchContext {
46 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
47 formatter
48 .debug_struct("LaunchContext")
49 .field(
50 "proxy_endpoint",
51 &self
52 .proxy
53 .as_ref()
54 .map(|proxy| (&proxy.hostname, proxy.port)),
55 )
56 .field("extra_args", &self.extra_args)
57 .finish()
58 }
59}
60
61/// Concrete browser backend used by a browser pool.
62///
63/// This deliberately follows ADR-0006 rather than duplicating the page methods sketched in
64/// INTERFACE ยง12.1. Page-level operations (`goto`, cookies, evaluation, and related methods) live
65/// solely on [`BrowserPage`]. [`Self::Page`] is `BrowserPage + Clone`: the pool retains a concrete
66/// clone for close bookkeeping while handing users an `Arc<dyn BrowserPage>`.
67#[async_trait::async_trait]
68pub trait BrowserProvider: Send + Sync + 'static {
69 /// Provider-native launched browser handle.
70 type Browser: Send + Sync + 'static;
71 /// Provider-native page adapter used for erasure and close bookkeeping.
72 type Page: BrowserPage + Clone;
73 /// Provider-specific browser launch options.
74 type LaunchOptions: Default + Clone + Send + Sync + 'static;
75
76 /// Launches a browser and applies every field in `ctx`.
77 async fn launch(
78 &self,
79 opts: Self::LaunchOptions,
80 ctx: &LaunchContext,
81 ) -> Result<Self::Browser, BrowserError>;
82
83 /// Creates a new page in `browser`.
84 async fn new_page(&self, browser: &Self::Browser) -> Result<Self::Page, BrowserError>;
85
86 /// Closes a page owned by the provider.
87 async fn close_page(&self, page: Self::Page) -> Result<(), BrowserError>;
88
89 /// Closes the browser and reaps its child process.
90 ///
91 /// Implementations must perform close-and-wait shutdown so browser child processes cannot
92 /// become zombies. Drop-based termination is only a fallback.
93 async fn close_browser(&self, browser: Self::Browser) -> Result<(), BrowserError>;
94}