Potidaea's "Open in wallet" is <a href="wiz://…" target="_blank" rel="noopener noreferrer">, a shape many dapps use. A new-window wiz:// link goes through the window-open handler, which credits a pairing to the page only through the request's referrer, so it can tell the page's own link from one in a frame. noreferrer strips the referrer, so the link was dropped and the wallet never heard of it. The inject preload, which runs in a tab's top document only, now hands a trusted click on a wiz:// link straight to main, and main accepts it only from the tab's main frame before routing it like any pairing link. Links inside iframes still go the old way and are still refused without a referrer.
65 lines
3.3 KiB
JavaScript
65 lines
3.3 KiB
JavaScript
// Session-wide preload that runs every page-inject add-on's bridge script in
|
|
// the isolated world of tabs whose URL matches the add-on's declared origin
|
|
// patterns. Registered via session.defaultSession.setPreloads in main.js
|
|
// alongside bcnr-preload.js.
|
|
//
|
|
// The decision of WHICH scripts apply is made in main against the sender's
|
|
// committed URL, not against anything the page can influence. Each script
|
|
// gets a `theseus` object scoped to its add-on id:
|
|
// theseus.contextBridge — expose an API into the page's main world
|
|
// theseus.invoke(msg, payload) — call the add-on's onMessage(msg) handler
|
|
// theseus.origin — the page origin main will show the user
|
|
// theseus.evalInPage(code) — run code in the page's main world, resolve
|
|
// its value (cookie-consent rules need it)
|
|
// plus a `require` that only resolves "electron" so scripts written in the
|
|
// ordinary preload idiom keep working.
|
|
const { contextBridge, ipcRenderer, webFrame } = require("electron");
|
|
|
|
// Web tabs run preloads in iframes too (nodeIntegrationInSubFrames, for the
|
|
// password hooks). Add-on page scripts (wallet providers, consent rules)
|
|
// stay top-frame only, as they always were: main decides them from the
|
|
// tab's URL, not the frame's, and must never put them into third-party frames.
|
|
if (window.top !== window) return;
|
|
|
|
let injections = [];
|
|
try { injections = ipcRenderer.sendSync("addon-inject-scripts", location.href) || []; }
|
|
catch (e) { console.warn("[theseus] add-on inject query failed:", e?.message || e); }
|
|
for (const inj of injections) {
|
|
const id = String(inj.id);
|
|
const theseus = Object.freeze({
|
|
id,
|
|
origin: inj.origin,
|
|
contextBridge,
|
|
invoke: (msg, payload) => ipcRenderer.invoke("addon-page-msg", id, String(msg), payload),
|
|
evalInPage: (code) => webFrame.executeJavaScript(String(code)),
|
|
});
|
|
const scopedRequire = (name) => {
|
|
if (name === "electron") return { contextBridge };
|
|
throw new Error(`addon inject scripts may only require("electron") — got ${name}`);
|
|
};
|
|
try {
|
|
new Function("theseus", "require", inj.source)(theseus, scopedRequire);
|
|
} catch (e) {
|
|
console.warn(`[theseus] add-on "${id}" page-inject failed:`, e?.message || e);
|
|
}
|
|
}
|
|
|
|
// wiz:// links (WizardConnect pairing, "Open in wallet"). A same-tab click is
|
|
// caught by main on will-navigate; one that opens a new window
|
|
// (target="_blank") reaches main's window-open handler, which can tell the
|
|
// page's own link from a frame's only by the referrer, and rel="noreferrer",
|
|
// common on dapps, strips it. So those clicks were dropped and the wallet
|
|
// never saw them. A click the user really made on such a link in this, the
|
|
// top document, goes straight to main instead; main checks it came from the
|
|
// tab's main frame and routes it like any other pairing link.
|
|
document.addEventListener("click", (e) => {
|
|
if (!e.isTrusted || e.button !== 0) return;
|
|
// composedPath reaches into shadow roots, where web-component buttons live.
|
|
const a = (typeof e.composedPath === "function" ? e.composedPath() : [e.target])
|
|
.find((n) => n && n.tagName === "A" && n.hasAttribute && n.hasAttribute("href"));
|
|
if (!a) return;
|
|
const href = String(a.getAttribute("href") || "").trim();
|
|
if (!/^wiz:/i.test(href) || href.length > 4096) return;
|
|
e.preventDefault();
|
|
ipcRenderer.send("wiz-link", href);
|
|
}, true);
|