1use ferridriver::Frame;
19use rquickjs::JsLifetime;
20use rquickjs::class::Trace;
21
22use crate::bindings::convert::{
23 FerriResultExt, extract_page_function, quickjs_arg_to_serialized, serialized_value_to_quickjs,
24};
25use crate::bindings::locator::LocatorJs;
26
27#[derive(JsLifetime, Trace)]
31#[rquickjs::class(rename = "Frame")]
32pub struct FrameJs {
33 #[qjs(skip_trace)]
34 inner: Frame,
35}
36
37impl FrameJs {
38 #[must_use]
39 pub(crate) fn new(inner: Frame) -> Self {
40 Self { inner }
41 }
42}
43
44#[rquickjs::methods]
45impl FrameJs {
46 #[qjs(rename = "name")]
50 pub fn name(&self) -> String {
51 self.inner.name()
52 }
53
54 #[qjs(rename = "url")]
56 pub fn url(&self) -> String {
57 self.inner.url()
58 }
59
60 #[qjs(rename = "isMainFrame")]
62 pub fn is_main_frame(&self) -> bool {
63 self.inner.is_main_frame()
64 }
65
66 #[qjs(rename = "parentFrame")]
68 pub fn parent_frame(&self) -> Option<FrameJs> {
69 self.inner.parent_frame().map(FrameJs::new)
70 }
71
72 #[qjs(rename = "childFrames")]
74 pub fn child_frames(&self) -> Vec<FrameJs> {
75 self.inner.child_frames().into_iter().map(FrameJs::new).collect()
76 }
77
78 #[qjs(rename = "isDetached")]
80 pub fn is_detached(&self) -> bool {
81 self.inner.is_detached()
82 }
83
84 #[qjs(rename = "evaluate")]
88 pub async fn evaluate<'js>(
89 &self,
90 ctx: rquickjs::Ctx<'js>,
91 page_function: rquickjs::Value<'js>,
92 arg: rquickjs::function::Opt<rquickjs::Value<'js>>,
93 ) -> rquickjs::Result<rquickjs::Value<'js>> {
94 let (source, is_fn) = extract_page_function(&ctx, page_function)?;
95 let serialized = quickjs_arg_to_serialized(&ctx, arg.0)?;
96 let result = self.inner.evaluate(&source, serialized, is_fn).await.into_js()?;
97 serialized_value_to_quickjs(&ctx, &result)
98 }
99
100 #[qjs(rename = "evaluateHandle")]
102 pub async fn evaluate_handle<'js>(
103 &self,
104 ctx: rquickjs::Ctx<'js>,
105 page_function: rquickjs::Value<'js>,
106 arg: rquickjs::function::Opt<rquickjs::Value<'js>>,
107 ) -> rquickjs::Result<crate::bindings::js_handle::JSHandleJs> {
108 let (source, is_fn) = extract_page_function(&ctx, page_function)?;
109 let serialized = quickjs_arg_to_serialized(&ctx, arg.0)?;
110 let handle = self.inner.evaluate_handle(&source, serialized, is_fn).await.into_js()?;
111 Ok(crate::bindings::js_handle::JSHandleJs::new(handle))
112 }
113
114 #[qjs(rename = "title")]
115 pub async fn title(&self) -> rquickjs::Result<String> {
116 self.inner.title().await.into_js()
117 }
118
119 #[qjs(rename = "content")]
120 pub async fn content(&self) -> rquickjs::Result<String> {
121 self.inner.content().await.into_js()
122 }
123
124 #[qjs(rename = "waitForLoadState")]
128 pub async fn wait_for_load_state(&self) -> rquickjs::Result<()> {
129 self.inner.wait_for_load_state().await.into_js()
130 }
131
132 #[qjs(rename = "waitForSelector")]
138 pub async fn wait_for_selector<'js>(
139 &self,
140 ctx: rquickjs::Ctx<'js>,
141 selector: String,
142 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
143 ) -> rquickjs::Result<Option<crate::bindings::element_handle::ElementHandleJs>> {
144 let opts = crate::bindings::page::parse_wait_options(&ctx, options)?;
145 let handle = self.inner.wait_for_selector(&selector, opts).await.into_js()?;
146 Ok(handle.map(crate::bindings::element_handle::ElementHandleJs::new))
147 }
148
149 #[qjs(rename = "locator")]
153 pub fn locator(&self, selector: String) -> LocatorJs {
154 LocatorJs::new(self.inner.locator(&selector, None))
155 }
156
157 #[qjs(rename = "getByRole")]
158 pub fn get_by_role(
159 &self,
160 role: String,
161 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
162 ) -> rquickjs::Result<LocatorJs> {
163 let opts = crate::bindings::page::parse_role_options(options)?;
164 Ok(LocatorJs::new(self.inner.get_by_role(&role, &opts)))
165 }
166
167 #[qjs(rename = "getByText")]
168 pub fn get_by_text(
169 &self,
170 text: rquickjs::Value<'_>,
171 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
172 ) -> rquickjs::Result<LocatorJs> {
173 let t = crate::bindings::page::string_or_regex_from_js(text)?;
174 let opts = crate::bindings::page::parse_text_options(options);
175 Ok(LocatorJs::new(self.inner.get_by_text(&t, &opts)))
176 }
177
178 #[qjs(rename = "getByLabel")]
179 pub fn get_by_label(
180 &self,
181 text: rquickjs::Value<'_>,
182 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
183 ) -> rquickjs::Result<LocatorJs> {
184 let t = crate::bindings::page::string_or_regex_from_js(text)?;
185 let opts = crate::bindings::page::parse_text_options(options);
186 Ok(LocatorJs::new(self.inner.get_by_label(&t, &opts)))
187 }
188
189 #[qjs(rename = "getByPlaceholder")]
190 pub fn get_by_placeholder(
191 &self,
192 text: rquickjs::Value<'_>,
193 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
194 ) -> rquickjs::Result<LocatorJs> {
195 let t = crate::bindings::page::string_or_regex_from_js(text)?;
196 let opts = crate::bindings::page::parse_text_options(options);
197 Ok(LocatorJs::new(self.inner.get_by_placeholder(&t, &opts)))
198 }
199
200 #[qjs(rename = "getByAltText")]
201 pub fn get_by_alt_text(
202 &self,
203 text: rquickjs::Value<'_>,
204 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
205 ) -> rquickjs::Result<LocatorJs> {
206 let t = crate::bindings::page::string_or_regex_from_js(text)?;
207 let opts = crate::bindings::page::parse_text_options(options);
208 Ok(LocatorJs::new(self.inner.get_by_alt_text(&t, &opts)))
209 }
210
211 #[qjs(rename = "getByTitle")]
212 pub fn get_by_title(
213 &self,
214 text: rquickjs::Value<'_>,
215 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
216 ) -> rquickjs::Result<LocatorJs> {
217 let t = crate::bindings::page::string_or_regex_from_js(text)?;
218 let opts = crate::bindings::page::parse_text_options(options);
219 Ok(LocatorJs::new(self.inner.get_by_title(&t, &opts)))
220 }
221
222 #[qjs(rename = "getByTestId")]
223 pub fn get_by_test_id(&self, test_id: rquickjs::Value<'_>) -> rquickjs::Result<LocatorJs> {
224 let t = crate::bindings::page::string_or_regex_from_js(test_id)?;
225 Ok(LocatorJs::new(self.inner.get_by_test_id(&t)))
226 }
227
228 #[qjs(rename = "frameLocator")]
230 pub fn frame_locator(&self, selector: String) -> crate::bindings::frame_locator::FrameLocatorJs {
231 crate::bindings::frame_locator::FrameLocatorJs::new(self.inner.frame_locator(&selector))
232 }
233
234 #[qjs(rename = "page")]
238 pub fn page(&self, ctx: rquickjs::Ctx<'_>) -> crate::bindings::page::PageJs {
239 crate::bindings::page::pagejs_for_ctx(&ctx, self.inner.page_arc().clone())
240 }
241
242 #[qjs(rename = "click")]
250 pub async fn click<'js>(
251 &self,
252 ctx: rquickjs::Ctx<'js>,
253 selector: String,
254 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
255 ) -> rquickjs::Result<()> {
256 let opts = crate::bindings::convert::parse_click_options(&ctx, options)?;
257 self.inner.click(&selector, opts).await.into_js()
258 }
259
260 #[qjs(rename = "dblclick")]
261 pub async fn dblclick<'js>(
262 &self,
263 ctx: rquickjs::Ctx<'js>,
264 selector: String,
265 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
266 ) -> rquickjs::Result<()> {
267 let opts = crate::bindings::convert::parse_dblclick_options(&ctx, options)?;
268 self.inner.dblclick(&selector, opts).await.into_js()
269 }
270
271 #[qjs(rename = "hover")]
272 pub async fn hover<'js>(
273 &self,
274 ctx: rquickjs::Ctx<'js>,
275 selector: String,
276 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
277 ) -> rquickjs::Result<()> {
278 let opts = crate::bindings::convert::parse_hover_options(&ctx, options)?;
279 self.inner.hover(&selector, opts).await.into_js()
280 }
281
282 #[qjs(rename = "tap")]
283 pub async fn tap<'js>(
284 &self,
285 ctx: rquickjs::Ctx<'js>,
286 selector: String,
287 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
288 ) -> rquickjs::Result<()> {
289 let opts = crate::bindings::convert::parse_tap_options(&ctx, options)?;
290 self.inner.tap(&selector, opts).await.into_js()
291 }
292
293 #[qjs(rename = "focus")]
294 pub async fn focus(&self, selector: String) -> rquickjs::Result<()> {
295 self.inner.focus(&selector).await.into_js()
296 }
297
298 #[qjs(rename = "fill")]
299 pub async fn fill<'js>(
300 &self,
301 ctx: rquickjs::Ctx<'js>,
302 selector: String,
303 value: String,
304 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
305 ) -> rquickjs::Result<()> {
306 let opts = crate::bindings::convert::parse_fill_options(&ctx, options)?;
307 self.inner.fill(&selector, &value, opts).await.into_js()
308 }
309
310 #[qjs(rename = "type")]
311 pub async fn type_text<'js>(
312 &self,
313 ctx: rquickjs::Ctx<'js>,
314 selector: String,
315 text: String,
316 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
317 ) -> rquickjs::Result<()> {
318 let opts = crate::bindings::convert::parse_type_options(&ctx, options)?;
319 self.inner.r#type(&selector, &text, opts).await.into_js()
320 }
321
322 #[qjs(rename = "press")]
323 pub async fn press<'js>(
324 &self,
325 ctx: rquickjs::Ctx<'js>,
326 selector: String,
327 key: String,
328 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
329 ) -> rquickjs::Result<()> {
330 let opts = crate::bindings::convert::parse_press_options(&ctx, options)?;
331 self.inner.press(&selector, &key, opts).await.into_js()
332 }
333
334 #[qjs(rename = "check")]
335 pub async fn check<'js>(
336 &self,
337 ctx: rquickjs::Ctx<'js>,
338 selector: String,
339 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
340 ) -> rquickjs::Result<()> {
341 let opts = crate::bindings::convert::parse_check_options(&ctx, options)?;
342 self.inner.check(&selector, opts).await.into_js()
343 }
344
345 #[qjs(rename = "uncheck")]
346 pub async fn uncheck<'js>(
347 &self,
348 ctx: rquickjs::Ctx<'js>,
349 selector: String,
350 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
351 ) -> rquickjs::Result<()> {
352 let opts = crate::bindings::convert::parse_check_options(&ctx, options)?;
353 self.inner.uncheck(&selector, opts).await.into_js()
354 }
355
356 #[qjs(rename = "setChecked")]
357 pub async fn set_checked<'js>(
358 &self,
359 ctx: rquickjs::Ctx<'js>,
360 selector: String,
361 checked: bool,
362 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
363 ) -> rquickjs::Result<()> {
364 let opts = crate::bindings::convert::parse_check_options(&ctx, options)?;
365 self.inner.set_checked(&selector, checked, opts).await.into_js()
366 }
367
368 #[qjs(rename = "selectOption")]
369 pub async fn select_option<'js>(
370 &self,
371 ctx: rquickjs::Ctx<'js>,
372 selector: String,
373 values: rquickjs::Value<'js>,
374 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
375 ) -> rquickjs::Result<Vec<String>> {
376 let values = crate::bindings::convert::parse_select_option_values(&ctx, values)?;
377 let opts = crate::bindings::convert::parse_select_option_options(&ctx, options)?;
378 self.inner.select_option(&selector, values, opts).await.into_js()
379 }
380
381 #[qjs(rename = "setInputFiles")]
382 pub async fn set_input_files<'js>(
383 &self,
384 ctx: rquickjs::Ctx<'js>,
385 selector: String,
386 files: rquickjs::Value<'js>,
387 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
388 ) -> rquickjs::Result<()> {
389 let files = crate::bindings::convert::parse_input_files(&ctx, files)?;
390 let opts = crate::bindings::convert::parse_set_input_files_options(&ctx, options)?;
391 self.inner.set_input_files(&selector, files, opts).await.into_js()
392 }
393
394 #[qjs(rename = "dragAndDrop")]
397 pub async fn drag_and_drop(&self, source: String, target: String) -> rquickjs::Result<()> {
398 self.inner.drag_and_drop(&source, &target, None).await.into_js()
399 }
400
401 #[qjs(rename = "dispatchEvent")]
402 pub async fn dispatch_event<'js>(
403 &self,
404 ctx: rquickjs::Ctx<'js>,
405 selector: String,
406 event_type: String,
407 event_init: rquickjs::function::Opt<rquickjs::Value<'js>>,
408 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
409 ) -> rquickjs::Result<()> {
410 let init_json = match event_init.0 {
411 Some(v) if !v.is_undefined() && !v.is_null() => {
412 Some(crate::bindings::convert::serde_from_js::<serde_json::Value>(&ctx, v)?)
413 },
414 _ => None,
415 };
416 let opts = crate::bindings::convert::parse_dispatch_event_options(&ctx, options)?;
417 self
418 .inner
419 .dispatch_event(&selector, &event_type, init_json, opts)
420 .await
421 .into_js()
422 }
423
424 #[qjs(rename = "textContent")]
425 pub async fn text_content(&self, selector: String) -> rquickjs::Result<Option<String>> {
426 self.inner.text_content(&selector).await.into_js()
427 }
428
429 #[qjs(rename = "innerText")]
430 pub async fn inner_text(&self, selector: String) -> rquickjs::Result<String> {
431 self.inner.inner_text(&selector).await.into_js()
432 }
433
434 #[qjs(rename = "innerHTML")]
435 pub async fn inner_html(&self, selector: String) -> rquickjs::Result<String> {
436 self.inner.inner_html(&selector).await.into_js()
437 }
438
439 #[qjs(rename = "getAttribute")]
440 pub async fn get_attribute(&self, selector: String, name: String) -> rquickjs::Result<Option<String>> {
441 self.inner.get_attribute(&selector, &name).await.into_js()
442 }
443
444 #[qjs(rename = "inputValue")]
445 pub async fn input_value(&self, selector: String) -> rquickjs::Result<String> {
446 self.inner.input_value(&selector).await.into_js()
447 }
448
449 #[qjs(rename = "isVisible")]
450 pub async fn is_visible(&self, selector: String) -> rquickjs::Result<bool> {
451 self.inner.is_visible(&selector).await.into_js()
452 }
453
454 #[qjs(rename = "isHidden")]
455 pub async fn is_hidden(&self, selector: String) -> rquickjs::Result<bool> {
456 self.inner.is_hidden(&selector).await.into_js()
457 }
458
459 #[qjs(rename = "isEnabled")]
460 pub async fn is_enabled(&self, selector: String) -> rquickjs::Result<bool> {
461 self.inner.is_enabled(&selector).await.into_js()
462 }
463
464 #[qjs(rename = "isDisabled")]
465 pub async fn is_disabled(&self, selector: String) -> rquickjs::Result<bool> {
466 self.inner.is_disabled(&selector).await.into_js()
467 }
468
469 #[qjs(rename = "isEditable")]
470 pub async fn is_editable(&self, selector: String) -> rquickjs::Result<bool> {
471 self.inner.is_editable(&selector).await.into_js()
472 }
473
474 #[qjs(rename = "isChecked")]
475 pub async fn is_checked(&self, selector: String) -> rquickjs::Result<bool> {
476 self.inner.is_checked(&selector).await.into_js()
477 }
478}