prompt.js raw
1 // Shadow DOM prompt for non-smesh sites.
2 // Loaded by content-script when extension needs approval and no onPrompt handler is registered.
3
4 (function() {
5 const host = document.createElement("div");
6 const shadow = host.attachShadow({ mode: "closed" });
7 document.body.appendChild(host);
8
9 let currentResolve = null;
10
11 // Build DOM safely (no innerHTML).
12 const style = document.createElement("style");
13 style.textContent = `
14 .backdrop { position:fixed; inset:0; z-index:2147483647; background:rgba(0,0,0,.5);
15 display:flex; align-items:center; justify-content:center; font-family:system-ui }
16 .card { background:#1a1a2e; color:#e0e0e0; padding:24px; border-radius:12px;
17 max-width:380px; width:90%; box-shadow:0 8px 32px rgba(0,0,0,.4) }
18 h3 { margin:0 0 8px; font-size:16px; color:#fff }
19 p { margin:0 0 16px; font-size:14px; line-height:1.4 }
20 .origin { color:#7b68ee; font-weight:600 }
21 .kind { color:#ffa500 }
22 .buttons { display:flex; gap:8px; justify-content:flex-end }
23 button { padding:8px 20px; border:none; border-radius:6px; font-size:14px; cursor:pointer }
24 .allow { background:#4caf50; color:#fff }
25 .allow:hover { background:#45a049 }
26 .deny { background:#444; color:#ccc }
27 .deny:hover { background:#555 }`;
28 shadow.appendChild(style);
29
30 const backdrop = document.createElement("div");
31 backdrop.className = "backdrop";
32 backdrop.style.display = "none";
33
34 const card = document.createElement("div");
35 card.className = "card";
36
37 const h3 = document.createElement("h3");
38 h3.textContent = "Signing Request";
39 card.appendChild(h3);
40
41 const p = document.createElement("p");
42 const originEl = document.createElement("span");
43 originEl.className = "origin";
44 const text1 = document.createTextNode(" wants to ");
45 const kindEl = document.createElement("span");
46 kindEl.className = "kind";
47 p.appendChild(originEl);
48 p.appendChild(text1);
49 p.appendChild(kindEl);
50 card.appendChild(p);
51
52 const buttons = document.createElement("div");
53 buttons.className = "buttons";
54 const denyBtn = document.createElement("button");
55 denyBtn.className = "deny";
56 denyBtn.textContent = "Deny";
57 const allowBtn = document.createElement("button");
58 allowBtn.className = "allow";
59 allowBtn.textContent = "Allow";
60 buttons.appendChild(denyBtn);
61 buttons.appendChild(allowBtn);
62 card.appendChild(buttons);
63
64 backdrop.appendChild(card);
65 shadow.appendChild(backdrop);
66
67 function show(msg) {
68 originEl.textContent = msg.origin || location.hostname;
69 kindEl.textContent = "sign a kind " + (msg.kind || "?") + " event";
70 backdrop.style.display = "flex";
71
72 return new Promise((resolve) => {
73 currentResolve = resolve;
74 setTimeout(() => { resolve(false); hide(); }, 60000);
75 });
76 }
77
78 function hide() {
79 backdrop.style.display = "none";
80 currentResolve = null;
81 }
82
83 allowBtn.addEventListener("click", () => { if (currentResolve) { currentResolve(true); hide(); } });
84 denyBtn.addEventListener("click", () => { if (currentResolve) { currentResolve(false); hide(); } });
85
86 // Listen for PROMPT messages from content-script.
87 window.addEventListener("message", async (e) => {
88 if (e.source !== window || !e.data?.broadcast || e.data.payload?.type !== "PROMPT") return;
89 const allowed = await show(e.data.payload);
90 window.postMessage({
91 target: "smesh-signer",
92 id: e.data.payload.promptId,
93 payload: { method: "smesh.promptResponse", params: { allowed } }
94 }, "*");
95 });
96 })();
97