browser_use/tools/
click.rs

1use crate::error::{BrowserError, Result};
2use crate::tools::{Tool, ToolContext, ToolResult};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6/// Parameters for the click tool
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
8pub struct ClickParams {
9    /// CSS selector or element index
10    #[serde(flatten)]
11    pub selector: ElementSelector,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
15#[serde(untagged)]
16pub enum ElementSelector {
17    /// Select by CSS selector
18    Css {
19        /// CSS selector
20        selector: String,
21    },
22    /// Select by index from DOM tree
23    Index {
24        /// Element index
25        index: usize,
26    },
27}
28
29/// Tool for clicking elements
30#[derive(Default)]
31pub struct ClickTool;
32
33impl Tool for ClickTool {
34    type Params = ClickParams;
35
36    fn name(&self) -> &str {
37        "click"
38    }
39
40    fn execute_typed(&self, params: ClickParams, context: &mut ToolContext) -> Result<ToolResult> {
41        match params.selector {
42            ElementSelector::Css { selector } => {
43                let element = context.session.find_element(&selector)?;
44                element
45                    .click()
46                    .map_err(|e| BrowserError::ToolExecutionFailed {
47                        tool: "click".to_string(),
48                        reason: e.to_string(),
49                    })?;
50
51                Ok(ToolResult::success_with(serde_json::json!({
52                    "selector": selector,
53                    "method": "css"
54                })))
55            }
56            ElementSelector::Index { index } => {
57                let css_selector = {
58                    let dom = context.get_dom()?;
59                    let selector_info = dom.get_selector(index).ok_or_else(|| {
60                        BrowserError::ElementNotFound(format!("No element with index {}", index))
61                    })?;
62                    selector_info.css_selector.clone()
63                };
64
65                let element = context.session.find_element(&css_selector)?;
66                element
67                    .click()
68                    .map_err(|e| BrowserError::ToolExecutionFailed {
69                        tool: "click".to_string(),
70                        reason: e.to_string(),
71                    })?;
72
73                Ok(ToolResult::success_with(serde_json::json!({
74                    "index": index,
75                    "selector": css_selector,
76                    "method": "index"
77                })))
78            }
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_click_params_css() {
89        let json = serde_json::json!({
90            "selector": "#my-button"
91        });
92
93        let params: ClickParams = serde_json::from_value(json).unwrap();
94        match params.selector {
95            ElementSelector::Css { selector } => assert_eq!(selector, "#my-button"),
96            _ => panic!("Expected CSS selector"),
97        }
98    }
99
100    #[test]
101    fn test_click_params_index() {
102        let json = serde_json::json!({
103            "index": 5
104        });
105
106        let params: ClickParams = serde_json::from_value(json).unwrap();
107        match params.selector {
108            ElementSelector::Index { index } => assert_eq!(index, 5),
109            _ => panic!("Expected index selector"),
110        }
111    }
112}