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}