Skip to main content

ferridriver_script/bindings/
mouse.rs

1//! `MouseJs`: wrapper around `ferridriver::Page::mouse()`.
2//!
3//! Mirrors Playwright's `page.mouse.*` namespace: `move(x, y, options?)`,
4//! `click(x, y, options?)`, `dblclick(x, y, options?)`, `down(options?)`,
5//! `up(options?)`, `wheel(dx, dy)`.
6
7use std::sync::Arc;
8
9use ferridriver::Page;
10use rquickjs::JsLifetime;
11use rquickjs::class::Trace;
12use rquickjs::function::Opt;
13use serde::Deserialize;
14
15use crate::bindings::convert::{FerriResultExt, serde_from_js};
16
17#[derive(Debug, Default, Deserialize)]
18#[serde(default, rename_all = "camelCase")]
19struct JsMouseClickOptions {
20  button: Option<String>,
21  click_count: Option<u32>,
22  delay: Option<u64>,
23}
24
25#[derive(Debug, Default, Deserialize)]
26#[serde(default, rename_all = "camelCase")]
27struct JsMouseMoveOptions {
28  steps: Option<u32>,
29}
30
31fn parse_click_options<'js>(
32  ctx: &rquickjs::Ctx<'js>,
33  v: Opt<rquickjs::Value<'js>>,
34) -> rquickjs::Result<JsMouseClickOptions> {
35  match v.0 {
36    Some(val) if !val.is_undefined() && !val.is_null() => serde_from_js(ctx, val),
37    _ => Ok(JsMouseClickOptions::default()),
38  }
39}
40
41#[derive(JsLifetime, Trace)]
42#[rquickjs::class(rename = "Mouse")]
43pub struct MouseJs {
44  #[qjs(skip_trace)]
45  page: Arc<Page>,
46}
47
48impl MouseJs {
49  #[must_use]
50  pub fn new(page: Arc<Page>) -> Self {
51    Self { page }
52  }
53}
54
55#[rquickjs::methods]
56impl MouseJs {
57  /// `mouse.click(x, y, options?: { button?, clickCount? })`.
58  #[qjs(rename = "click")]
59  pub async fn click<'js>(
60    &self,
61    ctx: rquickjs::Ctx<'js>,
62    x: f64,
63    y: f64,
64    options: Opt<rquickjs::Value<'js>>,
65  ) -> rquickjs::Result<()> {
66    let o = parse_click_options(&ctx, options)?;
67    let opts = ferridriver::page::MouseClickOptions {
68      button: o.button,
69      click_count: o.click_count,
70      delay: o.delay,
71    };
72    self.page.mouse().click(x, y, Some(opts)).await.into_js()
73  }
74
75  /// `mouse.move(x, y, options?: { steps? })`.
76  #[qjs(rename = "move")]
77  pub async fn move_<'js>(
78    &self,
79    ctx: rquickjs::Ctx<'js>,
80    x: f64,
81    y: f64,
82    options: Opt<rquickjs::Value<'js>>,
83  ) -> rquickjs::Result<()> {
84    let steps = match options.0 {
85      Some(val) if !val.is_undefined() && !val.is_null() => serde_from_js::<JsMouseMoveOptions>(&ctx, val)?.steps,
86      _ => None,
87    };
88    self.page.mouse().r#move(x, y, steps).await.into_js()
89  }
90
91  /// `mouse.dblclick(x, y, options?: { button? })`.
92  #[qjs(rename = "dblclick")]
93  pub async fn dblclick<'js>(
94    &self,
95    ctx: rquickjs::Ctx<'js>,
96    x: f64,
97    y: f64,
98    options: Opt<rquickjs::Value<'js>>,
99  ) -> rquickjs::Result<()> {
100    let o = parse_click_options(&ctx, options)?;
101    let opts = ferridriver::page::MouseClickOptions {
102      button: o.button,
103      click_count: None,
104      delay: o.delay,
105    };
106    self.page.mouse().dblclick(x, y, Some(opts)).await.into_js()
107  }
108
109  /// `mouse.down(options?: { button?, clickCount? })`.
110  #[qjs(rename = "down")]
111  pub async fn down<'js>(&self, ctx: rquickjs::Ctx<'js>, options: Opt<rquickjs::Value<'js>>) -> rquickjs::Result<()> {
112    let o = parse_click_options(&ctx, options)?;
113    let opts = ferridriver::page::MouseDownOptions {
114      button: o.button,
115      click_count: o.click_count,
116    };
117    self.page.mouse().down(Some(opts)).await.into_js()
118  }
119
120  /// `mouse.up(options?: { button?, clickCount? })`.
121  #[qjs(rename = "up")]
122  pub async fn up<'js>(&self, ctx: rquickjs::Ctx<'js>, options: Opt<rquickjs::Value<'js>>) -> rquickjs::Result<()> {
123    let o = parse_click_options(&ctx, options)?;
124    let opts = ferridriver::page::MouseUpOptions {
125      button: o.button,
126      click_count: o.click_count,
127    };
128    self.page.mouse().up(Some(opts)).await.into_js()
129  }
130
131  /// `mouse.wheel(deltaX, deltaY)`.
132  #[qjs(rename = "wheel")]
133  pub async fn wheel(&self, delta_x: f64, delta_y: f64) -> rquickjs::Result<()> {
134    self.page.mouse().wheel(delta_x, delta_y).await.into_js()
135  }
136}