experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(download): register will-download handler on webview guest session

Popup windows spawned by target="_blank" download links were not cleaned
up after the download started. The existing popup-closing logic in
registerDownloadHandler was correct, but the handler was only wired up
on profileSession, and will-download fires on the session that owns the
webContents emitting the download. A <webview> inside a canvas page
gets its session from its partition attribute, which page.js sets
asynchronously. The guest webContents can end up on a different Session
instance than the host BrowserWindow, so the profileSession listener
never fires for the popup's download.

Fix: export registerDownloadHandler from session-partition.ts with
idempotent WeakSet-based tracking, and call it from the canvas page's
and popup's did-attach-webview handler on guestWebContents.session.
This ensures the download handler (and its popup-cleanup branch) runs
regardless of which session the webview ended up attached to.

Test: tests/desktop/download.spec.ts:151 target=_blank download cleans
up the popup window — was consistently failing at 37 vs 36 windows, now
passes in both isolation and the full suite.

+42 -1
+26
backend/electron/ipc.ts
··· 136 136 137 137 import { 138 138 getProfileSession, 139 + registerDownloadHandler, 139 140 } from './session-partition.js'; 140 141 141 142 import { ··· 1723 1724 popupWin.webContents.on('did-attach-webview', (_event, guestWebContents) => { 1724 1725 console.log(`[webview-popup] Guest webContents attached to popup ${popupWin.id}, adding keyboard shortcuts`); 1725 1726 1727 + // Register download handler on the guest's own session. Electron's 1728 + // `will-download` fires on the session that owns the webContents, 1729 + // not the parent BrowserWindow's session. A <webview> gets its 1730 + // session from its `partition` attribute (set async by page.js), 1731 + // which may be a different Session instance than the host window's 1732 + // `profileSession`. Without registering here, downloads that abort 1733 + // navigation leave the popup stuck with no page and no cleanup. 1734 + // `registerDownloadHandler` is idempotent via WeakSet tracking. 1735 + try { 1736 + registerDownloadHandler(guestWebContents.session); 1737 + } catch (e) { 1738 + console.log('[webview-popup] Failed to register download handler on guest session:', e); 1739 + } 1740 + 1726 1741 // Keyboard shortcuts inside the webview guest for page navigation 1727 1742 guestWebContents.on('before-input-event', (event, input) => { 1728 1743 const modifier = process.platform === 'darwin' ? input.meta : input.control; ··· 1910 1925 if (useCanvas) { 1911 1926 win.webContents.on('did-attach-webview', (_event, guestWebContents) => { 1912 1927 console.log(`[webview-popup] Guest webContents attached to window ${win.id}, adding setWindowOpenHandler + Cmd+L`); 1928 + 1929 + // Register download handler on the guest's own session. See the 1930 + // equivalent comment in the popup's did-attach-webview handler 1931 + // above — downloads from a <webview> guest fire `will-download` 1932 + // on the guest's session, which may differ from profileSession 1933 + // depending on how the webview's partition attribute was set. 1934 + try { 1935 + registerDownloadHandler(guestWebContents.session); 1936 + } catch (e) { 1937 + console.log('[webview-popup] Failed to register download handler on guest session:', e); 1938 + } 1913 1939 1914 1940 // Keyboard shortcuts inside the webview guest for page navigation 1915 1941 guestWebContents.on('before-input-event', (event, input) => {
+16 -1
backend/electron/session-partition.ts
··· 258 258 * Chromium aborts the download navigation and the webview stays put. 259 259 * 3. Webview guest in a popup window opened for a download URL: close 260 260 * the host window — the webview has no real content (blank page). 261 + * 262 + * Idempotent: tracks which sessions already have the handler so we can 263 + * safely call it from multiple places (profileSession init + webview 264 + * `did-attach-webview`) without double-registering. 261 265 */ 262 - function registerDownloadHandler(ses: Session): void { 266 + const sessionsWithDownloadHandler = new WeakSet<Session>(); 267 + 268 + export function registerDownloadHandler(ses: Session): void { 269 + if (sessionsWithDownloadHandler.has(ses)) { 270 + DEBUG && console.log('[download] Handler already registered on this session, skipping'); 271 + return; 272 + } 273 + sessionsWithDownloadHandler.add(ses); 274 + _registerDownloadHandlerImpl(ses); 275 + } 276 + 277 + function _registerDownloadHandlerImpl(ses: Session): void { 263 278 ses.on('will-download', (_event, item, webContents) => { 264 279 const filename = item.getFilename(); 265 280 const desiredPath = path.join(app.getPath('downloads'), filename);