1use crate::proto::tab_session_command::Command as TabCommand;
2use crate::proto::tab_session_event::Event as TabEvent;
3use crate::proto::{
4 ClickElementCommand, FillElementCommand, FocusElementCommand, HoverElementCommand,
5 PressKeyCommand, TabSessionCommand,
6};
7
8use super::command::command_retry_options;
9use super::selectors::normalize_selector_for_transport;
10use super::tab::ensure_tab_open;
11use super::types::{
12 ClickResult, CommandOptions, ElementResult, Error, FillResult, PressOptions, PressResult,
13 Result, Tab,
14};
15
16impl Tab {
17 pub async fn click(&self, css_selector: impl Into<String>) -> Result<ClickResult> {
18 self.click_with_options(css_selector, CommandOptions::default()).await
19 }
20
21 pub async fn click_with_options(
22 &self,
23 css_selector: impl Into<String>,
24 options: CommandOptions,
25 ) -> Result<ClickResult> {
26 let css_selector = normalize_selector_for_transport(&css_selector.into());
27 let mut state = self.inner.state.lock().await;
28 let handle = self.ensure_handle(&mut state).await?;
29 ensure_tab_open(handle, &self.inner.session_id)?;
30
31 handle
32 .command_tx
33 .send(TabSessionCommand {
34 browser_session_id: self.inner.browser_session_id.clone(),
35 tab_session_id: self.inner.session_id.clone(),
36 command: Some(TabCommand::ClickElement(ClickElementCommand {
37 css_selector,
38 retry_options: command_retry_options(options.timeout_ms),
39 })),
40 })
41 .await
42 .map_err(|_| Error::new("failed to send ClickElementCommand"))?;
43
44 loop {
45 let event = handle
46 .events
47 .message()
48 .await?
49 .ok_or_else(|| Error::new("tab session closed while waiting for click result"))?;
50
51 match event.event {
52 Some(TabEvent::Attached(_)) => {}
53 Some(TabEvent::ElementClicked(clicked)) => {
54 return Ok(ClickResult {
55 selector: clicked.css_selector,
56 note: clicked.note,
57 bidi_session_id: clicked.bidi_session_id,
58 });
59 }
60 Some(TabEvent::Error(error)) => {
61 return Err(Error::new(format!(
62 "tab session error while clicking: {}",
63 error.message
64 )));
65 }
66 Some(TabEvent::Closed(_)) => {
67 handle.closed = true;
68 return Err(Error::new(format!(
69 "tab session {} closed while waiting for click result",
70 self.inner.session_id
71 )));
72 }
73 _ => {}
74 }
75 }
76 }
77
78 pub async fn focus(&self, css_selector: impl Into<String>) -> Result<ElementResult> {
79 self.focus_with_options(css_selector, CommandOptions::default()).await
80 }
81
82 pub async fn focus_with_options(
83 &self,
84 css_selector: impl Into<String>,
85 options: CommandOptions,
86 ) -> Result<ElementResult> {
87 let css_selector = normalize_selector_for_transport(&css_selector.into());
88 let mut state = self.inner.state.lock().await;
89 let handle = self.ensure_handle(&mut state).await?;
90 ensure_tab_open(handle, &self.inner.session_id)?;
91
92 handle
93 .command_tx
94 .send(TabSessionCommand {
95 browser_session_id: self.inner.browser_session_id.clone(),
96 tab_session_id: self.inner.session_id.clone(),
97 command: Some(TabCommand::FocusElement(FocusElementCommand {
98 css_selector,
99 retry_options: command_retry_options(options.timeout_ms),
100 })),
101 })
102 .await
103 .map_err(|_| Error::new("failed to send FocusElementCommand"))?;
104
105 loop {
106 let event = handle
107 .events
108 .message()
109 .await?
110 .ok_or_else(|| Error::new("tab session closed while waiting for focus result"))?;
111 match event.event {
112 Some(TabEvent::Attached(_)) => {}
113 Some(TabEvent::ElementFocused(focused)) => {
114 return Ok(ElementResult {
115 selector: focused.css_selector,
116 note: focused.note,
117 });
118 }
119 Some(TabEvent::Error(error)) => {
120 return Err(Error::new(format!(
121 "tab session error while focusing: {}",
122 error.message
123 )));
124 }
125 Some(TabEvent::Closed(_)) => {
126 handle.closed = true;
127 return Err(Error::new(format!(
128 "tab session {} closed while waiting for focus result",
129 self.inner.session_id
130 )));
131 }
132 _ => {}
133 }
134 }
135 }
136
137 pub async fn fill(
138 &self,
139 css_selector: impl Into<String>,
140 value: impl Into<String>,
141 ) -> Result<FillResult> {
142 self.fill_with_options(css_selector, value, CommandOptions::default())
143 .await
144 }
145
146 pub async fn fill_with_options(
147 &self,
148 css_selector: impl Into<String>,
149 value: impl Into<String>,
150 options: CommandOptions,
151 ) -> Result<FillResult> {
152 let css_selector = normalize_selector_for_transport(&css_selector.into());
153 let mut state = self.inner.state.lock().await;
154 let handle = self.ensure_handle(&mut state).await?;
155 ensure_tab_open(handle, &self.inner.session_id)?;
156
157 handle
158 .command_tx
159 .send(TabSessionCommand {
160 browser_session_id: self.inner.browser_session_id.clone(),
161 tab_session_id: self.inner.session_id.clone(),
162 command: Some(TabCommand::FillElement(FillElementCommand {
163 css_selector,
164 value: value.into(),
165 retry_options: command_retry_options(options.timeout_ms),
166 })),
167 })
168 .await
169 .map_err(|_| Error::new("failed to send FillElementCommand"))?;
170 loop {
171 let event = handle
172 .events
173 .message()
174 .await?
175 .ok_or_else(|| Error::new("tab session closed while waiting for fill result"))?;
176 match event.event {
177 Some(TabEvent::Attached(_)) => {}
178 Some(TabEvent::ElementFilled(filled)) => {
179 return Ok(FillResult {
180 selector: filled.css_selector,
181 value: filled.value,
182 note: filled.note,
183 });
184 }
185 Some(TabEvent::Error(error)) => {
186 return Err(Error::new(format!(
187 "tab session error while filling: {}",
188 error.message
189 )));
190 }
191 Some(TabEvent::Closed(_)) => {
192 handle.closed = true;
193 return Err(Error::new(format!(
194 "tab session {} closed while waiting for fill result",
195 self.inner.session_id
196 )));
197 }
198 _ => {}
199 }
200 }
201 }
202
203 pub async fn hover(&self, css_selector: impl Into<String>) -> Result<ElementResult> {
204 self.hover_with_options(css_selector, CommandOptions::default()).await
205 }
206
207 pub async fn hover_with_options(
208 &self,
209 css_selector: impl Into<String>,
210 options: CommandOptions,
211 ) -> Result<ElementResult> {
212 let css_selector = normalize_selector_for_transport(&css_selector.into());
213 let mut state = self.inner.state.lock().await;
214 let handle = self.ensure_handle(&mut state).await?;
215 ensure_tab_open(handle, &self.inner.session_id)?;
216 handle
217 .command_tx
218 .send(TabSessionCommand {
219 browser_session_id: self.inner.browser_session_id.clone(),
220 tab_session_id: self.inner.session_id.clone(),
221 command: Some(TabCommand::HoverElement(HoverElementCommand {
222 css_selector,
223 retry_options: command_retry_options(options.timeout_ms),
224 })),
225 })
226 .await
227 .map_err(|_| Error::new("failed to send HoverElementCommand"))?;
228 loop {
229 let event = handle
230 .events
231 .message()
232 .await?
233 .ok_or_else(|| Error::new("tab session closed while waiting for hover result"))?;
234 match event.event {
235 Some(TabEvent::Attached(_)) => {}
236 Some(TabEvent::ElementHovered(hovered)) => {
237 return Ok(ElementResult {
238 selector: hovered.css_selector,
239 note: hovered.note,
240 });
241 }
242 Some(TabEvent::Error(error)) => {
243 return Err(Error::new(format!(
244 "tab session error while hovering: {}",
245 error.message
246 )));
247 }
248 Some(TabEvent::Closed(_)) => {
249 handle.closed = true;
250 return Err(Error::new(format!(
251 "tab session {} closed while waiting for hover result",
252 self.inner.session_id
253 )));
254 }
255 _ => {}
256 }
257 }
258 }
259
260 pub async fn press(
261 &self,
262 css_selector: impl Into<String>,
263 key: impl Into<String>,
264 ) -> Result<PressResult> {
265 self.press_with_options(css_selector, key, PressOptions::default())
266 .await
267 }
268
269 pub async fn press_with_options(
270 &self,
271 css_selector: impl Into<String>,
272 key: impl Into<String>,
273 options: PressOptions,
274 ) -> Result<PressResult> {
275 let css_selector = normalize_selector_for_transport(&css_selector.into());
276 let mut state = self.inner.state.lock().await;
277 let handle = self.ensure_handle(&mut state).await?;
278 ensure_tab_open(handle, &self.inner.session_id)?;
279 handle
280 .command_tx
281 .send(TabSessionCommand {
282 browser_session_id: self.inner.browser_session_id.clone(),
283 tab_session_id: self.inner.session_id.clone(),
284 command: Some(TabCommand::PressKey(PressKeyCommand {
285 css_selector,
286 key: key.into(),
287 text: options.text,
288 retry_options: command_retry_options(options.timeout_ms),
289 })),
290 })
291 .await
292 .map_err(|_| Error::new("failed to send PressKeyCommand"))?;
293 loop {
294 let event = handle
295 .events
296 .message()
297 .await?
298 .ok_or_else(|| Error::new("tab session closed while waiting for press result"))?;
299 match event.event {
300 Some(TabEvent::Attached(_)) => {}
301 Some(TabEvent::KeyPressed(pressed)) => {
302 return Ok(PressResult {
303 selector: pressed.css_selector,
304 key: pressed.key,
305 note: pressed.note,
306 });
307 }
308 Some(TabEvent::Error(error)) => {
309 return Err(Error::new(format!(
310 "tab session error while pressing key: {}",
311 error.message
312 )));
313 }
314 Some(TabEvent::Closed(_)) => {
315 handle.closed = true;
316 return Err(Error::new(format!(
317 "tab session {} closed while waiting for press result",
318 self.inner.session_id
319 )));
320 }
321 _ => {}
322 }
323 }
324 }
325}