Skip to main content

playwright_cdp/
frame.rs

1//! `Frame` — a frame within a page (the main document, or an `<iframe>`).
2
3use crate::element_handle::ElementHandle;
4use crate::error::{Error, Result};
5use crate::options::{GotoOptions, WaitUntil};
6use crate::page::Page;
7use crate::response::Response;
8use crate::types::AriaRole;
9use serde_json::Value;
10
11/// A frame in a page. The main frame wraps the page's top-level document.
12#[derive(Clone)]
13pub struct Frame {
14    page: Page,
15    frame_id: String,
16}
17
18impl Frame {
19    pub(crate) fn new(page: Page, frame_id: String) -> Self {
20        Self { page, frame_id }
21    }
22
23    pub(crate) fn main(page: Page) -> Self {
24        let frame_id = page.main_frame_id().unwrap_or_default();
25        Self { page, frame_id }
26    }
27
28    /// The owning page.
29    pub fn page(&self) -> Page {
30        self.page.clone()
31    }
32
33    /// CDP frame id.
34    pub fn frame_id(&self) -> &str {
35        &self.frame_id
36    }
37
38    pub fn name(&self) -> String {
39        self.page
40            .frame_data(&self.frame_id)
41            .map(|d| d.name)
42            .unwrap_or_default()
43    }
44
45    pub fn url(&self) -> String {
46        self.page
47            .frame_data(&self.frame_id)
48            .map(|d| d.url)
49            .unwrap_or_default()
50    }
51
52    pub fn parent_frame(&self) -> Option<Frame> {
53        let parent = self.page.frame_data(&self.frame_id)?.parent_id.clone()?;
54        Some(Frame::new(self.page.clone(), parent))
55    }
56
57    pub fn child_frames(&self) -> Vec<Frame> {
58        self.page
59            .frame_ids_with_parent(&self.frame_id)
60            .into_iter()
61            .map(|id| Frame::new(self.page.clone(), id))
62            .collect()
63    }
64
65    pub fn is_detached(&self) -> bool {
66        self.page
67            .frame_data(&self.frame_id)
68            .map(|d| d.detached)
69            .unwrap_or(false)
70    }
71
72    /// Evaluate `expression` in this frame's main world.
73    pub async fn evaluate<R: serde::de::DeserializeOwned>(&self, expression: &str) -> Result<R> {
74        self.page.evaluate::<R>(expression).await
75    }
76
77    /// Navigate this frame to `url` (main frame only).
78    pub async fn goto(
79        &self,
80        url: &str,
81        opts: Option<GotoOptions>,
82    ) -> Result<Option<Response>> {
83        if self.frame_id != self.page.main_frame_id().unwrap_or_default() {
84            return Err(Error::InvalidArgument(
85                "Frame::goto on non-main frames is not supported".into(),
86            ));
87        }
88        self.page.goto(url, opts).await
89    }
90
91    pub async fn wait_for_load_state(&self, state: Option<WaitUntil>) -> Result<()> {
92        self.page.wait_for_load_state(state).await
93    }
94
95    /// Poll `expression` until truthy (delegates to the page). See
96    /// [`Page::wait_for_function`].
97    pub async fn wait_for_function<R: serde::de::DeserializeOwned>(
98        &self,
99        expression: &str,
100        arg: Option<serde_json::Value>,
101        options: Option<crate::options::WaitForFunctionOptions>,
102    ) -> Result<R> {
103        self.page.wait_for_function(expression, arg, options).await
104    }
105
106    pub async fn content(&self) -> Result<String> {
107        self.page.content().await
108    }
109
110    pub async fn set_content(&self, html: &str) -> Result<()> {
111        self.page.set_content(html).await
112    }
113
114    pub async fn title(&self) -> Result<String> {
115        self.page.title().await
116    }
117
118    pub fn locator(&self, selector: impl Into<String>) -> crate::locator::Locator {
119        crate::locator::Locator::new(self.page.clone(), selector.into(), true, None)
120    }
121
122    pub fn get_by_text(&self, text: &str, exact: bool) -> crate::locator::Locator {
123        self.page.get_by_text(text, exact)
124    }
125
126    pub fn get_by_label(&self, text: &str) -> crate::locator::Locator {
127        self.page.get_by_label(text)
128    }
129
130    pub fn get_by_role(
131        &self,
132        role: AriaRole,
133        opts: Option<crate::options::GetByRoleOptions>,
134    ) -> crate::locator::Locator {
135        self.page.get_by_role(role, opts)
136    }
137
138    /// The first element matching `selector`, if any.
139    pub async fn query_selector(&self, selector: &str) -> Result<Option<ElementHandle>> {
140        self.page.query_selector(selector).await
141    }
142
143    /// All elements matching `selector`.
144    pub async fn query_selector_all(&self, selector: &str) -> Result<Vec<ElementHandle>> {
145        self.page.query_selector_all(selector).await
146    }
147
148    /// Keep the `Value` import referenced (used by callers wiring args).
149    #[allow(dead_code)]
150    fn _json_marker(&self) -> Value {
151        serde_json::json!({})
152    }
153}