From b5648eaabd879990bb605ebe346ee10472d65c22 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 11 Oct 2025 18:39:45 +0200 Subject: [PATCH] Fix extension context detection in runtime providers Replace window.sendRuntimeMessage existence check with URL-based detection. The sendRuntimeMessage function exists in both extension and non-extension contexts (e.g., downloaded HTML artifacts), causing incorrect behavior. Changes: - Add window.__isExtensionContext() helper to RuntimeMessageBridge that checks: - chrome-extension:// URLs (Chrome) - moz-extension:// URLs (Firefox) - about:srcdoc (sandbox iframes) - Update all runtime providers to use __isExtensionContext() instead: - FileDownloadRuntimeProvider: Correctly falls back to browser download - ConsoleRuntimeProvider: Only sends messages in extension context - ArtifactsRuntimeProvider: Properly detects offline/read-only mode This fixes the issue where downloaded HTML artifacts incorrectly try to communicate with the extension when opened from disk. --- .../components/sandbox/ArtifactsRuntimeProvider.ts | 8 ++++---- .../components/sandbox/ConsoleRuntimeProvider.ts | 6 +++--- .../sandbox/FileDownloadRuntimeProvider.ts | 4 ++-- .../src/components/sandbox/RuntimeMessageBridge.ts | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts b/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts index b0a7c3a5..5f2509f1 100644 --- a/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts +++ b/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts @@ -43,7 +43,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider { (window as any).listArtifacts = async (): Promise => { // Online: ask extension - if ((window as any).sendRuntimeMessage) { + if ((window as any).__isExtensionContext?.()) { const response = await (window as any).sendRuntimeMessage({ type: "artifact-operation", action: "list", @@ -61,7 +61,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider { let content: string; // Online: ask extension - if ((window as any).sendRuntimeMessage) { + if ((window as any).__isExtensionContext?.()) { const response = await (window as any).sendRuntimeMessage({ type: "artifact-operation", action: "get", @@ -94,7 +94,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider { content: any, mimeType?: string, ): Promise => { - if (!(window as any).sendRuntimeMessage) { + if (!(window as any).__isExtensionContext?.()) { throw new Error("Cannot create/update artifacts in offline mode (read-only)"); } @@ -117,7 +117,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider { }; (window as any).deleteArtifact = async (filename: string): Promise => { - if (!(window as any).sendRuntimeMessage) { + if (!(window as any).__isExtensionContext?.()) { throw new Error("Cannot delete artifacts in offline mode (read-only)"); } diff --git a/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts b/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts index 908d46eb..d3b34eba 100644 --- a/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts +++ b/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts @@ -51,8 +51,8 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider { // Always log locally too (originalConsole as any)[method].apply(console, args); - // Send immediately and track the promise - if ((window as any).sendRuntimeMessage) { + // Send immediately and track the promise (only in extension context) + if ((window as any).__isExtensionContext?.()) { const sendPromise = (window as any) .sendRuntimeMessage({ type: "console", @@ -108,7 +108,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider { const finalError = error || lastError; - if ((window as any).sendRuntimeMessage) { + if ((window as any).__isExtensionContext?.()) { if (finalError) { await (window as any).sendRuntimeMessage({ type: "execution-error", diff --git a/packages/web-ui/src/components/sandbox/FileDownloadRuntimeProvider.ts b/packages/web-ui/src/components/sandbox/FileDownloadRuntimeProvider.ts index beb74b91..313cc900 100644 --- a/packages/web-ui/src/components/sandbox/FileDownloadRuntimeProvider.ts +++ b/packages/web-ui/src/components/sandbox/FileDownloadRuntimeProvider.ts @@ -52,8 +52,8 @@ export class FileDownloadRuntimeProvider implements SandboxRuntimeProvider { finalMimeType = mimeType || "application/json"; } - // Send to extension if available (online mode) - if ((window as any).sendRuntimeMessage) { + // Send to extension if in extension context (online mode) + if ((window as any).__isExtensionContext?.()) { const response = await (window as any).sendRuntimeMessage({ type: "file-returned", fileName, diff --git a/packages/web-ui/src/components/sandbox/RuntimeMessageBridge.ts b/packages/web-ui/src/components/sandbox/RuntimeMessageBridge.ts index b2e754e5..1e838b15 100644 --- a/packages/web-ui/src/components/sandbox/RuntimeMessageBridge.ts +++ b/packages/web-ui/src/components/sandbox/RuntimeMessageBridge.ts @@ -28,6 +28,13 @@ export class RuntimeMessageBridge { // Returns stringified function that uses window.parent.postMessage return ` window.__completionCallbacks = []; +// Check if we're in an extension context by examining the URL +window.__isExtensionContext = () => { + const url = window.location.href; + return url.startsWith('chrome-extension://') || + url.startsWith('moz-extension://') || + url === 'about:srcdoc'; +}; window.sendRuntimeMessage = async (message) => { const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9); @@ -68,6 +75,13 @@ window.onCompleted = (callback) => { // Returns stringified function that uses chrome.runtime.sendMessage return ` window.__completionCallbacks = []; +// Check if we're in an extension context by examining the URL +window.__isExtensionContext = () => { + const url = window.location.href; + return url.startsWith('chrome-extension://') || + url.startsWith('moz-extension://') || + url === 'about:srcdoc'; +}; window.sendRuntimeMessage = async (message) => { return await chrome.runtime.sendMessage({ ...message,