fix(desktop): guard print crash Closes #101880
This commit is contained in:
@@ -698,6 +698,31 @@ describe('PreviewPane console state', () => {
|
||||
profile: 'macmini'
|
||||
})
|
||||
})
|
||||
|
||||
// #101880: guest window.print() segfaults the macOS native print panel —
|
||||
// the pane stubs print in every guest document so the panel is never built.
|
||||
it('stubs window.print in the guest on dom-ready', async () => {
|
||||
let rendered!: ReturnType<typeof render>
|
||||
await act(async () => {
|
||||
rendered = render(
|
||||
<PreviewPane
|
||||
target={{ kind: 'url', label: 'Preview', source: 'http://localhost:5174', url: 'http://localhost:5174' }}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const webview = rendered.container.querySelector('webview') as HTMLElement & Record<string, unknown>
|
||||
const executeJavaScript = vi.fn(async (_code: string) => undefined)
|
||||
|
||||
Object.assign(webview, { executeJavaScript })
|
||||
|
||||
act(() => {
|
||||
webview.dispatchEvent(new Event('dom-ready'))
|
||||
})
|
||||
|
||||
expect(executeJavaScript).toHaveBeenCalledOnce()
|
||||
expect(String(executeJavaScript.mock.calls[0]?.[0])).toContain('window.print')
|
||||
})
|
||||
})
|
||||
|
||||
describe('PreviewPane guest external handoff', () => {
|
||||
|
||||
@@ -187,6 +187,20 @@ function isModuleMimeError(message: string): boolean {
|
||||
return lower.includes('failed to load module script') && lower.includes('mime type')
|
||||
}
|
||||
|
||||
/**
|
||||
* #101880: a guest page's `window.print()` (e.g. a Google Doc's Print
|
||||
* button) reaches the macOS native print panel, whose construction
|
||||
* segfaults inside PrintCore — the whole app dies before any dialog
|
||||
* appears. Stub `print` in the guest so the native panel is never built;
|
||||
* the warn surfaces in the preview console via the existing pipe. The
|
||||
* `__hermesPrintGuard` flag keeps re-arms idempotent. Full print-to-PDF
|
||||
* routing is the follow-up; this stops the crash.
|
||||
*/
|
||||
const PREVIEW_PRINT_GUARD_SCRIPT =
|
||||
'(function(){if(window.__hermesPrintGuard)return;window.__hermesPrintGuard=true;' +
|
||||
'window.print=function(){console.warn("[Hermes] Printing is disabled in the in-app preview. ' +
|
||||
'Open the page in your browser to print.");};})()'
|
||||
|
||||
function PreviewLoadError({
|
||||
consoleHeight = 0,
|
||||
error,
|
||||
@@ -1175,6 +1189,15 @@ export function PreviewPane({
|
||||
notePage()
|
||||
}
|
||||
|
||||
// #101880: arm the print guard for every new guest document — a fresh
|
||||
// load gets a fresh window object, so the stub must be re-injected.
|
||||
const armPrintGuard = () => {
|
||||
void webview.executeJavaScript?.(PREVIEW_PRINT_GUARD_SCRIPT)?.catch(() => {
|
||||
// Guest tore down mid-arm (a navigation raced the injection) — the
|
||||
// next dom-ready arms it again.
|
||||
})
|
||||
}
|
||||
|
||||
// The WEBVIEW is the source of truth for DevTools, not our click handler:
|
||||
// closing the DevTools window itself fires devtools-closed with no click,
|
||||
// and the glyph was left stuck "on" when we tracked it locally.
|
||||
@@ -1268,6 +1291,8 @@ export function PreviewPane({
|
||||
// SPAs title themselves long after the load settles, and a route change
|
||||
// renames the page without navigating at all.
|
||||
webview.addEventListener('page-title-updated', notePage)
|
||||
// #101880: never let a guest reach the native print panel.
|
||||
webview.addEventListener('dom-ready', armPrintGuard)
|
||||
host.appendChild(webview)
|
||||
webviewRef.current = webview
|
||||
|
||||
@@ -1284,6 +1309,7 @@ export function PreviewPane({
|
||||
webview.removeEventListener('did-start-loading', onStart)
|
||||
webview.removeEventListener('did-stop-loading', onStop)
|
||||
webview.removeEventListener('page-title-updated', notePage)
|
||||
webview.removeEventListener('dom-ready', armPrintGuard)
|
||||
webview.remove()
|
||||
setAnnotate(session => (session.mode ? { ...endAnnotateMode(session), stack: emptyAnnotateStack() } : session))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user