playwright/imp/
browser_type.rs

1use crate::imp::{
2    browser::Browser,
3    browser_context::BrowserContext,
4    core::*,
5    prelude::*,
6    utils::{BrowserChannel, ColorScheme, Geolocation, HttpCredentials, ProxySettings, Viewport},
7};
8
9#[derive(Debug)]
10pub(crate) struct BrowserType {
11    channel: ChannelOwner,
12    name: String,
13    executable: PathBuf,
14}
15
16impl BrowserType {
17    pub(crate) fn try_new(channel: ChannelOwner) -> Result<Self, Error> {
18        let Initializer { name, executable } = serde_json::from_value(channel.initializer.clone())?;
19        Ok(Self {
20            channel,
21            name,
22            executable,
23        })
24    }
25
26    pub(crate) fn name(&self) -> &str {
27        &self.name
28    }
29
30    pub(crate) fn executable(&self) -> &Path {
31        &self.executable
32    }
33
34    pub(crate) async fn launch(
35        &self,
36        args: LaunchArgs<'_, '_, '_>,
37    ) -> Result<Weak<Browser>, Arc<Error>> {
38        let res = send_message!(self, "launch", args);
39        let guid = only_guid(&res)?;
40        let b = get_object!(self.context()?.lock().unwrap(), guid, Browser)?;
41        Ok(b)
42    }
43
44    pub(crate) async fn launch_persistent_context(
45        &self,
46        args: LaunchPersistentContextArgs<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_>,
47    ) -> Result<Weak<BrowserContext>, Arc<Error>> {
48        let res = send_message!(self, "launchPersistentContext", args);
49        let guid = only_guid(&res)?;
50        let b = get_object!(self.context()?.lock().unwrap(), guid, BrowserContext)?;
51        Ok(b)
52    }
53
54    pub(crate) async fn connect_over_cdp(
55        &self,
56        args: ConnectOverCdpArgs<'_>,
57    ) -> ArcResult<Weak<Browser>> {
58        let res = send_message!(self, "connectOverCDP", args);
59        #[derive(Deserialize)]
60        #[serde(rename_all = "camelCase")]
61        struct Response {
62            browser: OnlyGuid,
63            default_context: Option<OnlyGuid>,
64        }
65        let Response {
66            browser,
67            default_context,
68        } = serde_json::from_value((*res).clone()).map_err(Error::Serde)?;
69        let browser = get_object!(self.context()?.lock().unwrap(), &browser.guid, Browser)?;
70        let arc_browser = upgrade(&browser)?;
71        arc_browser.set_is_remote_true();
72        if let Some(OnlyGuid { guid }) = default_context {
73            let default_context =
74                get_object!(self.context()?.lock().unwrap(), &guid, BrowserContext)?;
75            let arc_context = upgrade(&default_context)?;
76            arc_browser.push_context(default_context);
77            arc_context.set_browser(browser.clone());
78        }
79        Ok(browser)
80    }
81
82    pub(crate) async fn connect(&self, args: ConnectArgs<'_>) -> ArcResult<Weak<Browser>> {
83        todo!()
84    }
85}
86
87#[skip_serializing_none]
88#[skip_serializing_none]
89#[derive(Debug, Serialize)]
90#[serde(rename_all = "camelCase")]
91pub(crate) struct LaunchArgs<'a, 'b, 'c> {
92    #[serde(rename = "executablePath")]
93    pub(crate) executable: Option<&'a Path>,
94    pub(crate) args: Option<&'b [String]>,
95    pub(crate) ignore_all_default_args: Option<bool>,
96    #[serde(rename = "handleSIGINT")]
97    pub(crate) handle_sigint: Option<bool>,
98    #[serde(rename = "handleSIGTERM")]
99    pub(crate) handle_sigterm: Option<bool>,
100    #[serde(rename = "handleSIGHUP")]
101    pub(crate) handle_sighup: Option<bool>,
102    pub(crate) timeout: Option<f64>,
103    pub(crate) devtools: Option<bool>,
104    pub(crate) proxy: Option<ProxySettings>,
105    #[serde(rename = "downloadsPath")]
106    pub(crate) downloads: Option<&'c Path>,
107    #[serde(rename = "slowMo")]
108    pub(crate) slowmo: Option<f64>,
109    pub(crate) env: Option<Map<String, Value>>,
110    pub(crate) headless: Option<bool>,
111    pub(crate) chromium_sandbox: Option<bool>,
112    pub(crate) firefox_user_prefs: Option<Map<String, Value>>,
113    pub(crate) channel: Option<BrowserChannel>,
114}
115
116impl<'a, 'b, 'c> Default for LaunchArgs<'a, 'b, 'c> {
117    fn default() -> Self {
118        Self {
119            executable: None,
120            args: None,
121            ignore_all_default_args: None,
122            handle_sigint: None,
123            handle_sigterm: None,
124            handle_sighup: None,
125            timeout: Some(30000.0),
126            devtools: None,
127            proxy: None,
128            downloads: None,
129            slowmo: None,
130            env: None,
131            headless: None,
132            chromium_sandbox: None,
133            firefox_user_prefs: None,
134            channel: None,
135        }
136    }
137}
138
139impl RemoteObject for BrowserType {
140    fn channel(&self) -> &ChannelOwner {
141        &self.channel
142    }
143    fn channel_mut(&mut self) -> &mut ChannelOwner {
144        &mut self.channel
145    }
146}
147
148#[derive(Debug, Deserialize)]
149#[serde(rename_all = "camelCase")]
150struct Initializer {
151    name: String,
152    #[serde(rename = "executablePath")]
153    executable: PathBuf,
154}
155
156// launch args | context args | {user_data_dir: }
157#[skip_serializing_none]
158#[derive(Debug, Serialize)]
159#[serde(rename_all = "camelCase")]
160pub(crate) struct LaunchPersistentContextArgs<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k> {
161    user_data_dir: &'a Path,
162    sdk_language: &'static str,
163
164    #[serde(rename = "executablePath")]
165    pub(crate) executable: Option<&'b Path>,
166    pub(crate) args: Option<&'c [String]>,
167    pub(crate) ignore_all_default_args: Option<bool>,
168    #[serde(rename = "handleSIGINT")]
169    pub(crate) handle_sigint: Option<bool>,
170    #[serde(rename = "handleSIGTERM")]
171    pub(crate) handle_sigterm: Option<bool>,
172    #[serde(rename = "handleSIGHUP")]
173    pub(crate) handle_sighup: Option<bool>,
174    pub(crate) timeout: Option<f64>,
175    pub(crate) env: Option<Map<String, Value>>,
176    pub(crate) headless: Option<bool>,
177    pub(crate) devtools: Option<bool>,
178    pub(crate) proxy: Option<ProxySettings>,
179    #[serde(rename = "downloadsPath")]
180    pub(crate) downloads: Option<&'d Path>,
181    #[serde(rename = "slowMo")]
182    pub(crate) slowmo: Option<f64>,
183
184    pub(crate) viewport: Option<Option<Viewport>>,
185    pub(crate) screen: Option<Viewport>,
186    pub(crate) no_viewport: Option<bool>,
187    #[serde(rename = "ignoreHTTPSErrors")]
188    pub(crate) ignore_https_errors: Option<bool>,
189    #[serde(rename = "javaScriptEnabled")]
190    pub(crate) js_enabled: Option<bool>,
191    #[serde(rename = "bypassCSP")]
192    pub(crate) bypass_csp: Option<bool>,
193    pub(crate) user_agent: Option<&'e str>,
194    pub(crate) locale: Option<&'f str>,
195    pub(crate) timezone_id: Option<&'g str>,
196    pub(crate) geolocation: Option<Geolocation>,
197    pub(crate) permissions: Option<&'h [String]>,
198    #[serde(rename = "extraHTTPHeaders")]
199    pub(crate) extra_http_headers: Option<HashMap<String, String>>,
200    pub(crate) offline: Option<bool>,
201    pub(crate) http_credentials: Option<&'i HttpCredentials>,
202    pub(crate) device_scale_factor: Option<f64>,
203    pub(crate) is_mobile: Option<bool>,
204    pub(crate) has_touch: Option<bool>,
205    pub(crate) color_scheme: Option<ColorScheme>,
206    pub(crate) accept_downloads: Option<bool>,
207    pub(crate) chromium_sandbox: Option<bool>,
208    pub(crate) record_video: Option<RecordVideo<'j>>,
209    pub(crate) record_har: Option<RecordHar<'k>>,
210
211    pub(crate) channel: Option<BrowserChannel>,
212}
213
214#[skip_serializing_none]
215#[derive(Debug, Serialize, Clone)]
216#[serde(rename_all = "camelCase")]
217pub struct RecordVideo<'a> {
218    pub dir: &'a Path,
219    pub size: Option<Viewport>,
220}
221
222#[skip_serializing_none]
223#[derive(Debug, Serialize, Clone)]
224#[serde(rename_all = "camelCase")]
225pub struct RecordHar<'a> {
226    pub path: &'a Path,
227    pub omit_content: Option<bool>,
228}
229
230impl<'a> LaunchPersistentContextArgs<'a, '_, '_, '_, '_, '_, '_, '_, '_, '_, '_> {
231    pub(crate) fn new(user_data_dir: &'a Path) -> Self {
232        let sdk_language = "rust";
233        Self {
234            user_data_dir,
235            sdk_language,
236            executable: None,
237            args: None,
238            ignore_all_default_args: None,
239            handle_sigint: None,
240            handle_sigterm: None,
241            handle_sighup: None,
242            timeout: Some(30000.0),
243            env: None,
244            headless: None,
245            devtools: None,
246            proxy: None,
247            downloads: None,
248            slowmo: None,
249            viewport: None,
250            screen: None,
251            no_viewport: None,
252            ignore_https_errors: None,
253            js_enabled: None,
254            bypass_csp: None,
255            user_agent: None,
256            locale: None,
257            timezone_id: None,
258            geolocation: None,
259            permissions: None,
260            extra_http_headers: None,
261            offline: None,
262            http_credentials: None,
263            device_scale_factor: None,
264            is_mobile: None,
265            has_touch: None,
266            color_scheme: None,
267            accept_downloads: None,
268            chromium_sandbox: None,
269            record_video: None,
270            record_har: None,
271            channel: None,
272        }
273    }
274}
275
276#[skip_serializing_none]
277#[derive(Debug, Serialize)]
278#[serde(rename_all = "camelCase")]
279pub(crate) struct ConnectArgs<'a> {
280    ws_endpoint: &'a str,
281    pub(crate) timeout: Option<f64>,
282    #[serde(rename = "slowMo")]
283    pub(crate) slowmo: Option<f64>,
284}
285
286impl<'a> ConnectArgs<'a> {
287    pub(crate) fn new(ws_endpoint: &'a str) -> Self {
288        Self {
289            ws_endpoint,
290            timeout: Some(30000.0),
291            slowmo: None,
292        }
293    }
294}
295
296#[skip_serializing_none]
297#[derive(Debug, Serialize)]
298#[serde(rename_all = "camelCase")]
299pub(crate) struct ConnectOverCdpArgs<'a> {
300    sdk_language: &'static str,
301    #[serde(rename = "endpointURL")]
302    endpoint_url: &'a str,
303    pub(crate) headers: Option<HashMap<String, String>>,
304    pub(crate) timeout: Option<f64>,
305    #[serde(rename = "slowMo")]
306    pub(crate) slowmo: Option<f64>,
307}
308
309impl<'a> ConnectOverCdpArgs<'a> {
310    pub(crate) fn new(endpoint_url: &'a str) -> Self {
311        Self {
312            sdk_language: "rust",
313            endpoint_url,
314            headers: None,
315            timeout: Some(30000.0),
316            slowmo: None,
317        }
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324    use crate::imp::playwright::Playwright;
325
326    crate::runtime_test!(launch, {
327        let driver = Driver::install().unwrap();
328        let conn = Connection::run(&driver).unwrap();
329        let p = Playwright::wait_initial_object(&conn).await.unwrap();
330        let p = p.upgrade().unwrap();
331        let chromium = p.chromium().upgrade().unwrap();
332        let res = chromium.launch(LaunchArgs::default()).await;
333        dbg!(&res);
334        res.unwrap();
335    });
336
337    crate::runtime_test!(typo, {
338        let driver = Driver::install().unwrap();
339        let conn = Connection::run(&driver).unwrap();
340        let p = Playwright::wait_initial_object(&conn).await.unwrap();
341        let p = p.upgrade().unwrap();
342        let chromium = p.chromium().upgrade().unwrap();
343        async fn send(c: &BrowserType) -> Result<Arc<Value>, Error> {
344            Ok(send_message!(c, "nonExistentMethod", Map::default()))
345        }
346        match send(&chromium).await {
347            Err(Error::ErrorResponded(e)) => dbg!(e),
348            x => {
349                dbg!(&x);
350                unreachable!()
351            }
352        }
353    });
354}