fix(window): per-instance WebView2 user data folders so new launchers boot alongside old ones#342
Merged
Merged
Conversation
… boot alongside old ones Multi-instance users (issue #340) found that after leaving several launchers running for a while, newly started launchers showed a process in Task Manager but never any UI. Root cause: every instance shared Tauri's default WebView2 user data folder, and all WebView2s sharing a folder share one browser process. CoreWebView2Environment creation fails with ERROR_INVALID_STATE when it does not exactly match the running browser process — a different WebView2 Runtime version (the Evergreen runtime auto-updates in the background: the 'left idle for a while' trigger) or different browser args (mixing app versions). And because the main window is transparent + undecorated, a window whose webview failed paints nothing: invisible process, exactly as reported. Cross-process sharing is fragile even without version skew (MicrosoftEdge/WebView2Feedback#4751). Fix: give each instance its own throwaway profile at %LOCALAPPDATA%\tw.beanfun.app\webview-instances\<pid>. Nothing in this app needs webview persistence (settings live in Config.xml via the backend; login cookies live in the backend HTTP client's jar), so isolation costs nothing functionally and removes the shared browser process entirely. - lib.rs: prepare_webview_data_dir() creates the folder and holds an open .instance-lock handle for the process lifetime; sweep_stale_webview_dirs() reclaims dead instances' folders on boot (rename-then-delete: Windows refuses to rename a directory while any file inside is open, so live instances can never be swept — no PID table races) - main window is now built in setup() via WebviewWindowBuilder (same properties as the old tauri.conf.json entry, label stays 'main') so the PID-dependent data_directory can be injected - 5 new unit tests for the sweep protocol Verified on-device: two simultaneous instances each get their own folder and visible window; a later boot sweeps both dead folders.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Multi-instance users report (#340) that after leaving several launchers running for a while (8 in the report), newly started launchers show a process in Task Manager but never display any UI.
Root cause chain:
%LOCALAPPDATA%\tw.beanfun.app\EBWebView), and all WebView2s sharing a folder share one browser process.CoreWebView2Environmentcreation fails withHRESULT_FROM_WIN32(ERROR_INVALID_STATE)when the new environment doesn't exactly match the already-running browser process — a different WebView2 Runtime version or different environment options. The Evergreen WebView2 Runtime auto-updates in the background, which is the "left idle for a while" trigger: instances started before the update keep the old browser process alive, and every launcher started after it fails webview creation. Mixing Beanfun versions (whose browser args differ) trips the same check, and cross-process sharing is fragile even without version skew (WebView2Feedback#4751, closed "not planned").transparent: true+decorations: false— with no webview there is nothing to paint, so the failed instance is an invisible process. Exactly as reported.Fix
Give each instance its own throwaway profile:
%LOCALAPPDATA%\tw.beanfun.app\webview-instances\<pid>. No two Beanfun processes ever negotiate over a shared browser process, so runtime auto-updates and version mixes can no longer strand new instances. Nothing in this app needs webview persistence — settings live inConfig.xmlvia the backend (pinia-plugin-persistedstatedeliberately not installed) and login cookies live in the backend HTTP client's cookie jar — so isolation costs nothing functionally..instance-lockhandle in its folder for its whole lifetime, so a live instance can never be swept. Interrupted deletes keep the.stalename and are retried next boot. The sweep runs on a background thread off the boot path.setup()viaWebviewWindowBuilder(identical properties to the oldtauri.conf.jsonentry, label stays"main") because a static config can't carry a PID-dependent path..stale, ignores plain files).Verified on-device
webview-instances\<pid>folder.Trade-off notes
Closes #340