playwright/api/
page.rs

1pub use crate::{
2    api::{
3        frame::{
4            AddScriptTagBuilder, CheckBuilder, ClickBuilder, DblClickBuilder, FillBuilder,
5            GotoBuilder, HoverBuilder, PressBuilder, SelectOptionBuilder, SetContentBuilder,
6            SetInputFilesBuilder, TapBuilder, TypeBuilder, UncheckBuilder, WaitForFunctionBuilder,
7            WaitForSelectorBuilder
8        },
9        Download, JsHandle, Request
10    },
11    imp::page::{EventType, Media}
12};
13use crate::{
14    api::{
15        input_device::*, Accessibility, BrowserContext, ConsoleMessage, ElementHandle, FileChooser,
16        Frame, Keyboard, Response, TouchScreen, Video, WebSocket, Worker
17    },
18    imp::{
19        core::*,
20        frame::Frame as FrameImpl,
21        page::{EmulateMediaArgs, Evt, Page as Impl, PdfArgs, ReloadArgs, ScreenshotArgs},
22        prelude::*,
23        utils::{
24            ColorScheme, DocumentLoadState, File, FloatRect, Length, PdfMargins, ScreenshotType,
25            Viewport
26        }
27    },
28    Error
29};
30
31/// Page provides methods to interact with a single tab in a `Browser`, or an
32/// [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One `Browser`
33/// instance might have multiple `Page` instances.
34///
35/// This example creates a page, navigates it to a URL, and then saves a screenshot:
36///
37/// ```js
38/// const { webkit } = require('playwright');  // Or 'chromium' or 'firefox'.
39///
40/// (async () => {
41///  const browser = await webkit.launch();
42///  const context = await browser.newContext();
43///  const page = await context.newPage();
44///  await page.goto('https://example.com');
45///  await page.screenshot({path: 'screenshot.png'});
46///  await browser.close();
47/// })();
48/// ```
49///
50/// The Page class emits various events (described below) which can be handled using any of Node's native
51/// [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter) methods, such as `on`, `once` or
52/// `removeListener`.
53///
54/// This example logs a message for a single page `load` event:
55///
56/// ```js
57/// page.once('load', () => console.log('Page loaded!'));
58/// ```
59///
60/// To unsubscribe from events use the `removeListener` method:
61///
62/// ```js
63/// function logRequest(interceptedRequest) {
64///  console.log('A request was made:', interceptedRequest.url());
65/// }
66/// page.on('request', logRequest);
67///// Sometime later...
68/// page.removeListener('request', logRequest);
69/// ```
70#[derive(Debug, Clone)]
71pub struct Page {
72    inner: Weak<Impl>,
73    pub keyboard: Keyboard,
74    pub touch_screen: TouchScreen,
75    pub mouse: Mouse,
76    pub accessibility: Accessibility
77}
78
79impl PartialEq for Page {
80    fn eq(&self, other: &Self) -> bool {
81        let a = self.inner.upgrade();
82        let b = other.inner.upgrade();
83        a.and_then(|a| b.map(|b| (a, b)))
84            .map(|(a, b)| a.guid() == b.guid())
85            .unwrap_or_default()
86    }
87}
88
89impl Page {
90    pub(crate) fn new(inner: Weak<Impl>) -> Self {
91        Self {
92            inner: inner.clone(),
93            keyboard: Keyboard::new(inner.clone()),
94            touch_screen: TouchScreen::new(inner.clone()),
95            mouse: Mouse::new(inner.clone()),
96            accessibility: Accessibility::new(inner)
97        }
98    }
99
100    pub fn context(&self) -> BrowserContext {
101        BrowserContext::new(weak_and_then(&self.inner, |rc| rc.browser_context()))
102    }
103
104    fn main_frame_weak(&self) -> Weak<FrameImpl> {
105        weak_and_then(&self.inner, |rc| rc.main_frame())
106    }
107
108    /// The page's main frame. Page is guaranteed to have a main frame which persists during navigations.
109    pub fn main_frame(&self) -> Frame { Frame::new(self.main_frame_weak()) }
110
111    /// An array of all frames attached to the page.
112    pub fn frames(&self) -> Result<Vec<Frame>, Error> {
113        Ok(upgrade(&self.inner)?
114            .frames()
115            .into_iter()
116            .map(Frame::new)
117            .collect())
118    }
119
120    /// This method returns all of the dedicated [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
121    /// associated with the page.
122    ///
123    /// > NOTE: This does not contain ServiceWorkers
124    pub fn workers(&self) -> Result<Vec<Worker>, Error> {
125        Ok(upgrade(&self.inner)?
126            .workers()
127            .into_iter()
128            .map(Worker::new)
129            .collect())
130    }
131
132    /// Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
133    /// last redirect.
134    pub fn reload_builder(&self) -> ReloadBuilder { ReloadBuilder::new(self.inner.clone()) }
135    /// Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
136    /// last redirect. If can not go back, returns `null`.
137    ///
138    /// Navigate to the previous page in history.
139    pub fn go_back_builder(&self) -> GoBackBuilder { GoBackBuilder::new(self.inner.clone()) }
140    /// Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
141    /// last redirect. If can not go forward, returns `null`.
142    ///
143    /// Navigate to the next page in history.
144    pub fn go_forward_builder(&self) -> GoForwardBuilder {
145        GoForwardBuilder::new(self.inner.clone())
146    }
147
148    pub async fn set_default_navigation_timeout(&self, timeout: u32) -> ArcResult<()> {
149        upgrade(&self.inner)?
150            .set_default_navigation_timeout(timeout)
151            .await
152    }
153
154    pub async fn set_default_timeout(&self, timeout: u32) -> ArcResult<()> {
155        upgrade(&self.inner)?.set_default_timeout(timeout).await
156    }
157
158    pub fn viewport_size(&self) -> Result<Option<Viewport>, Error> {
159        Ok(upgrade(&self.inner)?.viewport_size())
160    }
161
162    /// In the case of multiple pages in a single browser, each page can have its own viewport size. However,
163    /// [`method: Browser.newContext`] allows to set viewport size (and more) for all pages in the context at once.
164    ///
165    /// `page.setViewportSize` will resize the page. A lot of websites don't expect phones to change size, so you should set the
166    /// viewport size before navigating to the page.
167    pub async fn set_viewport_size(&self, viewport_size: Viewport) -> ArcResult<()> {
168        upgrade(&self.inner)?.set_viewport_size(viewport_size).await
169    }
170
171    /// Video object associated with this page.
172    pub fn video(&self) -> Result<Option<Video>, Error> {
173        Ok(upgrade(&self.inner)?.video().map(Video::new))
174    }
175
176    ///// Returns frame matching the specified criteria. Either `name` or `url` must be specified.
177    // fn frame(&self) -> Option<Frame> { unimplemented!() }
178
179    /// Brings page to front (activates tab).
180    pub async fn bring_to_front(&self) -> ArcResult<()> {
181        upgrade(&self.inner)?.bring_to_front().await
182    }
183
184    /// Adds a script which would be evaluated in one of the following scenarios:
185    /// - Whenever the page is navigated.
186    /// - Whenever the child frame is attached or navigated. In this case, the script is evaluated in the context of the newly
187    ///  attached frame.
188    ///
189    /// The script is evaluated after the document was created but before any of its scripts were run. This is useful to amend
190    /// the JavaScript environment, e.g. to seed `Math.random`.
191    ///
192    /// An example of overriding `Math.random` before the page loads:
193    ///
194    /// ```js browser
195    ///// preload.js
196    /// Math.random = () => 42;
197    /// ```
198    /// ```js
199    ///// In your playwright script, assuming the preload.js file is in same directory
200    /// await page.addInitScript({ path: './preload.js' });
201    /// ```
202    /// 
203    /// > NOTE: The order of evaluation of multiple scripts installed via [`method: BrowserContext.addInitScript`] and
204    /// [`method: Page.addInitScript`] is not defined.
205    pub async fn add_init_script(&self, source: &str) -> ArcResult<()> {
206        // arg not supported
207        upgrade(&self.inner)?.add_init_script(source).await
208    }
209
210    /// Returns the PDF buffer.
211    ///
212    /// > NOTE: Generating a pdf is currently only supported in Chromium headless.
213    ///
214    /// `page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call
215    /// [`method: Page.emulateMedia`] before calling `page.pdf()`:
216    ///
217    /// > NOTE: By default, `page.pdf()` generates a pdf with modified colors for printing. Use the
218    /// [`-webkit-print-color-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust) property to
219    /// force rendering of exact colors.
220    ///
221    /// ```js
222    ///// Generates a PDF with 'screen' media type.
223    /// await page.emulateMedia({media: 'screen'});
224    /// await page.pdf({path: 'page.pdf'});
225    /// ```
226    /// 
227    /// The `width`, `height`, and `margin` options accept values labeled with units. Unlabeled values are treated as pixels.
228    ///
229    /// A few examples:
230    /// - `page.pdf({width: 100})` - prints with width set to 100 pixels
231    /// - `page.pdf({width: '100px'})` - prints with width set to 100 pixels
232    /// - `page.pdf({width: '10cm'})` - prints with width set to 10 centimeters.
233    ///
234    /// All possible units are:
235    /// - `px` - pixel
236    /// - `in` - inch
237    /// - `cm` - centimeter
238    /// - `mm` - millimeter
239    ///
240    /// The `format` options are:
241    /// - `Letter`: 8.5in x 11in
242    /// - `Legal`: 8.5in x 14in
243    /// - `Tabloid`: 11in x 17in
244    /// - `Ledger`: 17in x 11in
245    /// - `A0`: 33.1in x 46.8in
246    /// - `A1`: 23.4in x 33.1in
247    /// - `A2`: 16.54in x 23.4in
248    /// - `A3`: 11.7in x 16.54in
249    /// - `A4`: 8.27in x 11.7in
250    /// - `A5`: 5.83in x 8.27in
251    /// - `A6`: 4.13in x 5.83in
252    ///
253    /// > NOTE: `headerTemplate` and `footerTemplate` markup have the following limitations: > 1. Script tags inside templates
254    /// are not evaluated. > 2. Page styles are not visible inside templates.
255    pub fn pdf_builder(&self) -> PdfBuilder<'_, '_, '_, '_, '_, '_, '_, '_, '_, '_> {
256        PdfBuilder::new(self.inner.clone())
257    }
258
259    /// All temporary pages will be closed when the connection is terminated, but
260    /// it needs to be called explicitly to close it at any given time.
261    /// If `runBeforeUnload` is `false`, does not run any unload handlers and waits for the page to be closed. If
262    /// `runBeforeUnload` is `true` the method will run unload handlers, but will **not** wait for the page to close.
263    ///
264    /// By default, `page.close()` **does not** run `beforeunload` handlers.
265    ///
266    /// > NOTE: if `runBeforeUnload` is passed as true, a `beforeunload` dialog might be summoned and should be handled manually
267    /// via [`event: Page.dialog`] event.
268    pub async fn close(&self, run_before_unload: Option<bool>) -> ArcResult<()> {
269        let inner = match self.inner.upgrade() {
270            None => return Ok(()),
271            Some(inner) => inner
272        };
273        inner.close(run_before_unload).await
274    }
275
276    pub fn screenshot_builder(&self) -> ScreenshotBuilder {
277        ScreenshotBuilder::new(self.inner.clone())
278    }
279
280    /// This method changes the `CSS media type` through the `media` argument, and/or the `'prefers-colors-scheme'` media
281    /// feature, using the `colorScheme` argument.
282    ///
283    /// ```js
284    /// await page.evaluate(() => matchMedia('screen').matches);
285    ///// → true
286    /// await page.evaluate(() => matchMedia('print').matches);
287    ///// → false
288    /// await page.emulateMedia({ media: 'print' });
289    /// await page.evaluate(() => matchMedia('screen').matches);
290    ///// → false
291    /// await page.evaluate(() => matchMedia('print').matches);
292    ///// → true
293    /// await page.emulateMedia({});
294    /// await page.evaluate(() => matchMedia('screen').matches);
295    ///// → true
296    /// await page.evaluate(() => matchMedia('print').matches);
297    ///// → false
298    /// ```
299    /// ```js
300    /// await page.emulateMedia({ colorScheme: 'dark' });
301    /// await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches);
302    ///// → true
303    /// await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches);
304    ///// → false
305    /// await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches);
306    ///// → false
307    /// ```
308    pub fn emulate_media_builder(&self) -> EmulateMediaBuilder {
309        EmulateMediaBuilder::new(self.inner.clone())
310    }
311
312    /// Returns the opener for popup pages and `null` for others. If the opener has been closed already the returns `null`.
313    pub async fn opener(&self) -> ArcResult<Option<Page>> {
314        Ok(upgrade(&self.inner)?.opener().await?.map(Page::new))
315    }
316
317    /// The extra HTTP headers will be sent with every request the page initiates.
318    ///
319    /// > NOTE: [`method: Page.setExtraHTTPHeaders`] does not guarantee the order of headers in the outgoing requests.
320    pub async fn set_extra_http_headers<T>(&self, headers: T) -> ArcResult<()>
321    where
322        T: IntoIterator<Item = (String, String)>
323    {
324        upgrade(&self.inner)?.set_extra_http_headers(headers).await
325    }
326
327    pub async fn expect_event(&self, evt: EventType) -> Result<Event, Error> {
328        let stream = upgrade(&self.inner)?.subscribe_event();
329        let timeout = upgrade(&self.inner)?.default_timeout();
330        expect_event(stream, evt, timeout).await.map(Event::from)
331    }
332
333    subscribe_event! {}
334
335    // coverage
336    // expose_binding
337    // expose_function
338    // route
339    // unroute
340    // once_dialog
341
342    pub async fn wait_for_timeout(&self, timeout: f64) {
343        sleep(std::time::Duration::from_millis(timeout as u64)).await
344    }
345}
346
347#[derive(Clone)]
348pub enum Event {
349    Close,
350    Crash,
351    /// Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also
352    /// emitted if the page throws an error or a warning.
353    ///
354    /// The arguments passed into `console.log` appear as arguments on the event handler.
355    ///
356    /// An example of handling `console` event:
357    ///
358    /// ```js
359    /// page.on('console', async msg => {
360    ///  for (let i = 0; i < msg.args().length; ++i)
361    ///    console.log(`${i}: ${await msg.args()[i].jsonValue()}`);
362    /// });
363    /// await page.evaluate(() => console.log('hello', 5, {foo: 'bar'}));
364    /// ```
365    Console(ConsoleMessage),
366    /// Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must**
367    /// either [`method: Dialog.accept`] or [`method: Dialog.dismiss`] the dialog - otherwise the page will
368    /// [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and
369    /// actions like click will never finish.
370    ///
371    /// > NOTE: When no [`event: Page.dialog`] listeners are present, all dialogs are automatically dismissed.
372    Dialog,
373    DomContentLoaded,
374    /// Emitted when attachment download started. User can access basic file operations on downloaded content via the passed
375    /// `Download` instance.
376    ///
377    /// > NOTE: Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the
378    /// downloaded content. If `acceptDownloads` is not set, download events are emitted, but the actual download is not
379    /// performed and user has no access to the downloaded files.
380    Download(Download),
381    /// Emitted when a file chooser is supposed to appear, such as after clicking the  `<input type=file>`. Playwright can
382    /// respond to it via setting the input files using [`method: FileChooser.setFiles`] that can be uploaded after that.
383    ///
384    /// ```js
385    /// page.on('filechooser', async (fileChooser) => {
386    ///  await fileChooser.setFiles('/tmp/myfile.pdf');
387    /// });
388    /// ```
389    // FileChooser(FileChooser),
390    FrameAttached(Frame),
391    FrameDetached(Frame),
392    FrameNavigated(Frame),
393    Load,
394    PageError,
395    /// Emitted when the page opens a new tab or window. This event is emitted in addition to the
396    /// [`event: BrowserContext.page`], but only for popups relevant to this page.
397    ///
398    /// The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a
399    /// popup with `window.open('http://example.com')`, this event will fire when the network request to <http://example.com> is
400    /// done and its response has started loading in the popup.
401    ///
402    /// ```js
403    /// const [popup] = await Promise.all([
404    ///  page.waitForEvent('popup'),
405    ///  page.evaluate(() => window.open('https://example.com')),
406    /// ]);
407    /// console.log(await popup.evaluate('location.href'));
408    /// ```
409    ///
410    /// > NOTE: Use [`method: Page.waitForLoadState`] to wait until the page gets to a particular state (you should not need it
411    /// in most cases).
412    Popup(Page),
413    /// Emitted when a page issues a request. The request object is read-only. In order to intercept and mutate requests, see
414    /// [`method: Page.route`] or [`method: BrowserContext.route`].
415    Request(Request),
416    /// Emitted when a request fails, for example by timing out.
417    ///
418    /// > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
419    /// complete with [`event: Page.requestFinished`] event and not with [`event: Page.requestFailed`].
420    RequestFailed(Request),
421    /// Emitted when a request finishes successfully after downloading the response body. For a successful response, the
422    /// sequence of events is `request`, `response` and `requestfinished`.
423    RequestFinished(Request),
424    /// Emitted when response status and headers are received for a request. For a successful response, the sequence of events
425    /// is `request`, `response` and `requestfinished`.
426    Response(Response),
427    WebSocket(WebSocket),
428    Worker(Worker),
429    Video(Video)
430}
431
432impl From<Evt> for Event {
433    fn from(e: Evt) -> Event {
434        match e {
435            Evt::Close => Event::Close,
436            Evt::Crash => Event::Crash,
437            Evt::Console(x) => Event::Console(ConsoleMessage::new(x)),
438            Evt::Dialog => Event::Dialog,
439            Evt::Download(x) => Event::Download(Download::new(x)),
440            // Evt::FileChooser(x) => Event::FileChooser(x),
441            Evt::DomContentLoaded => Event::DomContentLoaded,
442            Evt::PageError => Event::PageError,
443            Evt::Request(x) => Event::Request(Request::new(x)),
444            Evt::Response(x) => Event::Response(Response::new(x)),
445            Evt::RequestFailed(x) => Event::RequestFailed(Request::new(x)),
446            Evt::RequestFinished(x) => Event::RequestFinished(Request::new(x)),
447            Evt::FrameAttached(x) => Event::FrameAttached(Frame::new(x)),
448            Evt::FrameDetached(x) => Event::FrameDetached(Frame::new(x)),
449            Evt::FrameNavigated(x) => Event::FrameNavigated(Frame::new(x)),
450            Evt::Load => Event::Load,
451            Evt::Popup(x) => Event::Popup(Page::new(x)),
452            Evt::WebSocket(x) => Event::WebSocket(WebSocket::new(x)),
453            Evt::Worker(x) => Event::Worker(Worker::new(x)),
454            Evt::Video(x) => Event::Video(Video::new(x))
455        }
456    }
457}
458
459impl IsEvent for Event {
460    type EventType = EventType;
461
462    fn event_type(&self) -> Self::EventType {
463        match self {
464            Self::Close => EventType::Close,
465            Self::Crash => EventType::Crash,
466            Self::Console(_) => EventType::Console,
467            Self::Dialog => EventType::Dialog,
468            Self::Download(_) => EventType::Download,
469            // Self::FileChooser(_) => EventType::FileChooser,
470            Self::DomContentLoaded => EventType::DomContentLoaded,
471            Self::PageError => EventType::PageError,
472            Self::Request(_) => EventType::Request,
473            Self::Response(_) => EventType::Response,
474            Self::RequestFailed(_) => EventType::RequestFailed,
475            Self::RequestFinished(_) => EventType::RequestFinished,
476            Self::FrameAttached(_) => EventType::FrameAttached,
477            Self::FrameDetached(_) => EventType::FrameDetached,
478            Self::FrameNavigated(_) => EventType::FrameNavigated,
479            Self::Load => EventType::Load,
480            Self::Popup(_) => EventType::Popup,
481            Self::WebSocket(_) => EventType::WebSocket,
482            Self::Worker(_) => EventType::Worker,
483            Self::Video(_) => EventType::Video
484        }
485    }
486}
487
488macro_rules! is_checked {
489    ($f: ident, $c: meta) => {
490        #[$c]
491        pub async fn $f(&self, selector: &str, timeout: Option<f64>) -> ArcResult<bool> {
492            self.main_frame().$f(selector, timeout).await
493        }
494    };
495}
496
497/// Shorthand of main_frame
498impl Page {
499    pub async fn query_selector(&self, selector: &str) -> ArcResult<Option<ElementHandle>> {
500        self.main_frame().query_selector(selector).await
501    }
502
503    pub async fn query_selector_all(&self, selector: &str) -> ArcResult<Vec<ElementHandle>> {
504        self.main_frame().query_selector_all(selector).await
505    }
506
507    pub fn wait_for_selector_builder<'a>(&self, selector: &'a str) -> WaitForSelectorBuilder<'a> {
508        self.main_frame().wait_for_selector_builder(selector)
509    }
510
511    is_checked! {is_checked, doc = "Errors if the element is not a checkbox or radio input."}
512    is_checked! {is_disabled, doc = ""}
513    is_checked! {is_editable, doc = ""}
514    is_checked! {is_enabled, doc = ""}
515    is_checked! {is_hidden, doc = ""}
516    is_checked! {is_visible, doc =""}
517
518    pub async fn dispatch_event<T>(
519        &self,
520        selector: &str,
521        r#type: &str,
522        event_init: Option<T>
523    ) -> ArcResult<()>
524    where
525        T: Serialize
526    {
527        // timeout not supported
528        self.main_frame()
529            .dispatch_event(selector, r#type, event_init)
530            .await
531    }
532
533    pub async fn evaluate_js_handle<T>(
534        &self,
535        expression: &str,
536        arg: Option<T>
537    ) -> ArcResult<JsHandle>
538    where
539        T: Serialize
540    {
541        self.main_frame().evaluate_js_handle(expression, arg).await
542    }
543
544    pub async fn evaluate_element_handle<T>(
545        &self,
546        expression: &str,
547        arg: Option<T>
548    ) -> ArcResult<ElementHandle>
549    where
550        T: Serialize
551    {
552        self.main_frame()
553            .evaluate_element_handle(expression, arg)
554            .await
555    }
556
557    pub async fn eval<U>(&self, expression: &str) -> ArcResult<U>
558    where
559        U: DeserializeOwned
560    {
561        self.main_frame().eval(expression).await
562    }
563
564    pub async fn evaluate<T, U>(&self, expression: &str, arg: T) -> ArcResult<U>
565    where
566        T: Serialize,
567        U: DeserializeOwned
568    {
569        self.main_frame().evaluate(expression, arg).await
570    }
571
572    pub async fn evaluate_on_selector<T, U>(
573        &self,
574        selector: &str,
575        expression: &str,
576        arg: Option<T>
577    ) -> ArcResult<U>
578    where
579        T: Serialize,
580        U: DeserializeOwned
581    {
582        self.main_frame()
583            .evaluate_on_selector(selector, expression, arg)
584            .await
585    }
586
587    pub async fn evaluate_on_selector_all<T, U>(
588        &self,
589        selector: &str,
590        expression: &str,
591        arg: Option<T>
592    ) -> ArcResult<U>
593    where
594        T: Serialize,
595        U: DeserializeOwned
596    {
597        self.main_frame()
598            .evaluate_on_selector_all(selector, expression, arg)
599            .await
600    }
601
602    pub fn add_script_tag_builder<'a>(&self, content: &'a str) -> AddScriptTagBuilder<'a, '_, '_> {
603        AddScriptTagBuilder::new(self.main_frame_weak(), content)
604    }
605
606    pub async fn add_style_tag(
607        &self,
608        content: &str,
609        url: Option<&str>
610    ) -> ArcResult<ElementHandle> {
611        self.main_frame().add_style_tag(content, url).await
612    }
613
614    pub fn url(&self) -> Result<String, Error> { self.main_frame().url() }
615
616    /// Gets the full HTML contents of the page, including the doctype.
617    pub async fn content<'a>(&self) -> ArcResult<String> { self.main_frame().content().await }
618
619    pub fn set_content_builder<'a>(&self, html: &'a str) -> SetContentBuilder<'a> {
620        self.main_frame().set_content_builder(html)
621    }
622
623    /// Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
624    /// last redirect.
625    ///
626    /// `page.goto` will throw an error if:
627    /// - there's an SSL error (e.g. in case of self-signed certificates).
628    /// - target URL is invalid.
629    /// - the `timeout` is exceeded during navigation.
630    /// - the remote server does not respond or is unreachable.
631    /// - the main resource failed to load.
632    ///
633    /// `page.goto` will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not
634    /// Found" and 500 "Internal Server Error".  The status code for such responses can be retrieved by calling
635    /// [`method: Response.status`].
636    ///
637    /// > NOTE: `page.goto` either throws an error or returns a main resource response. The only exceptions are navigation to
638    /// `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
639    /// > NOTE: Headless mode doesn't support navigation to a PDF document. See the
640    /// [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
641    ///
642    /// Shortcut for main frame's [`method: Frame.goto`]
643    pub fn goto_builder<'a>(&self, url: &'a str) -> GotoBuilder<'a, '_> {
644        GotoBuilder::new(self.main_frame_weak(), url)
645    }
646
647    // wait_for_load_state
648
649    pub async fn title(&self) -> ArcResult<String> { self.main_frame().title().await }
650
651    pub fn click_builder<'a>(&self, selector: &'a str) -> ClickBuilder<'a> {
652        self.main_frame().click_builder(selector)
653    }
654
655    pub fn dblclick_builder<'a>(&self, selector: &'a str) -> DblClickBuilder<'a> {
656        self.main_frame().dblclick_builder(selector)
657    }
658
659    pub fn tap_builder<'a>(&self, selector: &'a str) -> TapBuilder<'a> {
660        self.main_frame().tap_builder(selector)
661    }
662
663    pub fn fill_builder<'a, 'b>(&self, selector: &'a str, value: &'b str) -> FillBuilder<'a, 'b> {
664        self.main_frame().fill_builder(selector, value)
665    }
666
667    pub async fn focus(&self, selector: &str, timeout: Option<f64>) -> ArcResult<()> {
668        self.main_frame().focus(selector, timeout).await
669    }
670
671    pub async fn text_content(
672        &self,
673        selector: &str,
674        timeout: Option<f64>
675    ) -> ArcResult<Option<String>> {
676        self.main_frame().text_content(selector, timeout).await
677    }
678
679    pub async fn inner_text(&self, selector: &str, timeout: Option<f64>) -> ArcResult<String> {
680        self.main_frame().inner_text(selector, timeout).await
681    }
682
683    pub async fn inner_html(&self, selector: &str, timeout: Option<f64>) -> ArcResult<String> {
684        self.main_frame().inner_html(selector, timeout).await
685    }
686
687    pub async fn get_attribute(
688        &self,
689        selector: &str,
690        name: &str,
691        timeout: Option<f64>
692    ) -> ArcResult<Option<String>> {
693        self.main_frame()
694            .get_attribute(selector, name, timeout)
695            .await
696    }
697
698    pub fn hover_builder<'a>(&self, selector: &'a str) -> HoverBuilder<'a> {
699        self.main_frame().hover_builder(selector)
700    }
701
702    pub fn select_option_builder<'a>(&self, selector: &'a str) -> SelectOptionBuilder<'a> {
703        self.main_frame().select_option_builder(selector)
704    }
705
706    pub fn set_input_files_builder<'a>(
707        &self,
708        selector: &'a str,
709        file: File
710    ) -> SetInputFilesBuilder<'a> {
711        self.main_frame().set_input_files_builder(selector, file)
712    }
713
714    pub fn type_builer<'a, 'b>(&self, selector: &'a str, text: &'b str) -> TypeBuilder<'a, 'b> {
715        self.main_frame().type_builder(selector, text)
716    }
717
718    pub fn press_builder<'a, 'b>(&self, selector: &'a str, key: &'b str) -> PressBuilder<'a, 'b> {
719        self.main_frame().press_builder(selector, key)
720    }
721
722    pub fn check_builder<'a>(&self, selector: &'a str) -> CheckBuilder<'a> {
723        self.main_frame().check_builder(selector)
724    }
725
726    pub fn uncheck_builder<'a>(&self, selector: &'a str) -> UncheckBuilder<'a> {
727        self.main_frame().uncheck_builder(selector)
728    }
729
730    pub fn wait_for_function_builder<'a>(&self, expression: &'a str) -> WaitForFunctionBuilder<'a> {
731        self.main_frame().wait_for_function_builder(expression)
732    }
733    // expect_navigation
734}
735
736macro_rules! navigation {
737    ($t: ident, $f: ident) => {
738        pub struct $t {
739            inner: Weak<Impl>,
740            args: ReloadArgs
741        }
742
743        impl $t {
744            pub(crate) fn new(inner: Weak<Impl>) -> Self {
745                let args = ReloadArgs::default();
746                Self { inner, args }
747            }
748
749            pub async fn $f(self) -> ArcResult<Option<Response>> {
750                let Self { inner, args } = self;
751                let r = upgrade(&inner)?.$f(args).await?;
752                Ok(r.map(Response::new))
753            }
754
755            setter! {
756                timeout: Option<f64>,
757                /// When to consider operation succeeded, defaults to `load`. Events can be either:
758                /// - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.
759                /// - `'load'` - consider operation to be finished when the `load` event is fired.
760                /// - `'networkidle'` - consider operation to be finished when there are no network connections for at least `500` ms.
761                wait_until: Option<DocumentLoadState>
762            }
763        }
764    };
765}
766
767navigation!(ReloadBuilder, reload);
768navigation!(GoBackBuilder, go_back);
769navigation!(GoForwardBuilder, go_forward);
770
771pub struct PdfBuilder<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j> {
772    inner: Weak<Impl>,
773    args: PdfArgs<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j>
774}
775
776impl<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j> PdfBuilder<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j> {
777    pub(crate) fn new(inner: Weak<Impl>) -> Self {
778        let args = PdfArgs::default();
779        Self { inner, args }
780    }
781
782    pub async fn pdf(self) -> Result<(), Arc<Error>> {
783        let Self { inner, args } = self;
784        let _ = upgrade(&inner)?.pdf(args).await?;
785        Ok(())
786    }
787
788    setter! {
789        /// Scale of the webpage rendering. Defaults to `1`. Scale amount must be between 0.1 and 2.
790        scale: Option<f64>,
791        /// Display header and footer. Defaults to `false`.
792        display_header_footer: Option<bool>,
793        /// HTML template for the print header. Should be valid HTML markup with following classes used to inject printing values
794        /// into them:
795        /// - `'date'` formatted print date
796        /// - `'title'` document title
797        /// - `'url'` document location
798        /// - `'pageNumber'` current page number
799        /// - `'totalPages'` total pages in the document
800        header_template: Option<&'a str>,
801        /// HTML template for the print footer. Should use the same format as the `headerTemplate`.
802        footer_template: Option<&'b str>,
803        /// Print background graphics. Defaults to `false`.
804        print_background: Option<bool>,
805        /// Paper orientation. Defaults to `false`.
806        landscape: Option<bool>,
807        /// Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages.
808        page_ranges: Option<&'c str>,
809        /// Paper format. If set, takes priority over `width` or `height` options. Defaults to 'Letter'.
810        format: Option<&'d str>,
811        /// Paper width, accepts values labeled with units.
812        width: Option<Length<'e>>,
813        /// Paper height, accepts values labeled with units.
814        height: Option<Length<'f>>,
815        /// Give any CSS `@page` size declared in the page priority over what is declared in `width` and `height` or `format`
816        /// options. Defaults to `false`, which will scale the content to fit the paper size.
817        prefer_css_page_size: Option<bool>,
818        /// Paper margins, defaults to none.
819        margin: Option<PdfMargins<'g, 'h, 'i, 'j>>,
820        /// The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to the current working
821        /// directory. If no path is provided, the PDF won't be saved to the disk.
822        path: Option<PathBuf>
823    }
824}
825
826pub struct ScreenshotBuilder {
827    inner: Weak<Impl>,
828    args: ScreenshotArgs
829}
830
831impl ScreenshotBuilder {
832    pub(crate) fn new(inner: Weak<Impl>) -> Self {
833        let args = ScreenshotArgs::default();
834        Self { inner, args }
835    }
836
837    pub async fn screenshot(self) -> ArcResult<Vec<u8>> {
838        let Self { inner, args } = self;
839        upgrade(&inner)?.screenshot(args).await
840    }
841
842    pub fn r#type(mut self, x: ScreenshotType) -> Self {
843        self.args.r#type = Some(x);
844        self
845    }
846
847    setter! {
848        /// An object which specifies clipping of the resulting image. Should have the following fields:
849        clip: Option<FloatRect>,
850        /// When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to
851        /// `false`.
852        full_page: Option<bool>,
853        /// Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
854        /// Defaults to `false`.
855        omit_background: Option<bool>,
856        quality: Option<i32>,
857        /// Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
858        /// using the [`method: BrowserContext.setDefaultTimeout`] or [`method: Page.setDefaultTimeout`] methods.
859        timeout: Option<f64>,
860        /// The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a relative
861        /// path, then it is resolved relative to the current working directory. If no path is provided, the image won't be saved to
862        /// the disk.
863        path: Option<PathBuf>
864    }
865
866    pub fn clear_type(mut self) -> Self {
867        self.args.r#type = None;
868        self
869    }
870}
871
872pub struct EmulateMediaBuilder {
873    inner: Weak<Impl>,
874    args: EmulateMediaArgs
875}
876
877impl EmulateMediaBuilder {
878    pub(crate) fn new(inner: Weak<Impl>) -> Self {
879        let args = EmulateMediaArgs::default();
880        Self { inner, args }
881    }
882
883    pub async fn emulate_media(self) -> ArcResult<()> {
884        let Self { inner, args } = self;
885        upgrade(&inner)?.emulate_media(args).await
886    }
887
888    setter! {
889        /// Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
890        // NOTE: Not implemented passing `null` disables color scheme emulation
891        color_scheme: Option<ColorScheme>,
892        /// Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null`
893        /// disables CSS media emulation.
894        media: Option<Media>
895    }
896}