1use crate::tab::Tab;
2use crate::transport::next_id;
3use crate::types::{CaptureOptions, ImageFormat};
4use crate::utils::{self, send_and_get_msg};
5use anyhow::{Context, Result};
6use serde_json::json;
7
8pub struct Element<'a> {
10 parent: &'a Tab,
11 backend_node_id: u64,
12}
13
14impl<'a> Element<'a> {
15 pub(crate) async fn new(parent: &'a Tab, node_id: u64) -> Result<Self> {
16 let msg_id = next_id();
17 let msg = json!({
18 "id": msg_id,
19 "method": "DOM.describeNode",
20 "params": { "nodeId": node_id, "depth": 100 }
21 })
22 .to_string();
23
24 let res =
25 send_and_get_msg(parent.transport.clone(), msg_id, &parent.session_id, msg).await?;
26 let data = utils::serde_msg(&res)?;
27 let backend_node_id = data["result"]["node"]["backendNodeId"]
28 .as_u64()
29 .context("Missing backendNodeId")?;
30
31 Ok(Self {
32 parent,
33 backend_node_id,
34 })
35 }
36
37 pub async fn screenshot(&self) -> Result<String> {
38 self.screenshot_with_options(CaptureOptions::new().with_quality(90))
39 .await
40 }
41
42 pub async fn raw_screenshot(&self) -> Result<String> {
43 self.screenshot_with_options(CaptureOptions::raw_png())
44 .await
45 }
46
47 pub async fn screenshot_with_options(&self, opts: CaptureOptions) -> Result<String> {
48 if let Some(ref viewport) = opts.viewport {
49 self.parent.set_viewport(viewport).await?;
50 }
51
52 let msg_id = next_id();
53 let msg_box = json!({
54 "id": msg_id,
55 "method": "DOM.getBoxModel",
56 "params": { "backendNodeId": self.backend_node_id }
57 })
58 .to_string();
59
60 let res_box = send_and_get_msg(
61 self.parent.transport.clone(),
62 msg_id,
63 &self.parent.session_id,
64 msg_box,
65 )
66 .await?;
67 let data_box = utils::serde_msg(&res_box)?;
68 let border = &data_box["result"]["model"]["border"];
69
70 let (x, y, w, h) = (
71 border[0].as_f64().unwrap_or(0.0),
72 border[1].as_f64().unwrap_or(0.0),
73 (border[2].as_f64().unwrap_or(0.0) - border[0].as_f64().unwrap_or(0.0)),
74 (border[5].as_f64().unwrap_or(0.0) - border[1].as_f64().unwrap_or(0.0)),
75 );
76
77 let mut params = json!({
78 "format": opts.format.as_str(),
79 "clip": { "x": x, "y": y, "width": w, "height": h, "scale": 1.0 },
80 "fromSurface": true,
81 "captureBeyondViewport": opts.full_page,
82 });
83
84 if matches!(opts.format, ImageFormat::Jpeg | ImageFormat::WebP) {
85 params["quality"] = json!(opts.quality.unwrap_or(90));
86 }
87
88 if opts.omit_background && matches!(opts.format, ImageFormat::Png) {
89 let msg_id = next_id();
90 let msg = json!({
91 "id": msg_id,
92 "method": "Emulation.setDefaultBackgroundColorOverride",
93 "params": { "color": { "r": 0, "g": 0, "b": 0, "a": 0 } }
94 })
95 .to_string();
96 send_and_get_msg(
97 self.parent.transport.clone(),
98 msg_id,
99 &self.parent.session_id,
100 msg,
101 )
102 .await?;
103 }
104
105 let msg_id = next_id();
106 let msg_cap = json!({
107 "id": msg_id,
108 "method": "Page.captureScreenshot",
109 "params": params
110 })
111 .to_string();
112
113 self.parent.activate().await?;
114 let res_cap = send_and_get_msg(
115 self.parent.transport.clone(),
116 msg_id,
117 &self.parent.session_id,
118 msg_cap,
119 )
120 .await?;
121 let data_cap = utils::serde_msg(&res_cap)?;
122
123 if opts.omit_background && matches!(opts.format, ImageFormat::Png) {
124 let msg_id = next_id();
125 let msg = json!({
126 "id": msg_id,
127 "method": "Emulation.setDefaultBackgroundColorOverride",
128 "params": {}
129 })
130 .to_string();
131 let _ = send_and_get_msg(
132 self.parent.transport.clone(),
133 msg_id,
134 &self.parent.session_id,
135 msg,
136 )
137 .await;
138 }
139
140 data_cap["result"]["data"]
141 .as_str()
142 .map(|s| s.to_string())
143 .context("No image data received")
144 }
145
146 pub fn backend_node_id(&self) -> u64 {
147 self.backend_node_id
148 }
149}