fix(vscode,ui): stop postMessage crash when opening chat in Cursor (#2335)
VS Code webviews delete window.parent, so ChatContainer's settings-sync effect threw TypeError on chat open. Also harden the webview bridge when acquireVsCodeApi() returns undefined and SSE panel disposal races. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Cursor Agent
Serhii Dziupin
parent
200c02cd00
commit
485efc7117
@@ -715,10 +715,15 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
|
||||
const promptReadOnly = parentSession ? !allowPromptingSubagentSessions : readOnly;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window === 'undefined' || window.parent === window) {
|
||||
// VS Code/Cursor/Positron webviews delete window.parent (and window.top).
|
||||
// The old `window.parent === window` check does not catch that, so
|
||||
// `window.parent.postMessage(...)` threw on chat open:
|
||||
// TypeError: Cannot read properties of undefined (reading 'postMessage')
|
||||
if (typeof window === 'undefined' || !window.parent || window.parent === window) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentWindow = window.parent;
|
||||
const applySetting = (value: boolean) => {
|
||||
useUIStore.getState().setAllowPromptingSubagentSessions(value);
|
||||
};
|
||||
@@ -729,7 +734,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
|
||||
applySetting(payload.allowPromptingSubagentSessions);
|
||||
};
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.source !== window.parent || event.origin !== window.location.origin) return;
|
||||
if (event.source !== parentWindow || event.origin !== window.location.origin) return;
|
||||
const data = event.data as { type?: unknown; payload?: { allowPromptingSubagentSessions?: unknown } };
|
||||
if (data?.type !== 'openchamber:chat-settings-sync'
|
||||
|| typeof data.payload?.allowPromptingSubagentSessions !== 'boolean') return;
|
||||
@@ -738,7 +743,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
|
||||
|
||||
scopedWindow.__openchamberApplyChatSettingsSync = applySync;
|
||||
window.addEventListener('message', handleMessage);
|
||||
window.parent.postMessage({ type: 'openchamber:chat-settings-request' }, window.location.origin);
|
||||
parentWindow.postMessage({ type: 'openchamber:chat-settings-request' }, window.location.origin);
|
||||
return () => {
|
||||
window.removeEventListener('message', handleMessage);
|
||||
if (scopedWindow.__openchamberApplyChatSettingsSync === applySync) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
|
||||
/**
|
||||
* Mirrors the ChatContainer chat-settings-sync guard.
|
||||
* VS Code/Cursor/Positron webviews delete `window.parent`, so the old
|
||||
* `window.parent === window` check still fell through to `.postMessage` and
|
||||
* crashed chat open with:
|
||||
* TypeError: Cannot read properties of undefined (reading 'postMessage')
|
||||
*/
|
||||
const canPostMessageToParentFrame = (win: { parent?: unknown } | undefined): boolean => {
|
||||
if (typeof win === 'undefined' || !win) return false;
|
||||
return Boolean(win.parent) && win.parent !== win;
|
||||
};
|
||||
|
||||
describe('parent-frame postMessage guard (VS Code webview)', () => {
|
||||
test('rejects when parent was deleted (VS Code webview injector behavior)', () => {
|
||||
const vscodeLikeWindow = { parent: undefined };
|
||||
expect(canPostMessageToParentFrame(vscodeLikeWindow)).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects when parent is null', () => {
|
||||
expect(canPostMessageToParentFrame({ parent: null })).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects top-level windows where parent === self', () => {
|
||||
const topLevel = {} as { parent?: unknown };
|
||||
topLevel.parent = topLevel;
|
||||
expect(canPostMessageToParentFrame(topLevel)).toBe(false);
|
||||
});
|
||||
|
||||
test('allows real embedded iframe parent windows', () => {
|
||||
const parent = {};
|
||||
const child = { parent };
|
||||
expect(canPostMessageToParentFrame(child)).toBe(true);
|
||||
});
|
||||
|
||||
test('old guard incorrectly allows deleted parent', () => {
|
||||
const vscodeLikeWindow = { parent: undefined as unknown };
|
||||
const oldGuardWouldSkip = vscodeLikeWindow.parent === vscodeLikeWindow;
|
||||
expect(oldGuardWouldSkip).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user