From fc48a0c39af6b820f9572047662ed0ae2025f561 Mon Sep 17 00:00:00 2001 From: winter100004 Date: Thu, 11 Jun 2026 14:39:03 +0800 Subject: [PATCH] fix(extension): stop answering pings after the extension is uninstalled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the extension is uninstalled (or reloaded), its content script keeps running in already-open pages as an orphan: the window message listener stays alive, and since the ping handler touches no extension API, it kept answering โ€” so the SDK's isAvailable() reported true until the page was refreshed (and an actual pick then failed with a misleading "cancelled"). Guard the message listener: when the extension context is invalidated (browser.runtime.id gone, or touching browser.runtime throws), unhook and go silent. An unanswered ping times out in the SDK, so isAvailable() correctly reports false with no page refresh โ€” the uninstall-side twin of the install-time backfill. A re-install injects a fresh isolated world and answers instead; the dead context can no longer double-answer. Bumps the extension to 0.3.3. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../extension/entrypoints/content/index.tsx | 23 ++++++++++++++++++- packages/extension/package.json | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/extension/entrypoints/content/index.tsx b/packages/extension/entrypoints/content/index.tsx index 20440c5..257473b 100644 --- a/packages/extension/entrypoints/content/index.tsx +++ b/packages/extension/entrypoints/content/index.tsx @@ -158,7 +158,28 @@ export default defineContentScript({ } } - window.addEventListener("message", (event) => { + // After the extension is uninstalled (or reloaded), this injected script keeps + // running in already-open pages as an "orphan": its listeners stay alive but the + // extension context is invalidated (browser.runtime.id becomes undefined, and in + // some cases touching browser.runtime throws). + function extensionAlive(): boolean { + try { + return !!browser.runtime?.id + } catch { + return false + } + } + + window.addEventListener("message", function onMessage(event) { + // Orphan guard: once the extension is gone, go silent and unhook. An unanswered + // ping times out in the SDK, so isAvailable() correctly reports false with no + // page refresh โ€” the uninstall-side twin of the install-time backfill + // (background.ts). A re-install injects into a fresh isolated world and answers + // instead; unhooking here just stops this dead context from double-answering. + if (!extensionAlive()) { + window.removeEventListener("message", onMessage) + return + } // Only accept messages from this same window and origin (PROTOCOL.md ยง2). if (event.source !== window) return if (event.origin !== window.origin) return diff --git a/packages/extension/package.json b/packages/extension/package.json index 4eb1a01..f97c978 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -1,6 +1,6 @@ { "name": "@openpicker/extension", - "version": "0.3.2", + "version": "0.3.3", "private": true, "type": "module", "scripts": {