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 = "locator")]
136 pub fn locator(&self, selector: String) -> LocatorJs {
137 LocatorJs::new(self.inner.locator(&selector, None))
138 }
139
140 #[qjs(rename = "getByRole")]
141 pub fn get_by_role(
142 &self,
143 role: String,
144 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
145 ) -> rquickjs::Result<LocatorJs> {
146 let opts = crate::bindings::page::parse_role_options(options)?;
147 Ok(LocatorJs::new(self.inner.get_by_role(&role, &opts)))
148 }
149
150 #[qjs(rename = "getByText")]
151 pub fn get_by_text(
152 &self,
153 text: rquickjs::Value<'_>,
154 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
155 ) -> rquickjs::Result<LocatorJs> {
156 let t = crate::bindings::page::string_or_regex_from_js(text)?;
157 let opts = crate::bindings::page::parse_text_options(options);
158 Ok(LocatorJs::new(self.inner.get_by_text(&t, &opts)))
159 }
160
161 #[qjs(rename = "getByLabel")]
162 pub fn get_by_label(
163 &self,
164 text: rquickjs::Value<'_>,
165 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
166 ) -> rquickjs::Result<LocatorJs> {
167 let t = crate::bindings::page::string_or_regex_from_js(text)?;
168 let opts = crate::bindings::page::parse_text_options(options);
169 Ok(LocatorJs::new(self.inner.get_by_label(&t, &opts)))
170 }
171
172 #[qjs(rename = "getByPlaceholder")]
173 pub fn get_by_placeholder(
174 &self,
175 text: rquickjs::Value<'_>,
176 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
177 ) -> rquickjs::Result<LocatorJs> {
178 let t = crate::bindings::page::string_or_regex_from_js(text)?;
179 let opts = crate::bindings::page::parse_text_options(options);
180 Ok(LocatorJs::new(self.inner.get_by_placeholder(&t, &opts)))
181 }
182
183 #[qjs(rename = "getByAltText")]
184 pub fn get_by_alt_text(
185 &self,
186 text: rquickjs::Value<'_>,
187 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
188 ) -> rquickjs::Result<LocatorJs> {
189 let t = crate::bindings::page::string_or_regex_from_js(text)?;
190 let opts = crate::bindings::page::parse_text_options(options);
191 Ok(LocatorJs::new(self.inner.get_by_alt_text(&t, &opts)))
192 }
193
194 #[qjs(rename = "getByTitle")]
195 pub fn get_by_title(
196 &self,
197 text: rquickjs::Value<'_>,
198 options: rquickjs::function::Opt<rquickjs::Value<'_>>,
199 ) -> rquickjs::Result<LocatorJs> {
200 let t = crate::bindings::page::string_or_regex_from_js(text)?;
201 let opts = crate::bindings::page::parse_text_options(options);
202 Ok(LocatorJs::new(self.inner.get_by_title(&t, &opts)))
203 }
204
205 #[qjs(rename = "getByTestId")]
206 pub fn get_by_test_id(&self, test_id: rquickjs::Value<'_>) -> rquickjs::Result<LocatorJs> {
207 let t = crate::bindings::page::string_or_regex_from_js(test_id)?;
208 Ok(LocatorJs::new(self.inner.get_by_test_id(&t)))
209 }
210
211 #[qjs(rename = "frameLocator")]
213 pub fn frame_locator(&self, selector: String) -> crate::bindings::frame_locator::FrameLocatorJs {
214 crate::bindings::frame_locator::FrameLocatorJs::new(self.inner.frame_locator(&selector))
215 }
216
217 #[qjs(rename = "page")]
221 pub fn page(&self, ctx: rquickjs::Ctx<'_>) -> crate::bindings::page::PageJs {
222 crate::bindings::page::pagejs_for_ctx(&ctx, self.inner.page_arc().clone())
223 }
224
225 #[qjs(rename = "click")]
233 pub async fn click<'js>(
234 &self,
235 ctx: rquickjs::Ctx<'js>,
236 selector: String,
237 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
238 ) -> rquickjs::Result<()> {
239 let opts = crate::bindings::convert::parse_click_options(&ctx, options)?;
240 self.inner.click(&selector, opts).await.into_js()
241 }
242
243 #[qjs(rename = "dblclick")]
244 pub async fn dblclick<'js>(
245 &self,
246 ctx: rquickjs::Ctx<'js>,
247 selector: String,
248 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
249 ) -> rquickjs::Result<()> {
250 let opts = crate::bindings::convert::parse_dblclick_options(&ctx, options)?;
251 self.inner.dblclick(&selector, opts).await.into_js()
252 }
253
254 #[qjs(rename = "hover")]
255 pub async fn hover<'js>(
256 &self,
257 ctx: rquickjs::Ctx<'js>,
258 selector: String,
259 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
260 ) -> rquickjs::Result<()> {
261 let opts = crate::bindings::convert::parse_hover_options(&ctx, options)?;
262 self.inner.hover(&selector, opts).await.into_js()
263 }
264
265 #[qjs(rename = "tap")]
266 pub async fn tap<'js>(
267 &self,
268 ctx: rquickjs::Ctx<'js>,
269 selector: String,
270 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
271 ) -> rquickjs::Result<()> {
272 let opts = crate::bindings::convert::parse_tap_options(&ctx, options)?;
273 self.inner.tap(&selector, opts).await.into_js()
274 }
275
276 #[qjs(rename = "focus")]
277 pub async fn focus(&self, selector: String) -> rquickjs::Result<()> {
278 self.inner.focus(&selector).await.into_js()
279 }
280
281 #[qjs(rename = "fill")]
282 pub async fn fill<'js>(
283 &self,
284 ctx: rquickjs::Ctx<'js>,
285 selector: String,
286 value: String,
287 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
288 ) -> rquickjs::Result<()> {
289 let opts = crate::bindings::convert::parse_fill_options(&ctx, options)?;
290 self.inner.fill(&selector, &value, opts).await.into_js()
291 }
292
293 #[qjs(rename = "type")]
294 pub async fn type_text<'js>(
295 &self,
296 ctx: rquickjs::Ctx<'js>,
297 selector: String,
298 text: String,
299 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
300 ) -> rquickjs::Result<()> {
301 let opts = crate::bindings::convert::parse_type_options(&ctx, options)?;
302 self.inner.r#type(&selector, &text, opts).await.into_js()
303 }
304
305 #[qjs(rename = "press")]
306 pub async fn press<'js>(
307 &self,
308 ctx: rquickjs::Ctx<'js>,
309 selector: String,
310 key: String,
311 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
312 ) -> rquickjs::Result<()> {
313 let opts = crate::bindings::convert::parse_press_options(&ctx, options)?;
314 self.inner.press(&selector, &key, opts).await.into_js()
315 }
316
317 #[qjs(rename = "check")]
318 pub async fn check<'js>(
319 &self,
320 ctx: rquickjs::Ctx<'js>,
321 selector: String,
322 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
323 ) -> rquickjs::Result<()> {
324 let opts = crate::bindings::convert::parse_check_options(&ctx, options)?;
325 self.inner.check(&selector, opts).await.into_js()
326 }
327
328 #[qjs(rename = "uncheck")]
329 pub async fn uncheck<'js>(
330 &self,
331 ctx: rquickjs::Ctx<'js>,
332 selector: String,
333 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
334 ) -> rquickjs::Result<()> {
335 let opts = crate::bindings::convert::parse_check_options(&ctx, options)?;
336 self.inner.uncheck(&selector, opts).await.into_js()
337 }
338
339 #[qjs(rename = "setChecked")]
340 pub async fn set_checked<'js>(
341 &self,
342 ctx: rquickjs::Ctx<'js>,
343 selector: String,
344 checked: bool,
345 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
346 ) -> rquickjs::Result<()> {
347 let opts = crate::bindings::convert::parse_check_options(&ctx, options)?;
348 self.inner.set_checked(&selector, checked, opts).await.into_js()
349 }
350
351 #[qjs(rename = "selectOption")]
352 pub async fn select_option<'js>(
353 &self,
354 ctx: rquickjs::Ctx<'js>,
355 selector: String,
356 values: rquickjs::Value<'js>,
357 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
358 ) -> rquickjs::Result<Vec<String>> {
359 let values = crate::bindings::convert::parse_select_option_values(&ctx, values)?;
360 let opts = crate::bindings::convert::parse_select_option_options(&ctx, options)?;
361 self.inner.select_option(&selector, values, opts).await.into_js()
362 }
363
364 #[qjs(rename = "setInputFiles")]
365 pub async fn set_input_files<'js>(
366 &self,
367 ctx: rquickjs::Ctx<'js>,
368 selector: String,
369 files: rquickjs::Value<'js>,
370 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
371 ) -> rquickjs::Result<()> {
372 let files = crate::bindings::convert::parse_input_files(&ctx, files)?;
373 let opts = crate::bindings::convert::parse_set_input_files_options(&ctx, options)?;
374 self.inner.set_input_files(&selector, files, opts).await.into_js()
375 }
376
377 #[qjs(rename = "dragAndDrop")]
380 pub async fn drag_and_drop(&self, source: String, target: String) -> rquickjs::Result<()> {
381 self.inner.drag_and_drop(&source, &target, None).await.into_js()
382 }
383
384 #[qjs(rename = "dispatchEvent")]
385 pub async fn dispatch_event<'js>(
386 &self,
387 ctx: rquickjs::Ctx<'js>,
388 selector: String,
389 event_type: String,
390 event_init: rquickjs::function::Opt<rquickjs::Value<'js>>,
391 options: rquickjs::function::Opt<rquickjs::Value<'js>>,
392 ) -> rquickjs::Result<()> {
393 let init_json = match event_init.0 {
394 Some(v) if !v.is_undefined() && !v.is_null() => {
395 Some(crate::bindings::convert::serde_from_js::<serde_json::Value>(&ctx, v)?)
396 },
397 _ => None,
398 };
399 let opts = crate::bindings::convert::parse_dispatch_event_options(&ctx, options)?;
400 self
401 .inner
402 .dispatch_event(&selector, &event_type, init_json, opts)
403 .await
404 .into_js()
405 }
406
407 #[qjs(rename = "textContent")]
408 pub async fn text_content(&self, selector: String) -> rquickjs::Result<Option<String>> {
409 self.inner.text_content(&selector).await.into_js()
410 }
411
412 #[qjs(rename = "innerText")]
413 pub async fn inner_text(&self, selector: String) -> rquickjs::Result<String> {
414 self.inner.inner_text(&selector).await.into_js()
415 }
416
417 #[qjs(rename = "innerHTML")]
418 pub async fn inner_html(&self, selector: String) -> rquickjs::Result<String> {
419 self.inner.inner_html(&selector).await.into_js()
420 }
421
422 #[qjs(rename = "getAttribute")]
423 pub async fn get_attribute(&self, selector: String, name: String) -> rquickjs::Result<Option<String>> {
424 self.inner.get_attribute(&selector, &name).await.into_js()
425 }
426
427 #[qjs(rename = "inputValue")]
428 pub async fn input_value(&self, selector: String) -> rquickjs::Result<String> {
429 self.inner.input_value(&selector).await.into_js()
430 }
431
432 #[qjs(rename = "isVisible")]
433 pub async fn is_visible(&self, selector: String) -> rquickjs::Result<bool> {
434 self.inner.is_visible(&selector).await.into_js()
435 }
436
437 #[qjs(rename = "isHidden")]
438 pub async fn is_hidden(&self, selector: String) -> rquickjs::Result<bool> {
439 self.inner.is_hidden(&selector).await.into_js()
440 }
441
442 #[qjs(rename = "isEnabled")]
443 pub async fn is_enabled(&self, selector: String) -> rquickjs::Result<bool> {
444 self.inner.is_enabled(&selector).await.into_js()
445 }
446
447 #[qjs(rename = "isDisabled")]
448 pub async fn is_disabled(&self, selector: String) -> rquickjs::Result<bool> {
449 self.inner.is_disabled(&selector).await.into_js()
450 }
451
452 #[qjs(rename = "isEditable")]
453 pub async fn is_editable(&self, selector: String) -> rquickjs::Result<bool> {
454 self.inner.is_editable(&selector).await.into_js()
455 }
456
457 #[qjs(rename = "isChecked")]
458 pub async fn is_checked(&self, selector: String) -> rquickjs::Result<bool> {
459 self.inner.is_checked(&selector).await.into_js()
460 }
461}