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