Skip to main content

xcelerate_core/
element.rs

1use crate::page::Page;
2use crate::error::XcelerateResult;
3use std::sync::Arc;
4
5/// Represents an HTML element in the DOM.
6#[derive(uniffi::Object)]
7pub struct Element {
8    pub(crate) page: Arc<Page>,
9    pub(crate) object_id: String,
10}
11
12#[uniffi::export(async_runtime = "tokio")]
13impl Element {
14    /// Clicks the element.
15    pub async fn click(self: Arc<Self>) -> XcelerateResult<Arc<Self>> {
16        self.page.client.execute_with_session(
17            Some(&self.page.session_id),
18            js_protocol::runtime::CallFunctionOnParams {
19                functionDeclaration: "function() { this.click(); }".into(),
20                objectId: Some(self.object_id.clone()),
21                ..Default::default()
22            }
23        ).await?;
24        
25        Ok(self)
26    }
27
28    pub async fn type_text(self: Arc<Self>, text: String) -> XcelerateResult<Arc<Self>> {
29        let js = "function(t) { this.value = t; this.dispatchEvent(new Event('input', { bubbles: true })); }";
30        self.page.client.execute_with_session(
31            Some(&self.page.session_id),
32            js_protocol::runtime::CallFunctionOnParams {
33                functionDeclaration: js.into(),
34                arguments: Some(vec![js_protocol::runtime::CallArgument {
35                    value: Some(serde_json::json!(text)),
36                    ..Default::default()
37                }]),
38                objectId: Some(self.object_id.clone()),
39                ..Default::default()
40            }
41        ).await?;
42        
43        Ok(self)
44    }
45
46    /// Hovers over the element.
47    pub async fn hover(self: Arc<Self>) -> XcelerateResult<Arc<Self>> {
48        self.call_js("function() { this.dispatchEvent(new MouseEvent('mouseover', { bubbles: true })); }".to_string()).await?;
49        Ok(self)
50    }
51
52    /// Focuses the element.
53    pub async fn focus(self: Arc<Self>) -> XcelerateResult<Arc<Self>> {
54        self.call_js("function() { this.focus(); }".to_string()).await?;
55        Ok(self)
56    }
57
58    /// Returns the visible text of the element.
59    pub async fn text(&self) -> XcelerateResult<String> {
60        let res = self.call_js("function() { return this.innerText; }".to_string()).await?;
61        Ok(res.result.value.and_then(|v| v.as_str().map(|s| s.to_string())).unwrap_or_default())
62    }
63
64    /// Returns the value of a specific attribute.
65    pub async fn attribute(&self, name: String) -> XcelerateResult<Option<String>> {
66        let js = format!("function() {{ return this.getAttribute('{}'); }}", name);
67        let res = self.call_js(js).await?;
68        Ok(res.result.value.and_then(|v| v.as_str().map(|s| s.to_string())))
69    }
70
71    /// Returns the inner HTML of the element.
72    pub async fn inner_html(&self) -> XcelerateResult<String> {
73        let res = self.call_js("function() { return this.innerHTML; }".to_string()).await?;
74        Ok(res.result.value.and_then(|v| v.as_str().map(|s| s.to_string())).unwrap_or_default())
75    }
76}
77
78impl Element {
79    /// Helper to call JS on this element.
80    async fn call_js(&self, js: String) -> XcelerateResult<js_protocol::runtime::CallFunctionOnReturns> {
81        self.page.client.execute_with_session(
82            Some(&self.page.session_id),
83            js_protocol::runtime::CallFunctionOnParams {
84                functionDeclaration: js,
85                objectId: Some(self.object_id.clone()),
86                ..Default::default()
87            }
88        ).await
89    }
90}