You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a BlazorWebView page reloads (e.g., location.reload(), Android app foregrounded after being backgrounded), the old PageContext is disposed via WebViewManager.AttachToPageAsync(). During disposal, components implementing IAsyncDisposable may invoke JS interop on IJSObjectReference — following Microsoft's recommended cleanup pattern.
These JS calls go through WebViewJSRuntime.BeginInvokeJS() → IpcSender → the reloaded page's JS context, where the old JS object IDs no longer exist. This produces an unhandled JSException: JS object instance with ID X does not exist (has it been disposed?).
In Blazor Server, RemoteJSRuntime handles this correctly by throwing JSDisconnectedException when the circuit is disposed (#32901). WebViewJSRuntime has no equivalent guard.
Description
When a BlazorWebView page reloads (e.g.,
location.reload(), Android app foregrounded after being backgrounded), the oldPageContextis disposed viaWebViewManager.AttachToPageAsync(). During disposal, components implementingIAsyncDisposablemay invoke JS interop onIJSObjectReference— following Microsoft's recommended cleanup pattern.These JS calls go through
WebViewJSRuntime.BeginInvokeJS()→IpcSender→ the reloaded page's JS context, where the old JS object IDs no longer exist. This produces an unhandledJSException: JS object instance with ID X does not exist (has it been disposed?).In Blazor Server,
RemoteJSRuntimehandles this correctly by throwingJSDisconnectedExceptionwhen the circuit is disposed (#32901).WebViewJSRuntimehas no equivalent guard.Reproduction
https://github.com/mattleibow/MauiBlazorReloadIssue
IAsyncDisposablewithIJSObjectReference)JSExceptionoccursThe Counter page (sync
IDisposable, no JS in dispose) survives reload for comparison.Proposed Fix
IsDisposed/IsDisconnectedflag toPageContextPageContext.DisposeAsync()(beforeRenderer.DisposeAsync())WebViewJSRuntime.BeginInvokeJS(), check the flag and throwJSDisconnectedException— matchingRemoteJSRuntimebehaviorJSObjectReference.DisposeAsync()already catchesJSDisconnectedExceptionon main (Change IJSObjectReference.Dispose specifically so that it deals with catching and discarding JSDisconnectedException internally #49418)Additionally, guard
IpcSenderandIpcReceiveragainst disposedPageContextto prevent corruption of the new page state.Affected files
src/Components/WebView/WebView/src/PageContext.cs— add disposed flagsrc/Components/WebView/WebView/src/Services/WebViewJSRuntime.cs— throwJSDisconnectedExceptionsrc/Components/WebView/WebView/src/IpcSender.cs— guard against disposed contextsrc/Components/WebView/WebView/src/IpcReceiver.cs— ignore messages for disposed contextRelated
IJSObjectReference.DisposeJSDisconnectedExceptionhandling (already fixed on main)RemoteJSRuntime)WebView2WebViewManagerdisposal handling maui#34855 —WebView2WebViewManagerdisposal race condition (same family)