1use serde_json::json;
2use anyhow::{Context, Result};
3
4use crate::tab::Tab;
5use crate::general_utils;
6use crate::general_utils::next_id;
7
8#[derive(Debug)]
10struct ScreenshotConfig {
11 format: &'static str,
12 quality: Option<u8>,
13}
14
15impl Default for ScreenshotConfig {
16 fn default() -> Self {
17 Self {
18 format: "png",
19 quality: None,
20 }
21 }
22}
23
24pub struct Element<'a> {
26 parent: &'a Tab,
27 backend_node_id: u64,
31 }
34
35impl<'a> Element<'a> {
36 pub(crate) async fn new(parent: &'a Tab, node_id: u64) -> Result<Self> {
37 let msg_id = next_id();
38 let msg = json!({
39 "id": msg_id,
40 "method": "DOM.describeNode",
41 "params": {
42 "nodeId": node_id,
43 "depth": 100
44 }
45 }).to_string();
46
47 let res = general_utils::send_and_get_msg(parent.transport.clone(), msg_id, &parent.session_id, msg).await?;
48
49 let msg = general_utils::serde_msg(&res);
50
51 let node = msg["result"]
52 .get("node")
53 .context("Failed to get node")?;
54
55 let backend_node_id = node
70 .get("backendNodeId")
71 .context("Failed to get backendNodeId")?
72 .as_u64()
73 .context("Failed to convert backendNodeId to u64")?;
74
75 Ok(Self {
104 parent,
105 backend_node_id,
110 })
112 }
113
114 async fn get_box_model_dimensions(&self) -> Result<(f64, f64, f64, f64)> {
116 let msg_id = next_id();
117 let msg = json!({
118 "id": msg_id,
119 "method": "DOM.getBoxModel",
120 "params": {
121 "backendNodeId": self.backend_node_id
122 }
123 }).to_string();
124
125 let res = general_utils::send_and_get_msg(
126 self.parent.transport.clone(),
127 msg_id,
128 &self.parent.session_id,
129 msg
130 ).await?;
131
132 let msg = general_utils::serde_msg(&res);
133 let model = msg["result"]
134 .get("model")
135 .context("Failed to get model")?;
136
137 Ok((
138 model["border"][0].as_f64().unwrap(), model["border"][1].as_f64().unwrap(), model["border"][2].as_f64().unwrap(), model["border"][5].as_f64().unwrap() ))
143 }
144
145 async fn take_screenshot_with_config(&self, config: ScreenshotConfig) -> Result<String> {
147 let (top_left_x, top_left_y, top_right_x, bottom_left_y) =
148 self.get_box_model_dimensions().await?;
149
150 let mut params = json!({
151 "format": config.format,
152 "clip": {
153 "x": top_left_x,
154 "y": top_left_y,
155 "width": top_right_x - top_left_x,
156 "height": bottom_left_y - top_left_y,
157 "scale": 1.0
158 },
159 "fromSurface": true,
160 "captureBeyondViewport": true,
161 });
162
163 if config.format == "jpeg" {
164 if let Some(quality) = config.quality {
165 params["quality"] = json!(quality);
166 }
167 }
168
169 let msg_id = next_id();
170 let msg = json!({
171 "id": msg_id,
172 "method": "Page.captureScreenshot",
173 "params": params
174 }).to_string();
175
176 self.parent.activate().await?;
177 let res = general_utils::send_and_get_msg(
178 self.parent.transport.clone(),
179 msg_id,
180 &self.parent.session_id,
181 msg
182 ).await?;
183
184 let msg = general_utils::serde_msg(&res);
185 let base64 = msg["result"]
186 .get("data")
187 .context("Failed to get data")?
188 .as_str()
189 .context("Failed to convert data to string")?
190 .to_string();
191
192 Ok(base64)
193 }
194
195 pub async fn screenshot(&self) -> Result<String> {
197 self.take_screenshot_with_config(ScreenshotConfig {
198 format: "jpeg",
199 quality: Some(90),
200 }).await
201 }
202
203 pub async fn raw_screenshot(&self) -> Result<String> {
205 self.take_screenshot_with_config(ScreenshotConfig::default()).await
206 }
207}