in spring-ai-alibaba-jmanus/src/main/java/com/alibaba/cloud/ai/example/manus/tool/browser/BrowserUseTool.java [210:461]
public ToolExecuteResult run(String toolInput) {
log.info("BrowserUseTool toolInput:{}", toolInput);
Map<String, Object> toolInputMap = JSON.parseObject(toolInput, new TypeReference<Map<String, Object>>() {
});
String action = null;
if (toolInputMap.get("action") != null) {
action = (String) toolInputMap.get("action");
}
String url = null;
if (toolInputMap.get("url") != null) {
url = (String) toolInputMap.get("url");
}
Integer index = null;
if (toolInputMap.get("index") != null) {
index = (Integer) toolInputMap.get("index");
}
String text = null;
if (toolInputMap.get("text") != null) {
text = (String) toolInputMap.get("text");
}
String script = null;
if (toolInputMap.get("script") != null) {
script = (String) toolInputMap.get("script");
}
Integer scrollAmount = null;
if (toolInputMap.get("scroll_amount") != null) {
scrollAmount = (Integer) toolInputMap.get("scroll_amount");
}
Integer tabId = null;
if (toolInputMap.get("tab_id") != null) {
tabId = (Integer) toolInputMap.get("tab_id");
}
try {
if (action == null) {
return new ToolExecuteResult("Action parameter is required");
}
WebDriver driver = getDriver();
switch (action) {
case "navigate": {
if (url == null) {
return new ToolExecuteResult("URL is required for 'navigate' action");
}
driver.get(url);
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Navigated to " + url);
}
case "click": {
List<WebElementWrapper> interactiveElements = getInteractiveElements(driver);
if (index == null) {
return new ToolExecuteResult("Index is required for 'click' action");
}
if (index < 0 || index >= interactiveElements.size()) {
return new ToolExecuteResult("Element with index " + index + " not found");
}
WebElementWrapper elementWrapper = interactiveElements.get(index);
elementWrapper.prepareForInteraction(driver);
WebElement element = elementWrapper.getElement();
log.info("Clicking element: {}", (element != null ? element.getText() : "No text"));
// 记录点击前的窗口状态
Set<String> beforeWindowHandles = driver.getWindowHandles();
String currentUrl = driver.getCurrentUrl();
// 执行点击操作
simulateHumanBehavior(element);
try {
element.click();
}
catch (ElementClickInterceptedException e) {
// 如果普通点击失败,尝试使用 JavaScript 点击
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].click();", element);
}
// 等待页面变化(最多等待10秒)
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
try {
// 检查是否有新窗口打开
Set<String> afterWindowHandles = driver.getWindowHandles();
if (afterWindowHandles.size() > beforeWindowHandles.size()) {
// 找出新打开的窗口
afterWindowHandles.removeAll(beforeWindowHandles);
String newHandle = afterWindowHandles.iterator().next();
// 切换到新窗口
driver.switchTo().window(newHandle);
log.info("New tab detected, switched to: {}", driver.getCurrentUrl());
refreshTabsInfo(driver); // 刷新标签页信息
return new ToolExecuteResult(
"Clicked element and opened in new tab: " + driver.getCurrentUrl());
}
// 检查URL是否发生变化
boolean urlChanged = wait.until(d -> !d.getCurrentUrl().equals(currentUrl));
if (urlChanged) {
log.info("Page navigated to: {}", driver.getCurrentUrl());
refreshTabsInfo(driver); // 刷新标签页信息
return new ToolExecuteResult("Clicked element and navigated to: " + driver.getCurrentUrl());
}
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
// 如果没有明显变化,返回普通点击成功消息
return new ToolExecuteResult("Clicked element at index " + index);
}
catch (TimeoutException e) {
// 如果超时,检查是否仍在原页面
if (!driver.getCurrentUrl().equals(currentUrl)) {
return new ToolExecuteResult("Clicked and page changed to: " + driver.getCurrentUrl());
}
return new ToolExecuteResult(
"Clicked element at index " + index + " (no visible navigation occurred)");
}
}
case "input_text": {
if (index == null || text == null) {
return new ToolExecuteResult("Index and text are required for 'input_text' action");
}
List<WebElementWrapper> interactiveElements = getInteractiveElements(driver);
if (index < 0 || index >= interactiveElements.size()) {
return new ToolExecuteResult("Element with index " + index + " not found");
}
WebElementWrapper inputElementWrapper = interactiveElements.get(index);
inputElementWrapper.prepareForInteraction(driver);
WebElement inputElement = inputElementWrapper.getElement();
if (!inputElement.getTagName().equals("input") && !inputElement.getTagName().equals("textarea")) {
return new ToolExecuteResult("Element at index " + index + " is not an input element");
}
typeWithHumanDelay(inputElement, text);
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Successfully input '" + text + "' into element at index " + index);
}
case "key_enter": {
if (index == null) {
return new ToolExecuteResult("Index is required for 'key_enter' action");
}
List<WebElementWrapper> interactiveElements = getInteractiveElements(driver);
if (index < 0 || index >= interactiveElements.size()) {
return new ToolExecuteResult("Element with index " + index + " not found");
}
WebElementWrapper enterElementWrapper = interactiveElements.get(index);
enterElementWrapper.prepareForInteraction(driver);
WebElement enterElement = enterElementWrapper.getElement();
enterElement.sendKeys(Keys.RETURN);
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Hit the enter key at index " + index);
}
case "screenshot": {
TakesScreenshot screenshot = (TakesScreenshot) driver;
String base64Screenshot = screenshot.getScreenshotAs(OutputType.BASE64);
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult(
"Screenshot captured (base64 length: " + base64Screenshot.length() + ")");
}
case "get_html": {
String html = driver.getPageSource();
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult(
html.length() > MAX_LENGTH ? html.substring(0, MAX_LENGTH) + "..." : html);
}
case "get_text": {
String body = driver.findElement(By.tagName("body")).getText();
log.info("get_text body is {}", body);
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult(body);
}
case "execute_js": {
if (script == null) {
return new ToolExecuteResult("Script is required for 'execute_js' action");
}
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
Object result = jsExecutor.executeScript(script);
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
if (result == null) {
return new ToolExecuteResult("Successfully executed JavaScript code.");
}
else {
return new ToolExecuteResult(result.toString());
}
}
case "scroll": {
if (scrollAmount == null) {
return new ToolExecuteResult("Scroll amount is required for 'scroll' action");
}
((JavascriptExecutor) driver).executeScript("window.scrollBy(0," + scrollAmount + ");");
String direction = scrollAmount > 0 ? "down" : "up";
return new ToolExecuteResult("Scrolled " + direction + " by " + Math.abs(scrollAmount) + " pixels");
}
case "new_tab": {
if (url == null) {
return new ToolExecuteResult("URL is required for 'new_tab' action");
}
((JavascriptExecutor) driver).executeScript("window.open('" + url + "', '_blank');");
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Opened new tab with URL " + url);
}
case "close_tab": {
driver.close();
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Closed current tab");
}
case "switch_tab": {
if (tabId == null) {
return new ToolExecuteResult("Tab ID is out of range for 'switch_tab' action");
}
Object[] windowHandles = driver.getWindowHandles().toArray();
driver.switchTo().window(windowHandles[tabId].toString());
refreshTabsInfo(driver); // 刷新标签页信息
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Switched to tab " + tabId);
}
case "refresh": {
driver.navigate().refresh();
interactiveTextProcessor.refreshCache(driver);
return new ToolExecuteResult("Refreshed current page");
}
default:
return new ToolExecuteResult("Unknown action: " + action);
}
}
catch (Exception e) {
if (e instanceof ElementNotInteractableException) {
String errorMessage = String.format(
"""
Browser action '%s' failed, mostly like to have used the wrong index argument.
You can try to use 'get_html' to get and analyze the page HTML content first and then use other actions to find the right input element.
Tips for :
1. ignore all the hidden input or textarea elements.
2. for baidu engine, you can use js script to do the operation
detailed exception message:
%s
""",
action, e.getMessage());
return new ToolExecuteResult(errorMessage);
}
return new ToolExecuteResult("Browser action '" + action + "' failed: " + e.getMessage());
}
}