From f214abaf1b43ece99a332d5fda20ab90ce557960 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sun, 4 Oct 2026 04:21:58 +0200 Subject: [PATCH] Theseus: page dialogs wait while a wallet approval or the unlock prompt is up A dapp could request a signature and then call alert(): the page's sheet was raised over the approval, and closing it focused the page again while the approval's buttons were already armed, so a double-click on the sheet's OK landed on Approve. Page dialogs and Theseus's own sheets are now held until the approval or unlock prompt is answered, and nothing hands focus to the page while one is open. --- main.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/main.js b/main.js index 1b1b1b89..ecbe9833 100644 --- a/main.js +++ b/main.js @@ -5906,6 +5906,13 @@ function pumpJsDialog() { const i = jsDialogQueue.findIndex((q) => q.tabId == null || q.tabId === activeId); emitTabs(); if (i < 0) return; + // Nothing goes over a wallet approval or the unlock prompt. A dapp could + // ask for a signature and then alert(): its sheet covered the approval, + // and closing it handed focus back to the page while the approval's + // buttons were already armed, so a double-click on "OK" landed on + // "Approve". The dialog waits (its page is blocked anyway) until the + // approval or prompt is answered. + if (approvalCurrent || unlockCurrent) return; const next = jsDialogQueue.splice(i, 1)[0]; jsDialogCurrent = next; overlayReady(jsDialogPop).then(() => { @@ -5951,7 +5958,11 @@ function finishJsDialog(ok, value) { jsDialogCurrent = null; try { if (jsDialogPop) jsDialogPop.setVisible(false); } catch {} jsDialogReply(cur, ok, value); - try { const t = cur.tabId != null ? tabById(cur.tabId) : null; if (t && t.id === activeId) t.view.webContents.focus(); } catch {} + // Never hand focus to the page while an approval or the unlock prompt is + // up: keystrokes meant for the prompt would go to the page. + if (!approvalCurrent && !unlockCurrent) { + try { const t = cur.tabId != null ? tabById(cur.tabId) : null; if (t && t.id === activeId) t.view.webContents.focus(); } catch {} + } pumpJsDialog(); } // Theseus's own prompts — install this app / extension, remove, restart — @@ -6083,6 +6094,7 @@ ipcMain.handle("approval-pick", (e, reqId, action, checked, extra) => { result += "+" + cur.req.select.id + "=" + extra; } cur.resolve(result); + pumpJsDialog(); // a page dialog held back by the approval can show now pumpApproval(); return true; }); @@ -6893,6 +6905,7 @@ function finishUnlock(result) { try { unlockPop?.setVisible(false); } catch {} cur?.resolve(result); pumpUnlock(); // queued requests resolve at once if the vault is now open + pumpJsDialog(); } ipcMain.handle("unlock-submit", async (e, reqId, mode, value) => { if (!unlockPop || e.sender !== unlockPop.webContents) return { ok: false, error: "denied" };