fix(desktop): hand preview guest links to the audited opener via a guest preload

Preview-pane guest pages (Streamlit's traceback "Ask Google" / "Ask …"
buttons, plain `<a target="_blank">` anchors) could not open anything: the
`<webview>` has no `allowpopups`, so Chromium drops the popup before any
handler runs. Opening from `setWindowOpenHandler` is banned
(GHSA-9f4c-93c8-jc8g, window-open-policy.ts), so this adds an explicit
click bridge instead:

- main.ts installs a guest preload through `will-attach-webview`, keyed on
  the `persist:hermes-preview` partition only.
- The preload forwards a clicked `_blank` anchor's resolved href to the
  host renderer via `ipcRenderer.sendToHost`; it opens nothing itself.
- PreviewPane admits the URL and routes it through the existing audited
  `hermes:openExternal` IPC.

Salvaged from PR #112959 (squash of its two commits).

Fixes #112941
This commit is contained in:
liuhao1024
2026-09-16 21:48:29 -07:00
committed by Teknium
parent 091b8c4f53
commit b08ec00a70
10 changed files with 380 additions and 2 deletions

View File

@@ -4,8 +4,9 @@
// node_modules/ or tsx at runtime.
//
// Output:
// dist/electron-main.mjs (MJS bundle — entry point for packaged app)
// dist/electron-preload.js (CJS bundle — loaded via BrowserWindow preload)
// dist/electron-main.mjs (MJS bundle — entry point for packaged app)
// dist/electron-preload.js (CJS bundle — loaded via BrowserWindow preload)
// dist/preview-guest-preload.js (CJS bundle — preview <webview> guest preload)
//
// `electron` and `node-pty` are external (provided by the runtime / staged
// separately via stage-native-deps).
@@ -66,3 +67,21 @@ await build({
logLevel: 'info',
})
console.log(`bundled ${preloadOut}${isDev ? ' (dev)' : ''}`)
// Bundle preview-guest-preload-entry.ts → dist/preview-guest-preload.js
// (main.ts hands this path to the preview webview via will-attach-webview)
const guestPreloadEntry = resolve(root, 'electron/preview-guest-preload-entry.ts')
const guestPreloadOut = resolve(distDir, 'preview-guest-preload.js')
await build({
entryPoints: [guestPreloadEntry],
bundle: true,
platform: 'node',
format: 'cjs',
target: 'node20',
outfile: guestPreloadOut,
external,
define,
logLevel: 'info',
})
console.log(`bundled ${guestPreloadOut}${isDev ? ' (dev)' : ''}`)