viewpoint_core/page/frame/
content.rs

1//! Frame content operations (get/set content, title).
2
3use tracing::{info, instrument, trace};
4use viewpoint_cdp::protocol::runtime::EvaluateParams;
5
6use super::Frame;
7use crate::error::PageError;
8
9impl Frame {
10    /// Get the frame's HTML content.
11    ///
12    /// # Errors
13    ///
14    /// Returns an error if the frame is detached or the evaluation fails.
15    #[instrument(level = "debug", skip(self), fields(frame_id = %self.id))]
16    pub async fn content(&self) -> Result<String, PageError> {
17        if self.is_detached() {
18            return Err(PageError::EvaluationFailed("Frame is detached".to_string()));
19        }
20
21        // Get the execution context ID for this frame's main world
22        let context_id = self.main_world_context_id();
23        trace!(context_id = ?context_id, "Using execution context for content()");
24
25        let result: viewpoint_cdp::protocol::runtime::EvaluateResult = self
26            .connection
27            .send_command(
28                "Runtime.evaluate",
29                Some(EvaluateParams {
30                    expression: "document.documentElement.outerHTML".to_string(),
31                    object_group: None,
32                    include_command_line_api: None,
33                    silent: Some(true),
34                    context_id,
35                    return_by_value: Some(true),
36                    await_promise: Some(false),
37                }),
38                Some(&self.session_id),
39            )
40            .await?;
41
42        result
43            .result
44            .value
45            .and_then(|v| v.as_str().map(ToString::to_string))
46            .ok_or_else(|| PageError::EvaluationFailed("Failed to get content".to_string()))
47    }
48
49    /// Get the frame's document title.
50    ///
51    /// # Errors
52    ///
53    /// Returns an error if the frame is detached or the evaluation fails.
54    #[instrument(level = "debug", skip(self), fields(frame_id = %self.id))]
55    pub async fn title(&self) -> Result<String, PageError> {
56        if self.is_detached() {
57            return Err(PageError::EvaluationFailed("Frame is detached".to_string()));
58        }
59
60        // Get the execution context ID for this frame's main world
61        let context_id = self.main_world_context_id();
62        trace!(context_id = ?context_id, "Using execution context for title()");
63
64        let result: viewpoint_cdp::protocol::runtime::EvaluateResult = self
65            .connection
66            .send_command(
67                "Runtime.evaluate",
68                Some(EvaluateParams {
69                    expression: "document.title".to_string(),
70                    object_group: None,
71                    include_command_line_api: None,
72                    silent: Some(true),
73                    context_id,
74                    return_by_value: Some(true),
75                    await_promise: Some(false),
76                }),
77                Some(&self.session_id),
78            )
79            .await?;
80
81        result
82            .result
83            .value
84            .and_then(|v| v.as_str().map(ToString::to_string))
85            .ok_or_else(|| PageError::EvaluationFailed("Failed to get title".to_string()))
86    }
87
88    /// Set the frame's HTML content.
89    ///
90    /// # Errors
91    ///
92    /// Returns an error if the frame is detached or setting content fails.
93    #[instrument(level = "info", skip(self, html), fields(frame_id = %self.id))]
94    pub async fn set_content(&self, html: &str) -> Result<(), PageError> {
95        if self.is_detached() {
96            return Err(PageError::EvaluationFailed("Frame is detached".to_string()));
97        }
98
99        use viewpoint_cdp::protocol::page::SetDocumentContentParams;
100
101        self.connection
102            .send_command::<_, serde_json::Value>(
103                "Page.setDocumentContent",
104                Some(SetDocumentContentParams {
105                    frame_id: self.id.clone(),
106                    html: html.to_string(),
107                }),
108                Some(&self.session_id),
109            )
110            .await?;
111
112        info!("Frame content set");
113        Ok(())
114    }
115}