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())
19 .await
20 }
21
22 pub async fn click_with_options(
23 &self,
24 css_selector: impl Into<String>,
25 options: CommandOptions,
26 ) -> Result<ClickResult> {
27 let css_selector = normalize_selector_for_transport(&css_selector.into());
28 let mut state = self.inner.state.lock().await;
29 let handle = self.ensure_handle(&mut state).await?;
30 ensure_tab_open(handle, &self.inner.session_id)?;
31
32 handle
33 .command_tx
34 .send(TabSessionCommand {
35 browser_session_id: self.inner.browser_session_id.clone(),
36 tab_session_id: self.inner.session_id.clone(),
37 command: Some(TabCommand::ClickElement(ClickElementCommand {
38 css_selector: css_selector.clone(),
39 retry_options: command_retry_options(options.timeout_ms),
40 })),
41 })
42 .await
43 .map_err(|_| Error::new("failed to send ClickElementCommand"))?;
44
45 loop {
46 let event =
47 handle.events.message().await?.ok_or_else(|| {
48 Error::new("tab session closed while waiting for click result")
49 })?;
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, 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())
80 .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 =
108 handle.events.message().await?.ok_or_else(|| {
109 Error::new("tab session closed while waiting for focus result")
110 })?;
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 locator {:?}: {}",
122 css_selector, 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: css_selector.clone(),
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 =
172 handle.events.message().await?.ok_or_else(|| {
173 Error::new("tab session closed while waiting for fill result")
174 })?;
175 match event.event {
176 Some(TabEvent::Attached(_)) => {}
177 Some(TabEvent::ElementFilled(filled)) => {
178 return Ok(FillResult {
179 selector: filled.css_selector,
180 value: filled.value,
181 note: filled.note,
182 });
183 }
184 Some(TabEvent::Error(error)) => {
185 return Err(Error::new(format!(
186 "tab session error while filling locator {:?}: {}",
187 css_selector, error.message,
188 )));
189 }
190 Some(TabEvent::Closed(_)) => {
191 handle.closed = true;
192 return Err(Error::new(format!(
193 "tab session {} closed while waiting for fill result",
194 self.inner.session_id
195 )));
196 }
197 _ => {}
198 }
199 }
200 }
201
202 pub async fn hover(&self, css_selector: impl Into<String>) -> Result<ElementResult> {
203 self.hover_with_options(css_selector, CommandOptions::default())
204 .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: css_selector.clone(),
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 =
230 handle.events.message().await?.ok_or_else(|| {
231 Error::new("tab session closed while waiting for hover result")
232 })?;
233 match event.event {
234 Some(TabEvent::Attached(_)) => {}
235 Some(TabEvent::ElementHovered(hovered)) => {
236 return Ok(ElementResult {
237 selector: hovered.css_selector,
238 note: hovered.note,
239 });
240 }
241 Some(TabEvent::Error(error)) => {
242 return Err(Error::new(format!(
243 "tab session error while hovering locator {:?}: {}",
244 css_selector, error.message,
245 )));
246 }
247 Some(TabEvent::Closed(_)) => {
248 handle.closed = true;
249 return Err(Error::new(format!(
250 "tab session {} closed while waiting for hover result",
251 self.inner.session_id
252 )));
253 }
254 _ => {}
255 }
256 }
257 }
258
259 pub async fn press(
260 &self,
261 css_selector: impl Into<String>,
262 key: impl Into<String>,
263 ) -> Result<PressResult> {
264 self.press_with_options(css_selector, key, PressOptions::default())
265 .await
266 }
267
268 pub async fn press_with_options(
269 &self,
270 css_selector: impl Into<String>,
271 key: impl Into<String>,
272 options: PressOptions,
273 ) -> Result<PressResult> {
274 let css_selector = normalize_selector_for_transport(&css_selector.into());
275 let mut state = self.inner.state.lock().await;
276 let handle = self.ensure_handle(&mut state).await?;
277 ensure_tab_open(handle, &self.inner.session_id)?;
278 handle
279 .command_tx
280 .send(TabSessionCommand {
281 browser_session_id: self.inner.browser_session_id.clone(),
282 tab_session_id: self.inner.session_id.clone(),
283 command: Some(TabCommand::PressKey(PressKeyCommand {
284 css_selector,
285 key: key.into(),
286 text: options.text,
287 retry_options: command_retry_options(options.timeout_ms),
288 })),
289 })
290 .await
291 .map_err(|_| Error::new("failed to send PressKeyCommand"))?;
292 loop {
293 let event =
294 handle.events.message().await?.ok_or_else(|| {
295 Error::new("tab session closed while waiting for press result")
296 })?;
297 match event.event {
298 Some(TabEvent::Attached(_)) => {}
299 Some(TabEvent::KeyPressed(pressed)) => {
300 return Ok(PressResult {
301 selector: pressed.css_selector,
302 key: pressed.key,
303 note: pressed.note,
304 });
305 }
306 Some(TabEvent::Error(error)) => {
307 return Err(Error::new(format!(
308 "tab session error while pressing key: {}",
309 error.message
310 )));
311 }
312 Some(TabEvent::Closed(_)) => {
313 handle.closed = true;
314 return Err(Error::new(format!(
315 "tab session {} closed while waiting for press result",
316 self.inner.session_id
317 )));
318 }
319 _ => {}
320 }
321 }
322 }
323}