playwright/api/frame.rs
1pub use crate::imp::frame::{FrameNavigatedEvent, FrameState, Polling};
2use crate::{
3 api::{ElementHandle, JsHandle, Page, Response},
4 imp::{
5 core::*,
6 frame::{
7 AddScriptTagArgs, CheckArgs, ClickArgs, Evt, FillArgs, Frame as Impl, GotoArgs,
8 HoverArgs, Opt, PressArgs, SelectOptionArgs, SetContentArgs, SetInputFilesArgs,
9 TapArgs, TypeArgs, WaitForFunctionArgs, WaitForSelectorArgs
10 },
11 prelude::*,
12 utils::{DocumentLoadState, File, KeyboardModifier, MouseButton, Position}
13 }
14};
15
16/// At every point of time, page exposes its current frame tree via the [`method: Page.mainFrame`] and
17/// [`method: Frame.childFrames`] methods.
18///
19/// `Frame` object's lifecycle is controlled by three events, dispatched on the page object:
20/// - [`event: Page.frameAttached`] - fired when the frame gets attached to the page. A Frame can be attached to the page
21/// only once.
22/// - [`event: Page.frameNavigated`] - fired when the frame commits navigation to a different URL.
23/// - [`event: Page.frameDetached`] - fired when the frame gets detached from the page. A Frame can be detached from the
24/// page only once.
25///
26/// An example of dumping frame tree:
27///
28/// ```js
29/// const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
30///
31/// (async () => {
32/// const browser = await firefox.launch();
33/// const page = await browser.newPage();
34/// await page.goto('https://www.google.com/chrome/browser/canary.html');
35/// dumpFrameTree(page.mainFrame(), '');
36/// await browser.close();
37///
38/// function dumpFrameTree(frame, indent) {
39/// console.log(indent + frame.url());
40/// for (const child of frame.childFrames()) {
41/// dumpFrameTree(child, indent + ' ');
42/// }
43/// }
44/// })();
45/// ```
46#[derive(Clone)]
47pub struct Frame {
48 inner: Weak<Impl>
49}
50
51impl PartialEq for Frame {
52 fn eq(&self, other: &Self) -> bool {
53 let a = self.inner.upgrade();
54 let b = other.inner.upgrade();
55 a.and_then(|a| b.map(|b| (a, b)))
56 .map(|(a, b)| a.guid() == b.guid())
57 .unwrap_or_default()
58 }
59}
60
61macro_rules! is_checked {
62 ($f: ident) => {
63 pub async fn $f(&self, selector: &str, timeout: Option<f64>) -> ArcResult<bool> {
64 upgrade(&self.inner)?.$f(selector, timeout).await
65 }
66 };
67}
68
69impl Frame {
70 pub(crate) fn new(inner: Weak<Impl>) -> Self { Self { inner } }
71
72 pub fn url(&self) -> Result<String, Error> { Ok(upgrade(&self.inner)?.url()) }
73
74 /// Returns frame's name attribute as specified in the tag.
75 ///
76 /// If the name is empty, returns the id attribute instead.
77 ///
78 /// > NOTE: This value is calculated once when the frame is created, and will not update if the attribute is changed later.
79 pub fn name(&self) -> Result<String, Error> { Ok(upgrade(&self.inner)?.name()) }
80
81 pub fn page(&self) -> Result<Option<Page>, Error> {
82 Ok(upgrade(&self.inner)?.page().map(Page::new))
83 }
84
85 /// Parent frame, if any. Detached frames and main frames return `null`.
86 pub fn parent_frame(&self) -> Result<Option<Frame>, Error> {
87 Ok(upgrade(&self.inner)?.parent_frame().map(Frame::new))
88 }
89
90 pub fn child_frames(&self) -> Result<Vec<Frame>, Error> {
91 Ok(upgrade(&self.inner)?
92 .child_frames()
93 .into_iter()
94 .map(Frame::new)
95 .collect())
96 }
97
98 /// Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
99 /// last redirect.
100 ///
101 /// `frame.goto` will throw an error if:
102 /// - there's an SSL error (e.g. in case of self-signed certificates).
103 /// - target URL is invalid.
104 /// - the `timeout` is exceeded during navigation.
105 /// - the remote server does not respond or is unreachable.
106 /// - the main resource failed to load.
107 ///
108 /// `frame.goto` will not throw an error when any valid HTTP status code is returned by the remote server, including 404
109 /// "Not Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling
110 /// [`method: Response.status`].
111 ///
112 /// > NOTE: `frame.goto` either throws an error or returns a main resource response. The only exceptions are navigation to
113 /// `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
114 /// > NOTE: Headless mode doesn't support navigation to a PDF document. See the
115 /// [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
116 pub fn goto_builder<'a>(&self, url: &'a str) -> GotoBuilder<'a, '_> {
117 GotoBuilder::new(self.inner.clone(), url)
118 }
119
120 /// This method clicks an element matching `selector` by performing the following steps:
121 /// 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
122 /// 1. Wait for [actionability](https://playwright.dev/docs/actionability/) checks on the matched element, unless `force` option is set. If the
123 /// element is detached during the checks, the whole action is retried.
124 /// 1. Scroll the element into view if needed.
125 /// 1. Use [`property: Page.mouse`] to click in the center of the element, or the specified `position`.
126 /// 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
127 ///
128 /// When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
129 /// zero timeout disables this.
130 pub fn click_builder<'a>(&self, selector: &'a str) -> ClickBuilder<'a> {
131 ClickBuilder::new(self.inner.clone(), selector)
132 }
133
134 /// This method double clicks an element matching `selector` by performing the following steps:
135 /// 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
136 /// 1. Wait for [actionability](https://playwright.dev/docs/actionability/) checks on the matched element, unless `force` option is set. If the
137 /// element is detached during the checks, the whole action is retried.
138 /// 1. Scroll the element into view if needed.
139 /// 1. Use [`property: Page.mouse`] to double click in the center of the element, or the specified `position`.
140 /// 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set. Note that if the
141 /// first click of the `dblclick()` triggers a navigation event, this method will throw.
142 ///
143 /// When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
144 /// zero timeout disables this.
145 ///
146 /// > NOTE: `frame.dblclick()` dispatches two `click` events and a single `dblclick` event.
147 pub fn dblclick_builder<'a>(&self, selector: &'a str) -> DblClickBuilder<'a> {
148 DblClickBuilder::new(self.inner.clone(), selector)
149 }
150
151 /// This method taps an element matching `selector` by performing the following steps:
152 /// 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
153 /// 1. Wait for [actionability](https://playwright.dev/docs/actionability/) checks on the matched element, unless `force` option is set. If the
154 /// element is detached during the checks, the whole action is retried.
155 /// 1. Scroll the element into view if needed.
156 /// 1. Use [`property: Page.touchscreen`] to tap the center of the element, or the specified `position`.
157 /// 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
158 ///
159 /// When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
160 /// zero timeout disables this.
161 ///
162 /// > NOTE: `frame.tap()` requires that the `hasTouch` option of the browser context be set to true.
163 pub fn tap_builder<'a>(&self, selector: &'a str) -> TapBuilder<'a> {
164 TapBuilder::new(self.inner.clone(), selector)
165 }
166 /// This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability/) checks, focuses the
167 /// element, fills it and triggers an `input` event after filling. Note that you can pass an empty string to clear the input
168 /// field.
169 ///
170 /// If the target element is not an `<input>`, `<textarea>` or `[contenteditable]` element, this method throws an error.
171 /// However, if the element is inside the `<label>` element that has an associated
172 /// [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be filled
173 /// instead.
174 ///
175 /// To send fine-grained keyboard events, use [Frame::type_builder](Frame::type_builder).
176 pub fn fill_builder<'a, 'b>(&self, selector: &'a str, value: &'b str) -> FillBuilder<'a, 'b> {
177 FillBuilder::new(self.inner.clone(), selector, value)
178 }
179
180 /// This method fetches an element with `selector` and focuses it. If there's no element matching `selector`, the method
181 /// waits until a matching element appears in the DOM.
182 pub async fn focus(&self, selector: &str, timeout: Option<f64>) -> ArcResult<()> {
183 upgrade(&self.inner)?.focus(selector, timeout).await
184 }
185
186 /// Returns `element.textContent`.
187 pub async fn text_content(
188 &self,
189 selector: &str,
190 timeout: Option<f64>
191 ) -> ArcResult<Option<String>> {
192 upgrade(&self.inner)?.text_content(selector, timeout).await
193 }
194
195 /// Returns `element.innerText`.
196 pub async fn inner_text(&self, selector: &str, timeout: Option<f64>) -> ArcResult<String> {
197 upgrade(&self.inner)?.inner_text(selector, timeout).await
198 }
199
200 /// Returns `element.innerHTML`.
201 pub async fn inner_html(&self, selector: &str, timeout: Option<f64>) -> ArcResult<String> {
202 upgrade(&self.inner)?.inner_html(selector, timeout).await
203 }
204
205 /// Returns element attribute value.
206 pub async fn get_attribute(
207 &self,
208 selector: &str,
209 name: &str,
210 timeout: Option<f64>
211 ) -> ArcResult<Option<String>> {
212 upgrade(&self.inner)?
213 .get_attribute(selector, name, timeout)
214 .await
215 }
216
217 pub async fn query_selector(&self, selector: &str) -> ArcResult<Option<ElementHandle>> {
218 Ok(upgrade(&self.inner)?
219 .query_selector(selector)
220 .await?
221 .map(ElementHandle::new))
222 }
223
224 pub async fn query_selector_all(&self, selector: &str) -> ArcResult<Vec<ElementHandle>> {
225 let es = upgrade(&self.inner)?.query_selector_all(selector).await?;
226 Ok(es.into_iter().map(ElementHandle::new).collect())
227 }
228
229 /// Returns the `frame` or `iframe` element handle which corresponds to this frame.
230 ///
231 /// This is an inverse of [`method: ElementHandle.contentFrame`]. Note that returned handle actually belongs to the parent
232 /// frame.
233 ///
234 /// This method throws an error if the frame has been detached before `frameElement()` returns.
235 ///
236 /// ```js
237 /// const frameElement = await frame.frameElement();
238 /// const contentFrame = await frameElement.contentFrame();
239 /// console.log(frame === contentFrame); // -> true
240 /// ```
241 pub async fn frame_element(&self) -> ArcResult<ElementHandle> {
242 Ok(ElementHandle::new(
243 upgrade(&self.inner)?.frame_element().await?
244 ))
245 }
246
247 /// Returns when element specified by selector satisfies `state` option. Returns `null` if waiting for `hidden` or
248 /// `detached`.
249 ///
250 /// Wait for the `selector` to satisfy `state` option (either appear/disappear from dom, or become visible/hidden). If at
251 /// the moment of calling the method `selector` already satisfies the condition, the method will return immediately. If the
252 /// selector doesn't satisfy the condition for the `timeout` milliseconds, the function will throw.
253 ///
254 /// This method works across navigations:
255 ///
256 /// ```js
257 /// const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
258 ///
259 /// (async () => {
260 /// const browser = await chromium.launch();
261 /// const page = await browser.newPage();
262 /// for (let currentURL of ['https://google.com', 'https://bbc.com']) {
263 /// await page.goto(currentURL);
264 /// const element = await page.mainFrame().waitForSelector('img');
265 /// console.log('Loaded image: ' + await element.getAttribute('src'));
266 /// }
267 /// await browser.close();
268 /// })();
269 /// ```
270 pub fn wait_for_selector_builder<'a>(&self, selector: &'a str) -> WaitForSelectorBuilder<'a> {
271 WaitForSelectorBuilder::new(self.inner.clone(), selector)
272 }
273
274 pub async fn title(&self) -> ArcResult<String> { upgrade(&self.inner)?.title().await }
275
276 /// Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text. `frame.type` can be used to
277 /// send fine-grained keyboard events. To fill values in form fields, use [`method: Frame.fill`].
278 ///
279 /// To press a special key, like `Control` or `ArrowDown`, use [`method: Keyboard.press`].
280 ///
281 /// ```js
282 /// await frame.type('#mytextarea', 'Hello'); // Types instantly
283 /// await frame.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
284 /// ```
285 pub fn type_builder<'a, 'b>(&self, selector: &'a str, text: &'b str) -> TypeBuilder<'a, 'b> {
286 TypeBuilder::new(self.inner.clone(), selector, text)
287 }
288
289 /// `key` can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
290 /// value or a single character to generate the text for. A superset of the `key` values can be found
291 /// [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:
292 ///
293 /// `F1` - `F12`, `Digit0`- `Digit9`, `KeyA`- `KeyZ`, `Backquote`, `Minus`, `Equal`, `Backslash`, `Backspace`, `Tab`,
294 /// `Delete`, `Escape`, `ArrowDown`, `End`, `Enter`, `Home`, `Insert`, `PageDown`, `PageUp`, `ArrowRight`, `ArrowUp`, etc.
295 ///
296 /// Following modification shortcuts are also supported: `Shift`, `Control`, `Alt`, `Meta`, `ShiftLeft`.
297 ///
298 /// Holding down `Shift` will type the text that corresponds to the `key` in the upper case.
299 ///
300 /// If `key` is a single character, it is case-sensitive, so the values `a` and `A` will generate different respective
301 /// texts.
302 ///
303 /// Shortcuts such as `key: "Control+o"` or `key: "Control+Shift+T"` are supported as well. When specified with the
304 /// modifier, modifier is pressed and being held while the subsequent key is being pressed.
305 pub fn press_builder<'a, 'b>(&self, selector: &'a str, key: &'b str) -> PressBuilder<'a, 'b> {
306 PressBuilder::new(self.inner.clone(), selector, key)
307 }
308
309 /// This method hovers over an element matching `selector` by performing the following steps:
310 /// 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
311 /// 1. Wait for [actionability](https://playwright.dev/docs/actionability/) checks on the matched element, unless `force` option is set. If the
312 /// element is detached during the checks, the whole action is retried.
313 /// 1. Scroll the element into view if needed.
314 /// 1. Use [`property: Page.mouse`] to hover over the center of the element, or the specified `position`.
315 /// 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
316 ///
317 /// When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
318 /// zero timeout disables this.
319 pub fn hover_builder<'a>(&self, selector: &'a str) -> HoverBuilder<'a> {
320 HoverBuilder::new(self.inner.clone(), selector)
321 }
322
323 is_checked! {is_checked}
324 is_checked! {is_disabled}
325 is_checked! {is_editable}
326 is_checked! {is_enabled}
327 is_checked! {is_hidden}
328 is_checked! {is_visible}
329
330 /// Gets the full HTML contents of the frame, including the doctype.
331 pub async fn content<'a>(&self) -> ArcResult<String> { upgrade(&self.inner)?.content().await }
332
333 pub fn set_content_builder<'a>(&self, html: &'a str) -> SetContentBuilder<'a> {
334 SetContentBuilder::new(self.inner.clone(), html)
335 }
336
337 /// This method checks an element matching `selector` by performing the following steps:
338 /// 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
339 /// 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is already
340 /// checked, this method returns immediately.
341 /// 1. Wait for actionability checks on the matched element, unless `force` option is set. If the
342 /// element is detached during the checks, the whole action is retried.
343 /// 1. Scroll the element into view if needed.
344 /// 1. Use [`property: Page.mouse`] to click in the center of the element.
345 /// 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
346 /// 1. Ensure that the element is now checked. If not, this method throws.
347 ///
348 /// When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
349 /// zero timeout disables this.
350 pub fn check_builder<'a>(&self, selector: &'a str) -> CheckBuilder<'a> {
351 CheckBuilder::new(self.inner.clone(), selector)
352 }
353
354 /// This method checks an element matching `selector` by performing the following steps:
355 /// 1. Find an element matching `selector`. If there is none, wait until a matching element is attached to the DOM.
356 /// 1. Ensure that matched element is a checkbox or a radio input. If not, this method throws. If the element is already
357 /// unchecked, this method returns immediately.
358 /// 1. Wait for [actionability](https://playwright.dev/docs/actionability/) checks on the matched element, unless `force` option is set. If the
359 /// element is detached during the checks, the whole action is retried.
360 /// 1. Scroll the element into view if needed.
361 /// 1. Use [`property: Page.mouse`] to click in the center of the element.
362 /// 1. Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
363 /// 1. Ensure that the element is now unchecked. If not, this method throws.
364 ///
365 /// When all steps combined have not finished during the specified `timeout`, this method throws a `TimeoutError`. Passing
366 /// zero timeout disables this.
367 pub fn uncheck_builder<'a>(&self, selector: &'a str) -> UncheckBuilder<'a> {
368 UncheckBuilder::new(self.inner.clone(), selector)
369 }
370
371 // = |timeout| async { sleep(timeout).await }
372 pub async fn wait_for_timeout(&self, timeout: f64) {
373 sleep(std::time::Duration::from_millis(timeout as u64)).await
374 }
375
376 /// Returns the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.
377 ///
378 /// Adds a `<link rel="stylesheet">` tag into the page with the desired url or a `<style type="text/css">` tag with the
379 /// content.
380 pub async fn add_style_tag(
381 &self,
382 content: &str,
383 url: Option<&str>
384 ) -> ArcResult<ElementHandle> {
385 upgrade(&self.inner)?
386 .add_style_tag(content, url)
387 .await
388 .map(ElementHandle::new)
389 }
390
391 /// Returns the added tag when the script's onload fires or when the script content was injected into frame.
392 ///
393 /// Adds a `<script>` tag into the page with the desired url or content.
394 pub fn add_script_tag_builder<'a>(&self, content: &'a str) -> AddScriptTagBuilder<'a, '_, '_> {
395 AddScriptTagBuilder::new(self.inner.clone(), content)
396 }
397
398 pub async fn evaluate_element_handle<T>(
399 &self,
400 expression: &str,
401 args: Option<T>
402 ) -> ArcResult<ElementHandle>
403 where
404 T: Serialize
405 {
406 upgrade(&self.inner)?
407 .evaluate_element_handle(expression, args)
408 .await
409 .map(ElementHandle::new)
410 }
411
412 /// Returns the return value of `expression` as a `JSHandle`.
413 ///
414 /// The only difference between [`method: Frame.evaluate`] and [`method: Frame.evaluateHandle`] is that
415 /// [`method: Frame.evaluateHandle`] returns `JSHandle`.
416 ///
417 /// If the function, passed to the [`method: Frame.evaluateHandle`], returns a Promise, then
418 /// [`method: Frame.evaluateHandle`] would wait for the promise to resolve and return its value.
419 ///
420 /// ```js
421 /// const aWindowHandle = await frame.evaluateHandle(() => Promise.resolve(window));
422 /// aWindowHandle; // Handle for the window object.
423 /// ```
424 ///
425 /// A string can also be passed in instead of a function.
426 /// ```js
427 /// const aHandle = await frame.evaluateHandle('document'); // Handle for the 'document'.
428 /// ```
429 ///
430 /// `JSHandle` instances can be passed as an argument to the [`method: Frame.evaluateHandle`]:
431 /// ```js
432 /// const aHandle = await frame.evaluateHandle(() => document.body);
433 /// const resultHandle = await frame.evaluateHandle(([body, suffix]) => body.innerHTML + suffix, [aHandle, 'hello']);
434 /// console.log(await resultHandle.jsonValue());
435 /// await resultHandle.dispose();
436 /// ```
437 pub async fn evaluate_js_handle<T>(
438 &self,
439 expression: &str,
440 arg: Option<T>
441 ) -> ArcResult<JsHandle>
442 where
443 T: Serialize
444 {
445 upgrade(&self.inner)?
446 .evaluate_js_handle(expression, arg)
447 .await
448 .map(JsHandle::new)
449 }
450
451 pub async fn eval<U>(&self, expression: &str) -> ArcResult<U>
452 where
453 U: DeserializeOwned
454 {
455 upgrade(&self.inner)?.eval(expression).await
456 }
457
458 /// Returns the return value of `expression`.
459 ///
460 /// If the function passed to the [`method: Frame.evaluate`] returns a Promise, then [`method: Frame.evaluate`] would wait
461 /// for the promise to resolve and return its value.
462 ///
463 /// If the function passed to the [`method: Frame.evaluate`] returns a non-Serializable value, then
464 /// [`method: Frame.evaluate`] returns `undefined`. Playwright also supports transferring some additional values that are
465 /// not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`.
466 ///
467 /// ```js
468 /// const result = await frame.evaluate(([x, y]) => {
469 /// return Promise.resolve(x * y);
470 /// }, [7, 8]);
471 /// console.log(result); // prints "56"
472 /// ```
473 ///
474 /// `ElementHandle` instances can be passed as an argument to the [`method: Frame.evaluate`]:
475 ///
476 /// ```js
477 /// const bodyHandle = await frame.$('body');
478 /// const html = await frame.evaluate(([body, suffix]) => body.innerHTML + suffix, [bodyHandle, 'hello']);
479 /// await bodyHandle.dispose();
480 /// ```
481 pub async fn evaluate<T, U>(&self, expression: &str, arg: T) -> ArcResult<U>
482 where
483 T: Serialize,
484 U: DeserializeOwned
485 {
486 upgrade(&self.inner)?.evaluate(expression, Some(arg)).await
487 }
488
489 /// Returns the return value of `expression`.
490 ///
491 /// The method finds an element matching the specified selector within the frame and passes it as a first argument to
492 /// `expression`. If no elements match the selector, the
493 /// method throws an error.
494 ///
495 /// If `expression` returns a Promise, then [`method: Frame.evalOnSelector`] would wait for the promise to resolve and
496 /// return its value.
497 ///
498 /// Examples:
499 ///
500 /// ```js
501 /// const searchValue = await frame.$eval('#search', el => el.value);
502 /// const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
503 /// const html = await frame.$eval('.main-container', (e, suffix) => e.outerHTML + suffix, 'hello');
504 /// ```
505 pub async fn evaluate_on_selector<T, U>(
506 &self,
507 selector: &str,
508 expression: &str,
509 arg: Option<T>
510 ) -> ArcResult<U>
511 where
512 T: Serialize,
513 U: DeserializeOwned
514 {
515 upgrade(&self.inner)?
516 .evaluate_on_selector(selector, expression, arg)
517 .await
518 }
519
520 /// Returns the return value of `expression`.
521 ///
522 /// The method finds all elements matching the specified selector within the frame and passes an array of matched elements
523 /// as a first argument to `expression`.
524 ///
525 /// If `expression` returns a Promise, then [`method: Frame.evalOnSelectorAll`] would wait for the promise to resolve and
526 /// return its value.
527 ///
528 /// Examples:
529 ///
530 /// ```js
531 /// const divsCounts = await frame.$$eval('div', (divs, min) => divs.length >= min, 10);
532 /// ```
533 pub async fn evaluate_on_selector_all<T, U>(
534 &self,
535 selector: &str,
536 expression: &str,
537 arg: Option<T>
538 ) -> ArcResult<U>
539 where
540 T: Serialize,
541 U: DeserializeOwned
542 {
543 upgrade(&self.inner)?
544 .evaluate_on_selector_all(selector, expression, arg)
545 .await
546 }
547
548 /// The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element,
549 /// `click` is dispatched. This is equivalent to calling
550 /// [element.click()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click).
551 ///
552 /// ```js
553 /// await frame.dispatchEvent('button#submit', 'click');
554 /// ```
555 ///
556 /// Under the hood, it creates an instance of an event based on the given `type`, initializes it with `eventInit` properties
557 /// and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default.
558 ///
559 /// Since `eventInit` is event-specific, please refer to the events documentation for the lists of initial properties:
560 /// - [DragEvent](https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent)
561 /// - [FocusEvent](https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent)
562 /// - [KeyboardEvent](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent)
563 /// - [MouseEvent](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
564 /// - [PointerEvent](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent)
565 /// - [TouchEvent](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent)
566 /// - [Event](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)
567 ///
568 /// You can also specify `JSHandle` as the property value if you want live objects to be passed into the event:
569 ///
570 /// ```js
571 ///// Note you can only create DataTransfer in Chromium and Firefox
572 /// const dataTransfer = await frame.evaluateHandle(() => new DataTransfer());
573 /// await frame.dispatchEvent('#source', 'dragstart', { dataTransfer });
574 /// ```
575 pub async fn dispatch_event<T>(
576 &self,
577 selector: &str,
578 r#type: &str,
579 event_init: Option<T>
580 ) -> ArcResult<()>
581 where
582 T: Serialize
583 {
584 // timeout not supported
585 upgrade(&self.inner)?
586 .dispatch_event(selector, r#type, event_init)
587 .await
588 }
589
590 /// This method waits for an element matching `selector`, waits for [actionability](https://playwright.dev/docs/actionability/) checks, waits until
591 /// all specified options are present in the `<select>` element and selects these options.
592 ///
593 /// If the target element is not a `<select>` element, this method throws an error. However, if the element is inside the
594 /// `<label>` element that has an associated
595 /// [control](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/control), the control will be used instead.
596 ///
597 /// Returns the array of option values that have been successfully selected.
598 ///
599 /// Triggers a `change` and `input` event once all the provided options have been selected.
600 ///
601 /// ```js
602 ///// single selection matching the value
603 /// frame.selectOption('select#colors', 'blue');
604 ///// single selection matching both the value and the label
605 /// frame.selectOption('select#colors', { label: 'Blue' });
606 ///// multiple selection
607 /// frame.selectOption('select#colors', 'red', 'green', 'blue');
608 /// ```
609 pub fn select_option_builder<'a>(&self, selector: &'a str) -> SelectOptionBuilder<'a> {
610 SelectOptionBuilder::new(self.inner.clone(), selector)
611 }
612
613 /// This method expects `selector` to point to an
614 /// [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
615 ///
616 /// Sets the value of the file input to these file paths or files. If some of the `filePaths` are relative paths, then they
617 /// are resolved relative to the the current working directory. For empty array, clears the selected files.
618 pub fn set_input_files_builder<'a>(
619 &self,
620 selector: &'a str,
621 file: File
622 ) -> SetInputFilesBuilder<'a> {
623 SetInputFilesBuilder::new(self.inner.clone(), selector, file)
624 }
625
626 /// Returns when the `expression` returns a truthy value, returns that value.
627 ///
628 /// The [`method: Frame.waitForFunction`] can be used to observe viewport size change:
629 ///
630 /// ```js
631 /// const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
632 ///
633 /// (async () => {
634 /// const browser = await firefox.launch();
635 /// const page = await browser.newPage();
636 /// const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
637 /// page.setViewportSize({width: 50, height: 50});
638 /// await watchDog;
639 /// await browser.close();
640 /// })();
641 /// ```
642 pub fn wait_for_function_builder<'a>(&self, expression: &'a str) -> WaitForFunctionBuilder<'a> {
643 WaitForFunctionBuilder::new(self.inner.clone(), expression)
644 }
645
646 subscribe_event! {}
647
648 // wait_for_url
649}
650
651#[derive(Debug)]
652pub enum Event {
653 LoadState(DocumentLoadState),
654 Navigated(FrameNavigatedEvent)
655}
656
657impl From<Evt> for Event {
658 fn from(e: Evt) -> Self {
659 match e {
660 Evt::LoadState(x) => Self::LoadState(x),
661 Evt::Navigated(x) => Self::Navigated(x)
662 }
663 }
664}
665
666pub struct GotoBuilder<'a, 'b> {
667 inner: Weak<Impl>,
668 args: GotoArgs<'a, 'b>
669}
670
671impl<'a, 'b> GotoBuilder<'a, 'b> {
672 pub(crate) fn new(inner: Weak<Impl>, url: &'a str) -> Self {
673 let args = GotoArgs::new(url);
674 Self { inner, args }
675 }
676
677 pub async fn goto(self) -> Result<Option<Response>, Arc<Error>> {
678 let Self { inner, args } = self;
679 let r = upgrade(&inner)?.goto(args).await?;
680 Ok(r.map(Response::new))
681 }
682
683 setter! {
684 /// Referer header value. If provided it will take preference over the referer header value set by
685 /// [`method: Page.setExtraHTTPHeaders`].
686 referer: Option<&'b str>,
687 timeout: Option<f64>,
688 wait_until: Option<DocumentLoadState>
689 }
690}
691
692macro_rules! clicker {
693 ($t: ident, $f: ident) => {
694 pub struct $t<'a> {
695 inner: Weak<Impl>,
696 args: ClickArgs<'a>
697 }
698
699 impl<'a> $t<'a> {
700 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str) -> Self {
701 let args = ClickArgs::new(selector);
702 Self { inner, args }
703 }
704
705 pub async fn $f(self) -> Result<(), Arc<Error>> {
706 let Self { inner, args } = self;
707 let _ = upgrade(&inner)?.$f(args).await?;
708 Ok(())
709 }
710
711 setter! {
712 /// Defaults to `left`.
713 button: Option<MouseButton>,
714 /// defaults to 1. See [UIEvent.detail].
715 click_count: Option<i32>,
716 /// Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
717 delay: Option<f64>,
718 /// Whether to bypass the [actionability](https://playwright.dev/docs/actionability/) checks. Defaults to `false`.
719 force: Option<bool>,
720 /// Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
721 /// modifiers back. If not specified, currently pressed modifiers are used.
722 modifiers: Option<Vec<KeyboardModifier>>,
723 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
724 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
725 /// inaccessible pages. Defaults to `false`.
726 no_wait_after: Option<bool>,
727 /// A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
728 /// element.
729 position: Option<Position>,
730 timeout: Option<f64>,
731 /// When set, this method only performs the [actionability](https://playwright.dev/docs/actionability/) checks and skips the action. Defaults to
732 /// `false`. Useful to wait until the element is ready for the action without performing it.
733 trial: Option<bool>
734 }
735 }
736 };
737}
738
739clicker!(ClickBuilder, click);
740clicker!(DblClickBuilder, dblclick);
741
742pub struct WaitForSelectorBuilder<'a> {
743 inner: Weak<Impl>,
744 args: WaitForSelectorArgs<'a>
745}
746
747impl<'a> WaitForSelectorBuilder<'a> {
748 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str) -> Self {
749 let args = WaitForSelectorArgs::new(selector);
750 Self { inner, args }
751 }
752
753 pub async fn wait_for_selector(self) -> Result<Option<ElementHandle>, Arc<Error>> {
754 let Self { inner, args } = self;
755 let e = upgrade(&inner)?.wait_for_selector(args).await?;
756 Ok(e.map(ElementHandle::new))
757 }
758
759 setter! {
760 /// Defaults to `'visible'`.
761 state: Option<FrameState>,
762 timeout: Option<f64>
763 }
764}
765
766macro_rules! type_builder {
767 ($t: ident, $a: ident, $f: ident, $m: ident) => {
768 pub struct $t<'a, 'b> {
769 inner: Weak<Impl>,
770 args: $a<'a, 'b>
771 }
772
773 impl<'a, 'b> $t<'a, 'b> {
774 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str, $f: &'b str) -> Self {
775 let args = $a::new(selector, $f);
776 Self { inner, args }
777 }
778
779 pub async fn $m(self) -> Result<(), Arc<Error>> {
780 let Self { inner, args } = self;
781 let _ = upgrade(&inner)?.$m(args).await?;
782 Ok(())
783 }
784
785 setter! {
786 /// Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
787 delay: Option<f64>,
788 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
789 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
790 /// inaccessible pages. Defaults to `false`.
791 no_wait_after: Option<bool>,
792 timeout: Option<f64>
793 }
794 }
795 };
796}
797
798type_builder!(TypeBuilder, TypeArgs, text, r#type);
799type_builder!(PressBuilder, PressArgs, key, press);
800
801pub struct HoverBuilder<'a> {
802 inner: Weak<Impl>,
803 args: HoverArgs<'a>
804}
805
806impl<'a> HoverBuilder<'a> {
807 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str) -> Self {
808 let args = HoverArgs::new(selector);
809 Self { inner, args }
810 }
811
812 pub async fn goto(self) -> Result<(), Arc<Error>> {
813 let Self { inner, args } = self;
814 upgrade(&inner)?.hover(args).await
815 }
816
817 setter! {
818 /// Whether to bypass the actionability checks. Defaults to `false`.
819 force: Option<bool>,
820 /// Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
821 /// modifiers back. If not specified, currently pressed modifiers are used.
822 modifiers: Option<Vec<KeyboardModifier>>,
823 /// A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
824 /// element.
825 position: Option<Position>,
826 timeout: Option<f64>,
827 /// When set, this method only performs the [actionability](https://playwright.dev/docs/actionability/) checks and skips the action. Defaults to
828 /// `false`. Useful to wait until the element is ready for the action without performing it.
829 trial: Option<bool>
830 }
831}
832
833pub struct SetContentBuilder<'a> {
834 inner: Weak<Impl>,
835 args: SetContentArgs<'a>
836}
837
838impl<'a> SetContentBuilder<'a> {
839 pub(crate) fn new(inner: Weak<Impl>, html: &'a str) -> Self {
840 let args = SetContentArgs::new(html);
841 Self { inner, args }
842 }
843
844 pub async fn set_content(self) -> Result<(), Arc<Error>> {
845 let Self { inner, args } = self;
846 upgrade(&inner)?.set_content(args).await
847 }
848
849 setter! {
850 timeout: Option<f64>,
851 wait_until: Option<DocumentLoadState>
852 }
853}
854
855pub struct TapBuilder<'a> {
856 inner: Weak<Impl>,
857 args: TapArgs<'a>
858}
859
860impl<'a> TapBuilder<'a> {
861 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str) -> Self {
862 let args = TapArgs::new(selector);
863 Self { inner, args }
864 }
865
866 pub async fn tap(self) -> Result<(), Arc<Error>> {
867 let Self { inner, args } = self;
868 let _ = upgrade(&inner)?.tap(args).await?;
869 Ok(())
870 }
871
872 setter! {
873 /// Whether to bypass the actionability checks. Defaults to `false`.
874 force: Option<bool>,
875 /// Modifier keys to press. Ensures that only these modifiers are pressed during the operation, and then restores current
876 /// modifiers back. If not specified, currently pressed modifiers are used.
877 modifiers: Option<Vec<KeyboardModifier>>,
878 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
879 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
880 /// inaccessible pages. Defaults to `false`.
881 no_wait_after: Option<bool>,
882 /// A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
883 /// element.
884 position: Option<Position>,
885 timeout: Option<f64>,
886 /// When set, this method only performs the [actionability](https://playwright.dev/docs/actionability/) checks and skips the action. Defaults to
887 /// `false`. Useful to wait until the element is ready for the action without performing it.
888 trial: Option<bool>
889 }
890}
891
892pub struct FillBuilder<'a, 'b> {
893 inner: Weak<Impl>,
894 args: FillArgs<'a, 'b>
895}
896
897impl<'a, 'b> FillBuilder<'a, 'b> {
898 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str, value: &'b str) -> Self {
899 let args = FillArgs::new(selector, value);
900 Self { inner, args }
901 }
902
903 pub async fn fill(self) -> Result<(), Arc<Error>> {
904 let Self { inner, args } = self;
905 let _ = upgrade(&inner)?.fill(args).await?;
906 Ok(())
907 }
908
909 setter! {
910 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
911 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
912 /// inaccessible pages. Defaults to `false`.
913 no_wait_after: Option<bool>,
914 timeout: Option<f64>
915 }
916}
917
918macro_rules! check_builder {
919 ($t: ident, $m: ident) => {
920 pub struct $t<'a> {
921 inner: Weak<Impl>,
922 args: CheckArgs<'a>
923 }
924
925 impl<'a> $t<'a> {
926 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str) -> Self {
927 let args = CheckArgs::new(selector);
928 Self { inner, args }
929 }
930
931 pub async fn $m(self) -> Result<(), Arc<Error>> {
932 let Self { inner, args } = self;
933 let _ = upgrade(&inner)?.$m(args).await?;
934 Ok(())
935 }
936
937 setter! {
938 /// A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the element.
939 position: Option<Position>,
940 /// Whether to bypass the actionability checks. Defaults to `false`.
941 force: Option<bool>,
942 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
943 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
944 /// inaccessible pages. Defaults to `false`.
945 no_wait_after: Option<bool>,
946 timeout: Option<f64>,
947 /// When set, this method only performs the [actionability](https://playwright.dev/docs/actionability/) checks and skips the action. Defaults to
948 /// `false`. Useful to wait until the element is ready for the action without performing it.
949 trial: Option<bool>
950 }
951 }
952 };
953}
954
955check_builder!(CheckBuilder, check);
956check_builder!(UncheckBuilder, uncheck);
957
958pub struct AddScriptTagBuilder<'a, 'b, 'c> {
959 inner: Weak<Impl>,
960 args: AddScriptTagArgs<'a, 'b, 'c>
961}
962
963impl<'a, 'b, 'c> AddScriptTagBuilder<'a, 'b, 'c> {
964 pub(crate) fn new(inner: Weak<Impl>, content: &'a str) -> Self {
965 let args = AddScriptTagArgs::new(content);
966 Self { inner, args }
967 }
968
969 pub async fn add_script_tag(self) -> Result<ElementHandle, Arc<Error>> {
970 let Self { inner, args } = self;
971 upgrade(&inner)?
972 .add_script_tag(args)
973 .await
974 .map(ElementHandle::new)
975 }
976
977 /// Script type. Use 'module' in order to load a Javascript ES6 module. See
978 /// [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.
979 pub fn r#type(mut self, x: &'c str) -> Self {
980 self.args.r#type = Some(x);
981 self
982 }
983
984 setter! {
985 /// URL of a script to be added.
986 url: Option<&'b str>
987 }
988
989 pub fn clear_type(mut self) -> Self {
990 self.args.r#type = None;
991 self
992 }
993}
994
995pub struct SelectOptionBuilder<'a> {
996 inner: Weak<Impl>,
997 args: SelectOptionArgs<'a>,
998 err: Option<Error>
999}
1000
1001impl<'a> SelectOptionBuilder<'a> {
1002 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str) -> Self {
1003 let args = SelectOptionArgs::new(selector);
1004 Self {
1005 inner,
1006 args,
1007 err: None
1008 }
1009 }
1010
1011 pub async fn select_option(self) -> Result<Vec<String>, Arc<Error>> {
1012 let Self { inner, args, err } = self;
1013 if let Some(e) = err {
1014 return Err(e.into());
1015 }
1016 upgrade(&inner)?.select_option(args).await
1017 }
1018
1019 pub fn add_element(mut self, x: &ElementHandle) -> Self {
1020 let guid = match x.guid() {
1021 Ok(i) => i,
1022 Err(e) => {
1023 if self.err.is_none() {
1024 self.err = Some(e);
1025 }
1026 return self;
1027 }
1028 };
1029 let x = OnlyGuid { guid };
1030 if let Some(e) = &mut self.args.elements {
1031 e.push(x);
1032 } else {
1033 self.args.elements = Some(vec![x]);
1034 }
1035 self
1036 }
1037
1038 pub fn add_value(mut self, x: String) -> Self {
1039 let x = Opt::Value(x);
1040 if let Some(o) = &mut self.args.options {
1041 o.push(x);
1042 } else {
1043 self.args.options = Some(vec![x]);
1044 }
1045 self
1046 }
1047
1048 pub fn add_index(mut self, x: usize) -> Self {
1049 let x = Opt::Index(x);
1050 if let Some(o) = &mut self.args.options {
1051 o.push(x);
1052 } else {
1053 self.args.options = Some(vec![x]);
1054 }
1055 self
1056 }
1057
1058 pub fn add_label(mut self, x: String) -> Self {
1059 let x = Opt::Label(x);
1060 if let Some(o) = &mut self.args.options {
1061 o.push(x);
1062 } else {
1063 self.args.options = Some(vec![x]);
1064 }
1065 self
1066 }
1067
1068 setter! {
1069 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
1070 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
1071 /// inaccessible pages. Defaults to `false`.
1072 no_wait_after: Option<bool>,
1073 timeout: Option<f64>
1074 }
1075
1076 pub fn clear_elements(mut self) -> Self {
1077 self.args.elements = None;
1078 self
1079 }
1080
1081 pub fn clear_options(mut self) -> Self {
1082 self.args.options = None;
1083 self
1084 }
1085}
1086
1087pub struct SetInputFilesBuilder<'a> {
1088 inner: Weak<Impl>,
1089 args: SetInputFilesArgs<'a>
1090}
1091
1092impl<'a> SetInputFilesBuilder<'a> {
1093 pub(crate) fn new(inner: Weak<Impl>, selector: &'a str, file: File) -> Self {
1094 let mut args = SetInputFilesArgs::new(selector);
1095 args.files = vec![file];
1096 Self { inner, args }
1097 }
1098
1099 pub async fn set_input_files(self) -> Result<(), Arc<Error>> {
1100 let Self { inner, args } = self;
1101 upgrade(&inner)?.set_input_files(args).await
1102 }
1103
1104 pub fn add_file(mut self, x: File) -> Self {
1105 self.args.files.push(x);
1106 self
1107 }
1108
1109 setter! {
1110 /// Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
1111 /// opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to
1112 /// inaccessible pages. Defaults to `false`.
1113 no_wait_after: Option<bool>,
1114 timeout: Option<f64>
1115 }
1116
1117 pub fn clear_files(mut self) -> Self {
1118 self.args.files = vec![];
1119 self
1120 }
1121}
1122
1123pub struct WaitForFunctionBuilder<'a> {
1124 inner: Weak<Impl>,
1125 args: WaitForFunctionArgs<'a>,
1126 err: Option<Error>
1127}
1128
1129impl<'a> WaitForFunctionBuilder<'a> {
1130 pub(crate) fn new(inner: Weak<Impl>, expression: &'a str) -> Self {
1131 let args = WaitForFunctionArgs::new(expression);
1132 Self {
1133 inner,
1134 args,
1135 err: None
1136 }
1137 }
1138
1139 pub async fn wait_for_function(self) -> Result<JsHandle, Arc<Error>> {
1140 let Self { inner, args, err } = self;
1141 if let Some(e) = err {
1142 return Err(e.into());
1143 }
1144 upgrade(&inner)?
1145 .wait_for_function(args)
1146 .await
1147 .map(JsHandle::new)
1148 }
1149
1150 pub fn arg<T>(mut self, x: &T) -> Self
1151 where
1152 T: Serialize
1153 {
1154 let arg = match ser::to_value(x).map_err(Error::SerializationPwJson) {
1155 Err(e) => {
1156 self.err = Some(e);
1157 return self;
1158 }
1159 Ok(arg) => arg
1160 };
1161 self.args.arg = Some(arg);
1162 self
1163 }
1164
1165 setter! {
1166 /// If `polling` is `'raf'`, then `expression` is constantly executed in `requestAnimationFrame` callback. If `polling` is a
1167 /// number, then it is treated as an interval in milliseconds at which the function would be executed. Defaults to `raf`.
1168 polling: Option<Polling>,
1169 timeout: Option<f64>
1170 }
1171
1172 pub fn clear_arg(mut self) -> Self {
1173 self.args.arg = None;
1174 self.err = None;
1175 self
1176 }
1177}