外部ドメインページを表示したTauriのWebViewから、RustアプリケーションとIPCするまでのお話
メインのwindowでアプリケーションのUIを表示しつつ、外部ドメインページを開いた別windowからアプリケーションに対してIPCしたい。
メインwindowのフロントエンドからアプリケーションに通信する手段としては @tauri-apps/api の invoke などライブラリのAPIを利用すれば良いが、外部コンテンツを表示している間はTauriのAPIを実行できなくなってしまう。
WebViewにiframeを設けてコンテンツを表示するという手もあるが、 X-Frame-Options:Deny
を有効にしているコンテンツはiframeで描画できないのでイマイチ。
どうにか抜け道を探る。
Tauriには Rust側から任意のJavaScriptを実行できる様に Electronで言うところの contents.executeJavaScript 相当の実装として tauri::window::Window::eval
が用意されている。
evalの引数にJavaScriptを渡しwindowに実行させることでRustプロセス側から任意のJavaScriptを実行させることが可能。
サンプルとしてGoogleを開いたWebViewで console.log
で延々と名前を読み上げるJSを実行させてみる。
以下 tauri x viteをベースに実装したサンプル。
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{App, WindowBuilder};
#[tauri::command]
// jsからinvoke('greet', { name: string })で呼び出される
fn greet(name: &str) -> String {
println!("greeting from: {}", name);
return format!("Hello, {}! You've been greeted from Rust!", name);
}
fn create_external_window(app: &App) {
let external = WindowBuilder::new(
app,
"external",
tauri::WindowUrl::External("https://google.com/".parse().unwrap()),
)
.build()
.expect("error while building external");
let js_eval = format!(
r#"
const names = ['jo', 'doe', 'foo', 'bar'];
let count = 0;
setInterval(async () => {{
console.log(names[count]);
count++;
if (count === names.length) {{
count = 0;
}}
}}, 1000);
"#
);
external.eval(&js_eval).unwrap();
}
fn main() {
tauri::Builder::default()
.setup(|app| {
create_external_window(&app);
Ok(())
})
.invoke_handler(tauri::generate_handler![greet]) // invokeで実行可能なcommandを登録
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
こいつ、動くぞ!
実はTauriアプリケーションで作られるWebViewからはグローバル関数に登録されている window.__TAURI__
を実行することで各種APIが利用できる様になっている。
これを利用してWebView側からIPCできないか試みる。
試しに invoke
に相当する await window.__TAURI_INVOKE__("greet", {name: 'joe'})
をconsoleから実行してみた。
メインウィンドウでは期待通り動作し、Rustプロセスのprintlnも出力された。
fn greet(name: &str) -> String {
println!("greeting from: {}", name); // 端末に出力
return format!("Hello, {}! You've been greeted from Rust!", name); // invokeを実行したWebViewへのレスポンス
}
しかし、Googleを開いたWebView (external) 側は失敗。どうやらscopeの設定が甘いらしい。
Scope not defined for window `external` and URL `https://www.google.com/`. See https://tauri.app/v1/api/config/#securityconfig.dangerousremotedomainipcaccess and https://docs.rs/tauri/1/tauri/scope/struct.IpcScope.html#method.configure_remote_access
どうやら外部ドメインからIPCできないようにスコープを切りつつホワイトリストに追加する必要があるようだ。
tauri.conf.json
の security.dangerousRemoteDomainIpcAccess
プロパティを設定してみる。
外部ドメイン側からTauriアプリケーションに対してアクセス可能となるため、ドキュメントには信頼の置けないドメインに対して設定しない旨注意書きがされている。
WARNING: Only use this option if you either have internal checks against malicious external sites or you can trust the allowed external sites. You application might be vulnerable to dangerous Tauri command related attacks otherwise.
{
"tauri": {
"security": {
"csp": null,
"dangerousRemoteDomainIpcAccess": [
{
"domain": "www.google.com", // 実行を許容するドメイン
"windows": ["external"] // 外部ドメインサイトを表示するwindowのlabelを指定する
}
]
}
}
やったね
追記: Tauri V2 では tauri.conf.json
のappプロパティをこの様にすると良さそう
{
"app": {
"security": {
"csp": {
"connect-src": [
"ipc://localhost",
"https://www.google.com",
],
}
},
"withGlobalTauri": true
},
}