Короткий ответ — «напрямую, кроссплатформенно и без сотрудничества браузера — нельзя». Плагин opener и простое открытие URL со стороны ОС/процесса браузера по умолчанию создают новую вкладку/окно; браузеры не дают внешним процессам произвольно «подменять» адрес в уже открытой вкладке по соображениям безопасности/моделей процесса.
Но есть практические пути обойти это, с разной степенью надёжности и требованиями к пользователю/среде. Ниже — варианты (от самых простых до наиболее надёжных) и примерные реализации.
1) macOS — AppleScript (самый простой, работает для Chrome/Safari)
- если ваше приложение целится в macOS, можно вызвать osascript, чтобы сказать Chrome заменить URL в активной вкладке:
- AppleScript: tell application "Google Chrome" to set URL of active tab of front window to "https://example.com"
- Пример вызова из Rust:
- use std::process::Command;
- let url = "https://example.com";
- let script = format!(r#"tell application "Google Chrome" to set URL of active tab of front window to "{}""#, url);
- Command::new("osascript").arg("-e").arg(script).status()?;
- Для Safari: tell application "Safari" to set URL of current tab of front window to "https://..."
Плюсы: просто. Минусы: только macOS и только если у пользователя установлен соответствующий браузер; fragile, требует разрешений.
2) Chrome DevTools Protocol (CDP / remote debugging) — контролировать вкладки напрямую
- Возможен для Chrome/Chromium: запустить Chrome с флагом --remote-debugging-port=9222 и затем использовать HTTP API /json и WebSocket (webSocketDebuggerUrl) для отправки команды Page.navigate к выбранной вкладке.
- Недостаток: нужно, чтобы Chrome был запущен с этим флагом (либо вы запускаете собственный экземпляр Chrome). Нельзя подключиться к произвольно запущенному без этого флага.
- Пример логики:
1. GET http://127.0.0.1:9222/json — получить список вкладок (каждая запись содержит webSocketDebuggerUrl).
2. Открыть WebSocket и отправить JSON вида {"id":1,"method":"Page.navigate","params":{"url":"https://example.com"}}.
- В Rust можно использовать reqwest (или ureq) для HTTP и tokio-tungstenite / tungstenite для WebSocket.
- Плюсы: мощно и надежно для Chrome. Минусы: требует флага; не подходит для «произвольного» уже запущенного браузера.
3) Расширение браузера + native messaging (наиболее надёжное и кросс-платформенное в рамках одного браузера)
- Сделать extension (Chrome/Firefox) которое имеет permission tabs и слушает сообщения (например через native messaging или через WebSocket к локальному серверу), а при получении команды меняет URL текущей вкладки (chrome.tabs.update(tabId, { url: "…" })).
- Native messaging позволяет нативному приложению (вашему Tauri/Rust) отправлять сообщение расширению, и расширение выполняет navigate.
- Плюсы: работает с обычным браузером без специальных флагов, надёжно и быстро (ведь расширение запускается в контексте браузера). Минусы: пользователь должен установить расширение (и зарегистрировать host для native messaging), требуется реализация расширения для каждого браузера.
4) Автоматизация GUI (xdotool, AppleScript GUI, Win32 UI Automation)
- На Linux можно отправить комбинацию клавиш / строку адреса в окно браузера через xdotool; на Windows — UI Automation или AutoHotkey; на macOS — AppleScript GUI.
- Плюсы: можно «взломать» поведение браузера. Минусы: хрупко, зависит от локали, фокуса окна и т.п.
Почему eoka-agent/headless_chrome не помогли
- headless_chrome обычно запускает изолированный экземпляр браузера (headless) — он управляет этим экземпляром, но не «вклинивается» в уже запущенный пользовательский браузер. eoka-agent похожим образом не решает контроль над произвольно запущенным Chrome без remote-debugging.
Рекомендация практического решения
- Если нужно только для macOS — используйте AppleScript (самое простое).
- Если целитесь в Chrome и готовы запускать/поддерживать экземпляр с --remote-debugging-port — используйте CDP (наиболее мощный способ).
- Если нужен надёжный пользовательский опыт на обычном браузере — делайте расширение + native messaging; это «правильный» способ заставить браузер заменить URL в существующей вкладке так же, как window.location.href внутри страницы.
Если хотите, могу:
- Привести пример Rust-кода, который вызывает osascript (macOS).
- Привести пример минимального CDP-клиента на Rust (fetch /json -> подключиться к WebSocket -> Page.navigate).
- Описать структуру расширения Chrome и пример обмена сообщениями(native messaging).
Скажите, какой из подходов вам предпочтительнее (macOS only, CDP для Chrome, или расширение + native messaging), и я пришлю конкретный пример кода.