From f2e44ff4ab6c1250a3166092e5ffa74811413bf2 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:57:55 -0800 Subject: [PATCH] fix: use windows.create() for tx/sign approval popups instead of openPopup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit action.openPopup() is unreliable when called from the background script during an async message handler — it requires a user gesture context. tx and sign approvals are triggered programmatically by dApp RPC calls, not by user clicking the toolbar icon, so openPopup() fails silently. Use windows.create() directly for tx/sign approvals, matching the standard extension pattern (used by MetaMask and others). Site-connection approvals retain openPopup() since they can fall back to the user clicking the toolbar icon. Also updates popup window dimensions to 360x600 to match the standard popup viewport specified in README. Closes #4 --- src/background/index.js | 54 ++++++++++++----------------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/src/background/index.js b/src/background/index.js index db30f43..1060fcf 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -93,11 +93,13 @@ function resetPopupUrl() { } } -// Fallback: open approval in a separate window (used when openPopup is unavailable) +// Open approval in a separate popup window. +// This is the primary mechanism for tx/sign approvals (triggered programmatically, +// not from a user gesture) and the fallback for site-connection approvals. function openApprovalWindow(id) { const popupUrl = runtime.getURL("src/popup/index.html?approval=" + id); - const popupWidth = 400; - const popupHeight = 500; + const popupWidth = 360; + const popupHeight = 600; windowsApi.getLastFocused((currentWin) => { const opts = { @@ -148,7 +150,9 @@ function requestApproval(origin, hostname) { } // Open a tx-approval popup and return a promise that resolves with txHash or error. -// Uses the toolbar popup only — no fallback window. +// Uses windows.create() directly because tx approvals are triggered programmatically +// (from a dApp RPC call), not from a user gesture, so action.openPopup() is +// unreliable in this context. function requestTxApproval(origin, hostname, txParams) { return new Promise((resolve) => { const id = crypto.randomUUID(); @@ -160,27 +164,14 @@ function requestTxApproval(origin, hostname, txParams) { type: "tx", }; - if (actionApi && typeof actionApi.setPopup === "function") { - actionApi.setPopup({ - popup: "src/popup/index.html?approval=" + id, - }); - } - if (actionApi && typeof actionApi.openPopup === "function") { - try { - const result = actionApi.openPopup(); - if (result && typeof result.catch === "function") { - result.catch(() => {}); - } - } catch { - // openPopup unsupported — user clicks toolbar icon - } - } + openApprovalWindow(id); }); } // Open a sign-approval popup and return a promise that resolves with { signature } or { error }. -// Uses the toolbar popup only — no fallback window. If openPopup() fails the -// popup URL is still set, so the user can click the toolbar icon to respond. +// Uses windows.create() directly because sign approvals are triggered programmatically +// (from a dApp RPC call), not from a user gesture, so action.openPopup() is +// unreliable in this context. function requestSignApproval(origin, hostname, signParams) { return new Promise((resolve) => { const id = crypto.randomUUID(); @@ -192,27 +183,14 @@ function requestSignApproval(origin, hostname, signParams) { type: "sign", }; - if (actionApi && typeof actionApi.setPopup === "function") { - actionApi.setPopup({ - popup: "src/popup/index.html?approval=" + id, - }); - } - if (actionApi && typeof actionApi.openPopup === "function") { - try { - const result = actionApi.openPopup(); - if (result && typeof result.catch === "function") { - result.catch(() => {}); - } - } catch { - // openPopup unsupported — user clicks toolbar icon - } - } + openApprovalWindow(id); }); } // Detect when an approval popup (browser-action) closes without a response. -// TX and sign approvals are NOT auto-rejected on disconnect because toolbar -// popups naturally close on focus loss and the user can reopen them. +// TX and sign approvals now use windows.create() and are handled by the +// windowsApi.onRemoved listener below, but we still handle site-connection +// approval disconnects here. runtime.onConnect.addListener((port) => { if (port.name.startsWith("approval:")) { const id = port.name.split(":")[1];