tauri
tauri copied to clipboard
[bug] Iframe is not able to receive invoke callbacks
Describe the bug
I am mounting an iframe ("child") within a Tauri app. I control both the parent and child and they both are rendered via tauri's vite build. Both the parent and the child have access to window.__TAURI__
and related methods.
The problem is that I'm not able to invoke
handlers from the iframe. Using invoke
from the ifram produces the following warning in the developer console:
[TAURI] Couldn't find callback id 90962087 in window. This happens when the app is reloaded while Rust is running an asynchronous operation.
It seems that iframe invoke
successfully registers callbacks and calls the rust handlers, which are successfully run. However, when rust tries to return the response, the callback is caught by the parent window instead of the iframe. The parent does not have the callbacks registered, hence the warning.
Is there some way to get Tauri to work with this setup? One way to go about this would be to prefix the callback handlers by frame or window?
Reproduction
Repro:
- Clone repo at https://github.com/gamgi/repro-tauri and enter its root.
- Install prerequisites
npm install
- Start the tauri app
cargo tauri dev
- In parent greet box, type name and press greet (it works)
- In child greet box, type name and press greet (it does not work)
- Open console, see error
[TAURI] Couldn't find callback id 90962087 in window. This happens when the app is reloaded while Rust is running an asynchronous operation.
Additional verifications:
- The iframe does have tauri IPC defined, this can be verified by
document.querySelector("#root > div > iframe").contentWindow.__TAURI__
. - The iframe is able to call tauri, which can be verified by the "Rust backend called with ..." log from rust
Expected behavior
I expect the child window to be able to invoke tauri handlers from the iframe and to get a response.
Platform and versions
Environment › OS: Mac OS 10.15.7 X64 › Node.js: 18.12.1 › npm: 8.19.2 › pnpm: Not installed! › yarn: Not installed! › rustup: 1.25.1 › rustc: 1.66.1 › cargo: 1.66.1 › Rust toolchain: 1.66.1-x86_64-apple-darwin
Packages › @tauri-apps/cli [NPM]: 1.2.3 › @tauri-apps/api [NPM]: 1.2.0 › tauri [RUST]: 1.2.4, › tauri-build [RUST]: 1.2.1, › tao [RUST]: 0.15.8, › wry [RUST]: 0.23.4,
App › build-type: bundle › CSP: unset › distDir: ../dist › devPath: http://localhost:1420/ › framework: React › bundler: Vite
App directory structure ├─ node_modules ├─ public ├─ src-tauri ├─ .git ├─ .vscode └─ src
Stack trace
`[TAURI] Couldn't find callback id 90962087 in window. This happens when the app is reloaded while Rust is running an asynchronous operation.`
Additional context
No response
Indeed, the child iframe is able to invoke
tauri commands, but the callbacks are fired on root window instead of the iframe, resulting in Couldn't find callback id ... in window
. This seems like a bug.
The workaround is to use a custom invoke initialization script using tauri::Builder::invoke_system(invoke_initialization_script, ...)
which adds proxies from the parent window to the iframe.
I have a working implementation outlined in this commit.
Hi, just leaving more information here because I'm familiar with the topic. WebKit (macOS and Linux) actually supports an option that makes all invoking be able to happen on a parent or a child, separately. It allows you to create and use callbacks solely on the child frame. Unfortunately Webview2 (Windows) does not support something like this (at the last time I checked which is around Tauri 1.0) and all callbacks get received by the parent. Because of this, the feature is not enabled on WebKit to ensure the same behavior across platforms.
Your solution probably works just fine if the iframe is not sandboxed (I didn't run your repro but that commit diff makes sense) but might break if the sandbox is activated for the iframe. An alternative solution that works with the sandbox is to use postMessage
to communicate between the parent and child. This is actually how the Isolation Pattern is implemented, where the parent IPC potentially sets up communication with the child frame. The key generation and encryption stuff is specific to the Isolation Pattern, but you can see how they are using postMessage
to communicate between them.
Perhaps we could come up with some provided scripts or documentation example to help people implement that for their own iframe children easier.
On a side note, you might be interested in the template feature of serialize-to-javascript
for your solution.
any update on this? We have a scenario with a site with an iFrame. and we need to communicate from child iFrame to the rust layer. Can someone please help with how I can setup Tauri to have the child iFrame-rust communication.
I am only building child iFrame code with Tauri/api. The host site doesn't know anything about tauri
If your iframe and main window have the same domain name, then this may be a good method(Add the following code to the very top of iframe js initialization):
// Override the __TAURI_IPC__ function
window.__TAURI_IPC__ = (args) => {
//The parent window callback points to the child window
window.parent[`_${args.callback}`] = window[`_${args.callback}`];
// Call the function of the parent window
window.parent.__TAURI_IPC__(args);
};
First, Tauri will mount the global window.__TAURI_IPC__
in the main window by default, but there is no such function in the child iframe. However, many of Tauri's APIs are based on the window.__TAURI_IPC__
function to communicate with the Tauri backend, so we need to set a window.__TAURI_IPC__
function for the child iframe.
However, the callback function of the communication is defined in the child iframe, so when the communication ends, the callback
function cannot be found in the parent window, so we need to assign the callback
function of the child iframe to the window object of the parent window.