playwright/imp/
browser_context.rs

1use crate::imp::{
2    browser::Browser,
3    core::*,
4    page::Page,
5    prelude::*,
6    utils::{Cookie, Geolocation, Header, StorageState},
7};
8
9#[derive(Debug)]
10pub(crate) struct BrowserContext {
11    channel: ChannelOwner,
12    var: Mutex<Variable>,
13    tx: Mutex<Option<broadcast::Sender<Evt>>>,
14}
15
16#[derive(Debug, Default)]
17pub(crate) struct Variable {
18    browser: Option<Weak<Browser>>,
19    pages: Vec<Weak<Page>>,
20    timeout: Option<u32>,
21    navigation_timeout: Option<u32>,
22}
23
24impl BrowserContext {
25    const DEFAULT_TIMEOUT: u32 = 30000;
26
27    pub(crate) fn try_new(channel: ChannelOwner) -> Result<Self, Error> {
28        let Initializer {} = serde_json::from_value(channel.initializer.clone())?;
29        let browser = match &channel.parent {
30            Some(RemoteWeak::Browser(b)) => Some(b.clone()),
31            _ => None,
32        };
33        let var = Mutex::new(Variable {
34            browser,
35            ..Variable::default()
36        });
37        Ok(Self {
38            channel,
39            var,
40            tx: Mutex::default(),
41        })
42    }
43
44    pub(crate) async fn new_page(&self) -> Result<Weak<Page>, Arc<Error>> {
45        let res = send_message!(self, "newPage", Map::new());
46        let guid = only_guid(&res)?;
47        let p = get_object!(self.context()?.lock().unwrap(), guid, Page)?;
48        Ok(p)
49    }
50
51    pub(crate) async fn close(&self) -> Result<(), Arc<Error>> {
52        let _ = send_message!(self, "close", Map::new());
53        Ok(())
54    }
55
56    pub(crate) async fn storage_state(&self) -> ArcResult<StorageState> {
57        let v = send_message!(self, "storageState", Map::new());
58        let s = serde_json::from_value((*v).clone()).map_err(Error::Serde)?;
59        Ok(s)
60    }
61
62    pub(crate) async fn clear_cookies(&self) -> ArcResult<()> {
63        let _ = send_message!(self, "clearCookies", Map::new());
64        Ok(())
65    }
66
67    pub(crate) async fn cookies(&self, urls: &[String]) -> ArcResult<Vec<Cookie>> {
68        #[derive(Serialize)]
69        #[serde(rename_all = "camelCase")]
70        struct Args<'a> {
71            urls: &'a [String],
72        }
73        let args = Args { urls };
74        let v = send_message!(self, "cookies", args);
75        let cookies = first(&v).ok_or(Error::InvalidParams)?;
76        let cs: Vec<Cookie> = serde_json::from_value((*cookies).clone()).map_err(Error::Serde)?;
77        Ok(cs)
78    }
79
80    pub(crate) async fn add_cookies(&self, cookies: &[Cookie]) -> ArcResult<()> {
81        #[derive(Serialize)]
82        #[serde(rename_all = "camelCase")]
83        struct Args<'a> {
84            cookies: &'a [Cookie],
85        }
86        let args = Args { cookies };
87        let _ = send_message!(self, "addCookies", args);
88        Ok(())
89    }
90
91    pub(crate) async fn grant_permissions(
92        &self,
93        permissions: &[String],
94        origin: Option<&str>,
95    ) -> ArcResult<()> {
96        #[skip_serializing_none]
97        #[derive(Serialize)]
98        #[serde(rename_all = "camelCase")]
99        struct Args<'a, 'b> {
100            permissions: &'a [String],
101            origin: Option<&'b str>,
102        }
103        let args = Args {
104            permissions,
105            origin,
106        };
107        let _ = send_message!(self, "grantPermissions", args);
108        Ok(())
109    }
110
111    pub(crate) async fn clear_permissions(&self) -> ArcResult<()> {
112        let _ = send_message!(self, "clearPermissions", Map::new());
113        Ok(())
114    }
115
116    pub(crate) async fn set_geolocation(&self, geolocation: Option<&Geolocation>) -> ArcResult<()> {
117        #[skip_serializing_none]
118        #[derive(Serialize)]
119        #[serde(rename_all = "camelCase")]
120        struct Args<'a> {
121            geolocation: Option<&'a Geolocation>,
122        }
123        let args = Args { geolocation };
124        let _ = send_message!(self, "setGeolocation", args);
125        Ok(())
126    }
127
128    pub(crate) async fn set_offline(&self, offline: bool) -> ArcResult<()> {
129        let mut args = Map::new();
130        args.insert("offline".into(), offline.into());
131        let _ = send_message!(self, "setOffline", args);
132        Ok(())
133    }
134
135    pub(crate) async fn add_init_script(&self, script: &str) -> ArcResult<()> {
136        let mut args = HashMap::new();
137        args.insert("source", script);
138        let _ = send_message!(self, "addInitScript", args);
139        Ok(())
140    }
141
142    pub(crate) async fn set_extra_http_headers<T>(&self, headers: T) -> ArcResult<()>
143    where
144        T: IntoIterator<Item = (String, String)>,
145    {
146        #[derive(Serialize)]
147        #[serde(rename_all = "camelCase")]
148        struct Args {
149            headers: Vec<Header>,
150        }
151        let args = Args {
152            headers: headers.into_iter().map(Header::from).collect(),
153        };
154        let _ = send_message!(self, "setExtraHTTPHeaders", args);
155        Ok(())
156    }
157
158    // async def expose_binding(
159    // async def expose_function(self, name: str, callback: Callable) -> None:
160    // async def route(self, url: URLMatch, handler: RouteHandler) -> None:
161    // async def unroute(
162
163    // async fn pause(&self) -> ArcResult<()> {
164    //    let _ = send_message!(self, "pause", Map::new());
165    //    Ok(())
166    //}
167}
168
169// mutable
170impl BrowserContext {
171    pub(crate) fn browser(&self) -> Option<Weak<Browser>> {
172        self.var.lock().unwrap().browser.clone()
173    }
174
175    pub(crate) fn set_browser(&self, browser: Weak<Browser>) {
176        self.var.lock().unwrap().browser = Some(browser);
177    }
178
179    pub(crate) fn pages(&self) -> Vec<Weak<Page>> {
180        self.var.lock().unwrap().pages.clone()
181    }
182
183    pub(super) fn push_page(&self, p: Weak<Page>) {
184        self.var.lock().unwrap().pages.push(p);
185    }
186
187    pub(super) fn remove_page(&self, page: &Weak<Page>) {
188        let pages = &mut self.var.lock().unwrap().pages;
189        pages.remove_one(|p| p.ptr_eq(page));
190    }
191
192    pub(crate) fn default_timeout(&self) -> u32 {
193        self.var
194            .lock()
195            .unwrap()
196            .timeout
197            .unwrap_or(Self::DEFAULT_TIMEOUT)
198    }
199
200    pub(crate) fn default_navigation_timeout(&self) -> u32 {
201        self.var
202            .lock()
203            .unwrap()
204            .navigation_timeout
205            .unwrap_or(Self::DEFAULT_TIMEOUT)
206    }
207
208    pub(crate) async fn set_default_timeout(&self, timeout: u32) -> ArcResult<()> {
209        let mut args = Map::new();
210        args.insert("timeout".into(), timeout.into());
211        let _ = send_message!(self, "setDefaultTimeoutNoReply", args);
212        self.var.lock().unwrap().timeout = Some(timeout);
213        Ok(())
214    }
215
216    pub(crate) async fn set_default_navigation_timeout(&self, timeout: u32) -> ArcResult<()> {
217        let mut args = Map::new();
218        args.insert("timeout".into(), timeout.into());
219        let _ = send_message!(self, "setDefaultNavigationTimeoutNoReply", args);
220        self.var.lock().unwrap().navigation_timeout = Some(timeout);
221        Ok(())
222    }
223
224    fn on_close(&self, ctx: &Context) -> Result<(), Error> {
225        let browser = match self.browser().and_then(|b| b.upgrade()) {
226            None => return Ok(()),
227            Some(b) => b,
228        };
229        let this = get_object!(ctx, self.guid(), BrowserContext)?;
230        browser.remove_context(&this);
231        self.emit_event(Evt::Close);
232        Ok(())
233    }
234
235    fn on_route(&self, _ctx: &Context, _parmas: Map<String, Value>) -> Result<(), Error> {
236        // TODO: noimplemented
237        Ok(())
238    }
239}
240
241impl RemoteObject for BrowserContext {
242    fn channel(&self) -> &ChannelOwner {
243        &self.channel
244    }
245    fn channel_mut(&mut self) -> &mut ChannelOwner {
246        &mut self.channel
247    }
248
249    fn handle_event(
250        &self,
251        ctx: &Context,
252        method: Str<Method>,
253        params: Map<String, Value>,
254    ) -> Result<(), Error> {
255        match method.as_str() {
256            "page" => {
257                let first = first_object(&params).ok_or(Error::InvalidParams)?;
258                let OnlyGuid { guid } = serde_json::from_value((*first).clone())?;
259                let p = get_object!(ctx, &guid, Page)?;
260                self.push_page(p.clone());
261                self.emit_event(Evt::Page(p));
262            }
263            "close" => self.on_close(ctx)?,
264            "bindingCall" => {}
265            "route" => self.on_route(ctx, params)?,
266            _ => {}
267        }
268        Ok(())
269    }
270}
271
272#[derive(Debug, Clone)]
273pub(crate) enum Evt {
274    Close,
275    Page(Weak<Page>),
276}
277
278impl EventEmitter for BrowserContext {
279    type Event = Evt;
280
281    fn tx(&self) -> Option<broadcast::Sender<Self::Event>> {
282        self.tx.lock().unwrap().clone()
283    }
284
285    fn set_tx(&self, tx: broadcast::Sender<Self::Event>) {
286        *self.tx.lock().unwrap() = Some(tx);
287    }
288}
289
290#[derive(Debug, Clone, Copy, PartialEq)]
291pub enum EventType {
292    Close,
293    Page,
294}
295
296impl IsEvent for Evt {
297    type EventType = EventType;
298
299    fn event_type(&self) -> Self::EventType {
300        match self {
301            Self::Close => EventType::Close,
302            Self::Page(_) => EventType::Page,
303        }
304    }
305}
306
307#[derive(Debug, Deserialize)]
308#[serde(rename_all = "camelCase")]
309struct Initializer {}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314    use crate::imp::{browser::*, browser_type::*, playwright::Playwright};
315
316    crate::runtime_test!(storage_state, {
317        let driver = Driver::install().unwrap();
318        let conn = Connection::run(&driver).unwrap();
319        let p = Playwright::wait_initial_object(&conn).await.unwrap();
320        let p = p.upgrade().unwrap();
321        let chromium = p.chromium().upgrade().unwrap();
322        let b = chromium.launch(LaunchArgs::default()).await.unwrap();
323        let b = b.upgrade().unwrap();
324        let c = b.new_context(NewContextArgs::default()).await.unwrap();
325        let c = c.upgrade().unwrap();
326        c.storage_state().await.unwrap();
327        c.cookies(&[]).await.unwrap();
328        // Note: set_default_timeout is incompatible with Playwright 1.57.0 driver (setDefaultTimeoutNoReply not supported)
329        // c.set_default_timeout(30000).await.unwrap();
330    });
331}