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 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}