theseus/addon-inject-preload.js
Local Dev 67939b1493 feat(theseus/bchwallet): window.bitcoincash dapp bridge with per-origin permissions
wallet-inject.js runs in the isolated world of https://*.x pages and exposes
window.bitcoincash { isTheseus, version, network, getAddress, signAndSend,
signMessage }. Every call is routed page -> addon-page-msg -> activate()
handler -> approval overlay showing the requesting origin:
- getAddress: approval with an "always allow" checkbox; grants persist in
  api.storage.permissions and are listed/revocable under Settings.
- signAndSend / signMessage: approval on every call, never remembered.
  signMessage returns a BIP-137 recoverable signature (verified offline).
- one pending approval per origin; page-facing errors never echo balance.
Host fix: the inject IPC assigned event.returnValue twice, so pages always
got an empty script list.
2026-09-06 02:56:34 +02:00

36 lines
1.7 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
// plus a `require` that only resolves "electron" so scripts written in the
// ordinary preload idiom keep working.
const { contextBridge, ipcRenderer } = require("electron");
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),
});
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);
}
}