Skip to main content

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}