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}