playwright/imp/
playwright.rs

1use crate::{
2    api::{browser::ContextBuilder, browser_type::PersistentContextLauncher},
3    imp::{
4        browser_type::BrowserType, core::*, impl_future::*, prelude::*, selectors::Selectors,
5        utils::Viewport
6    }
7};
8use serde::Deserialize;
9use std::{sync::TryLockError, time::Instant};
10
11#[derive(Debug)]
12pub(crate) struct Playwright {
13    channel: ChannelOwner,
14    chromium: Weak<BrowserType>,
15    firefox: Weak<BrowserType>,
16    webkit: Weak<BrowserType>,
17    selectors: Option<Weak<Selectors>>,
18    devices: Vec<DeviceDescriptor>
19}
20
21impl Playwright {
22    pub(crate) fn try_new(ctx: &Context, channel: ChannelOwner) -> Result<Self, Error> {
23        let i: Initializer = serde_json::from_value(channel.initializer.clone())?;
24        let chromium = get_object!(ctx, &i.chromium.guid, BrowserType)?;
25        let firefox = get_object!(ctx, &i.firefox.guid, BrowserType)?;
26        let webkit = get_object!(ctx, &i.webkit.guid, BrowserType)?;
27        // selectors is optional in newer Playwright versions (1.50+)
28        let selectors = if let Some(ref sel) = i.selectors {
29            Some(get_object!(ctx, &sel.guid, Selectors)?)
30        } else {
31            None
32        };
33        let devices = i.device_descriptors;
34        Ok(Self {
35            channel,
36            chromium,
37            firefox,
38            webkit,
39            selectors,
40            devices
41        })
42    }
43
44    pub(crate) fn devices(&self) -> &[DeviceDescriptor] { &self.devices }
45
46    pub(crate) fn device(&self, name: &str) -> Option<&DeviceDescriptor> {
47        self.devices.iter().find(|d| d.name == name)
48    }
49
50    pub(crate) fn chromium(&self) -> Weak<BrowserType> { self.chromium.clone() }
51
52    pub(crate) fn firefox(&self) -> Weak<BrowserType> { self.firefox.clone() }
53
54    pub(crate) fn webkit(&self) -> Weak<BrowserType> { self.webkit.clone() }
55
56    pub(crate) fn selectors(&self) -> Option<Weak<Selectors>> { self.selectors.clone() }
57
58    pub(crate) fn wait_initial_object(conn: &Connection) -> WaitInitialObject {
59        WaitInitialObject::new(conn.context())
60    }
61}
62
63impl RemoteObject for Playwright {
64    fn channel(&self) -> &ChannelOwner { &self.channel }
65    fn channel_mut(&mut self) -> &mut ChannelOwner { &mut self.channel }
66}
67
68#[derive(Debug, Deserialize)]
69#[serde(rename_all = "camelCase")]
70struct Initializer {
71    chromium: OnlyGuid,
72    firefox: OnlyGuid,
73    webkit: OnlyGuid,
74    android: OnlyGuid,
75    #[serde(default)]
76    electron: Option<OnlyGuid>,
77    #[serde(default)]
78    selectors: Option<OnlyGuid>,
79    #[serde(default)]
80    utils: Option<OnlyGuid>,
81    #[serde(default)]
82    device_descriptors: Vec<DeviceDescriptor>
83}
84
85pub(crate) struct WaitInitialObject {
86    ctx: Wm<Context>,
87    started: Instant
88}
89
90impl WaitInitialObject {
91    fn new(ctx: Wm<Context>) -> Self {
92        Self {
93            ctx,
94            started: Instant::now()
95        }
96    }
97}
98
99impl Future for WaitInitialObject {
100    type Output = Result<Weak<Playwright>, Error>;
101
102    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
103        let i: &S<Guid> = S::validate("Playwright").unwrap();
104        let this = self.get_mut();
105        macro_rules! pending {
106            () => {{
107                cx.waker().wake_by_ref();
108                if this.started.elapsed().as_secs() > 10 {
109                    return Poll::Ready(Err(Error::InitializationError));
110                }
111                return Poll::Pending;
112            }};
113        }
114        let rc = upgrade(&this.ctx)?;
115        let c = match rc.try_lock() {
116            Ok(x) => x,
117            Err(TryLockError::WouldBlock) => pending!(),
118            Err(e) => Err(e).unwrap()
119        };
120        match get_object!(c, i, Playwright) {
121            Ok(p) => Poll::Ready(Ok(p)),
122            Err(_) => pending!()
123        }
124    }
125}
126
127#[derive(Debug, Clone, PartialEq)]
128pub struct DeviceDescriptor {
129    pub name: String,
130    pub user_agent: String,
131    pub viewport: Viewport,
132    pub screen: Option<Viewport>,
133    pub device_scale_factor: f64,
134    pub is_mobile: bool,
135    pub has_touch: bool,
136    pub default_browser_type: String
137}
138
139impl<'de> Deserialize<'de> for DeviceDescriptor {
140    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141    where
142        D: serde::Deserializer<'de>
143    {
144        #[derive(Deserialize)]
145        struct DeviceDescriptorImpl {
146            name: String,
147            descriptor: Descriptor
148        }
149        #[derive(Deserialize)]
150        #[serde(rename_all = "camelCase")]
151        struct Descriptor {
152            user_agent: String,
153            viewport: Viewport,
154            screen: Option<Viewport>,
155            device_scale_factor: f64,
156            is_mobile: bool,
157            has_touch: bool,
158            default_browser_type: String
159        }
160        let DeviceDescriptorImpl {
161            name,
162            descriptor:
163                Descriptor {
164                    user_agent,
165                    viewport,
166                    screen,
167                    device_scale_factor,
168                    is_mobile,
169                    has_touch,
170                    default_browser_type
171                }
172        } = DeviceDescriptorImpl::deserialize(deserializer)?;
173        Ok(DeviceDescriptor {
174            name,
175            user_agent,
176            viewport,
177            screen,
178            device_scale_factor,
179            is_mobile,
180            has_touch,
181            default_browser_type
182        })
183    }
184}
185
186macro_rules! impl_set_device {
187    ($device: expr, $builder:expr) => {
188        (if let Some(screen) = &$device.screen {
189            $builder.screen(screen.clone())
190        } else {
191            $builder
192        })
193        .user_agent(&$device.user_agent)
194        .viewport(Some($device.viewport.clone()))
195        .device_scale_factor($device.device_scale_factor)
196        .is_mobile($device.is_mobile)
197        .has_touch($device.has_touch)
198    };
199}
200
201impl DeviceDescriptor {
202    pub(crate) fn set_persistent_context<'source, 'b, 'c, 'd, 'e, 'g, 'h, 'i, 'j, 'k, 'l>(
203        device: &'source Self,
204        builder: PersistentContextLauncher<'b, 'c, 'd, 'e, 'source, 'g, 'h, 'i, 'j, 'k, 'l>
205    ) -> PersistentContextLauncher<'b, 'c, 'd, 'e, 'source, 'g, 'h, 'i, 'j, 'k, 'l> {
206        impl_set_device!(device, builder)
207    }
208
209    pub(crate) fn set_context<'source, 'c, 'd, 'e, 'f, 'g, 'h>(
210        device: &'source Self,
211        builder: ContextBuilder<'source, 'c, 'd, 'e, 'f, 'g, 'h>
212    ) -> ContextBuilder<'source, 'c, 'd, 'e, 'f, 'g, 'h> {
213        impl_set_device!(device, builder)
214    }
215}