#53924: fix: post navigations started while a dead renderer is torn down
Description of Change
If a renderer process dies while its page load is still waiting to commit, and the app starts another navigation as soon as that load fails, the main process crashes. For example:
win.loadURL(url).catch(() => win.loadURL('about:blank'))The load failure is reported through did-stop-loading, which Electron emits (and which rejects the loadURL() promise) from inside Chromium's cleanup of the dead renderer. The second loadURL() then swapped out a frame host Chromium was still tearing down, which hits a CHECK in ~RenderFrameHostImpl, or a use-after-free when the dead renderer belonged to a speculative frame host (for example after a Cross-Origin-Opener-Policy response).
did-stop-loading stays synchronous. While it is being emitted for a load that ended because its renderer died, loadURL(), reload(), reloadIgnoringCache(), goBack(), goForward(), goToOffset() and goToIndex() post themselves to the next task instead of navigating from inside the cleanup, so the app's follow-up navigation still happens. Electron detects the case from the main frame's process already being marked dead, or from DidFinishNavigation having just reported a navigation discarded because its renderer died (the speculative-frame case).
Two specs are added next to the #51900 ones, one per variant; they freeze the renderer with SIGSTOP so are skipped on Windows. On Linux both crash the main process without the change and pass with it, and the loading, navigation-history and crash specs in api-web-contents-spec and api-browser-window-spec pass.
Checklist
- PR description included and stakeholders cc'd
-
npm testpasses - tests are changed or added
- I have reviewed and verified the changes
Release Notes
Notes: Fixed a main-process crash when an app called loadURL() again right after a load failed because the renderer process exited before the page committed.
Backports
Semver Impact
Semantic Versioning helps users understand the impact of updates:
- Major (X.y.z): Breaking changes that may require code modifications
- Minor (x.Y.z): New features that maintain backward compatibility
- Patch (x.y.Z): Bug fixes that don't change the API
- None: Changes that don't affect using facing parts of Electron