From 1bbf0f84e7c41c778102105de167fd088d577f71 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Wed, 7 Aug 2024 10:06:30 -0700 Subject: [PATCH] initial request queue logic Making progress towards: https://github.com/damus-io/damus/issues/2369 Signed-off-by: William Casarin --- Damoose/Damoose.entitlements | 2 +- Damoose/Resources/background.js | 46 ++++++++++++++-- Damoose/Resources/content.js | 98 +++++++++++++++++++++++++++++++-- Damoose/Resources/manifest.json | 28 ++++++++-- Damoose/Resources/nostr.js | 70 +++++++++++++++++++++++ Damoose/Resources/popup.css | 3 +- Damoose/Resources/popup.html | 5 +- Damoose/Resources/popup.js | 41 +++++++++++++- 8 files changed, 273 insertions(+), 20 deletions(-) create mode 100644 Damoose/Resources/nostr.js diff --git a/Damoose/Damoose.entitlements b/Damoose/Damoose.entitlements index ee95ab7e5..0ab12df3b 100644 --- a/Damoose/Damoose.entitlements +++ b/Damoose/Damoose.entitlements @@ -5,6 +5,6 @@ com.apple.security.app-sandbox com.apple.security.network.client - + diff --git a/Damoose/Resources/background.js b/Damoose/Resources/background.js index 56cb3fcaa..90a741001 100644 --- a/Damoose/Resources/background.js +++ b/Damoose/Resources/background.js @@ -1,6 +1,44 @@ -browser.runtime.onMessage.addListener((request, sender, sendResponse) => { - console.log("Received request: ", request); +let damoose = { + queue: {}, + reqids: 0, +} - if (request.greeting === "hello") - return Promise.resolve({ farewell: "goodbye" }); +function queue_request(d, kind, host, sendResponse) { + const id = ++d.reqids + if (d.queue[host] == null) + d.queue[host] = [] + d.queue[host].push({kind, id, sendResponse}) +} + +browser.runtime.onMessage.addListener((message, _sender, sendResponse) => { + switch (message.kind) { + // window.nostr + case 'getPubKey': + case 'signEvent': + case 'nip04.encrypt': + case 'nip04.decrypt': + case 'getRelays': + queue_request(damoose, message.kind, message.host, sendResponse); + return true; + + // + // extension <-> page comms + // + // *requests* + // The auth popup asks for the latest requests that we have + // queued. This simply returns it to the tab/page in question. + // + case 'requests': + const payload = { + requests: damoose.queue[message.host] || [], + host: message.host + }; + return Promise.resolve(payload) + + default: + return Promise.resolve(); + } }); + + + diff --git a/Damoose/Resources/content.js b/Damoose/Resources/content.js index d4c3f2b7d..308f1dfec 100644 --- a/Damoose/Resources/content.js +++ b/Damoose/Resources/content.js @@ -1,7 +1,93 @@ -browser.runtime.sendMessage({ greeting: "hello" }).then((response) => { - console.log("Received response: ", response); -}); +let script = document.createElement('script'); +script.setAttribute('src', browser.runtime.getURL('nostr.js')); +document.body.appendChild(script); +console.log("Added Damoose the nostr helper to the page.") + + +function test_iframe() { + const iframe = document.createElement('iframe'); + // Create an iframe for the secure popup + iframe.src = browser.runtime.getURL('popup.html'); // Load the secure popup HTML + iframe.style.position = 'fixed'; + iframe.style.bottom = '0'; // Align the iframe to the bottom of the screen + iframe.style.left = '0'; + iframe.style.width = '100%'; // Make the iframe span the entire width of the screen + iframe.style.height = '50%'; // Make the iframe cover the bottom half of the screen + iframe.style.borderTop = '2px solid black'; + iframe.style.zIndex = '10000'; // Ensure it's on top of other elements + iframe.style.display = 'none'; // Initially hidden + iframe.style.backgroundColor = 'white'; // Opaque background + + // Add sandbox attributes to prevent host page access + iframe.sandbox = 'allow-scripts allow-same-origin'; + + // Append the iframe to the body + document.body.appendChild(iframe); + + // Function to show the iframe popup + function toggle_popup() { + if (iframe.style.display === 'block') + iframe.style.display = 'none'; + else + iframe.style.display = 'block'; + } + + // Example trigger + document.addEventListener('keydown', function(event) { + if (event.key === 'o') { // Press 'o' to show the iframe popup + toggle_popup(); + } + }); + + window.addEventListener('message', async message => { + const validEvents = [ + // window.nostr stuff + 'getPubKey', + 'signEvent', + 'getRelays', + 'nip04.encrypt', + 'nip04.decrypt', + + // plugin stuff + 'popup_initialized', + ]; + let { kind, reqId, payload } = message.data; + if (!validEvents.includes(kind)) return; + + if (kind === 'popup_initialized') { + // get all the queued requests for this host from the extension + const requests = await browser.runtime.sendMessage({ + kind: 'requests', + host: window.location.host, + }); + + if (browser.runtime.lastError) + console.log(browser.runtime.lastError) + + // send initial requests + iframe.contentWindow.postMessage({ + kind: 'requests', + payload: requests, + }, '*') + } else { + // window.nostr stuff + const host = window.location.host + const msg = {kind, payload, host} + const result = await browser.runtime.sendMessage(msg) + + iframe.contentWindow.postMessage({ + kind: 'request', + payload: msg, + }, '*') + + console.log(result); + + kind = `return_${kind}`; + + window.postMessage({ kind, reqId, payload: result }, '*'); + } + }); +} + +test_iframe() -browser.runtime.onMessage.addListener((request, sender, sendResponse) => { - console.log("Received request: ", request); -}); diff --git a/Damoose/Resources/manifest.json b/Damoose/Resources/manifest.json index 2af3f2b13..dbc04eab8 100644 --- a/Damoose/Resources/manifest.json +++ b/Damoose/Resources/manifest.json @@ -2,8 +2,8 @@ "manifest_version": 3, "default_locale": "en", - "name": "__MSG_extension_name__", - "description": "__MSG_extension_description__", + "name": "Damoose", + "description": "The Damus nostr safari companion. Protect your key and create web highlights.", "version": "1.0", "icons": { @@ -16,18 +16,36 @@ "background": { "scripts": [ "background.js" ], - "type": "module" + "type": "module", + "run_at": "document_end" }, "content_scripts": [{ "js": [ "content.js" ], - "matches": [ "*://example.com/*" ] + "matches": [ "*://*/*" ] }], + "web_accessible_resources": [ + { + "resources": [ + "nostr.js", + "popup.js", + "popup.html" + ], + "matches": [ + "" + ] + } + ], + + "content_security_policy": { + "extension_pages": "script-src 'self'" + }, + "action": { "default_popup": "popup.html", "default_icon": "images/toolbar-icon.svg" }, - "permissions": [ ] + "permissions": [ "nativeMessaging" ] } diff --git a/Damoose/Resources/nostr.js b/Damoose/Resources/nostr.js new file mode 100644 index 000000000..f071ded9d --- /dev/null +++ b/Damoose/Resources/nostr.js @@ -0,0 +1,70 @@ +// +// nostr.js +// +// Code from nostore +// + + +window.nostr = { + requests: {}, + + async getPublicKey() { + return await this.broadcast('getPubKey'); + }, + + async signEvent(event) { + return await this.broadcast('signEvent', event); + }, + + async getRelays() { + return await this.broadcast('getRelays'); + }, + + // This is here for Alby comatibility. This is not part of the NIP-07 standard. + // I have found at least one site, nostr.band, which expects it to be present. + async enable() { + return { enabled: true }; + }, + + broadcast(kind, payload) { + let reqId = Math.random().toString(); + return new Promise((resolve, _reject) => { + this.requests[reqId] = resolve; + window.postMessage({ kind, reqId, payload }, '*'); + }); + }, + + nip04: { + async encrypt(pubKey, plainText) { + return await window.nostr.broadcast('nip04.encrypt', { + pubKey, + plainText, + }); + }, + + async decrypt(pubKey, cipherText) { + return await window.nostr.broadcast('nip04.decrypt', { + pubKey, + cipherText, + }); + }, + }, +}; + + +window.addEventListener('message', message => { + const validEvents = [ + 'getPubKey', + 'signEvent', + 'getRelays', + 'nip04.encrypt', + 'nip04.decrypt', + ].map(e => `return_${e}`); + let { kind, reqId, payload } = message.data; + + if (!validEvents.includes(kind)) return; + + window.nostr.requests[reqId]?.(payload); + delete window.nostr.requests[reqId]; +}); + diff --git a/Damoose/Resources/popup.css b/Damoose/Resources/popup.css index 5b149b9e3..4ec624481 100644 --- a/Damoose/Resources/popup.css +++ b/Damoose/Resources/popup.css @@ -3,11 +3,10 @@ } body { - width: 100px; + /* width: 100px;*/ padding: 10px; font-family: system-ui; - text-align: center; } @media (prefers-color-scheme: dark) { diff --git a/Damoose/Resources/popup.html b/Damoose/Resources/popup.html index ac5231933..3519214b7 100644 --- a/Damoose/Resources/popup.html +++ b/Damoose/Resources/popup.html @@ -6,6 +6,9 @@ - Hello World! + THE DAMOOSE IS HERE! +
+ +
diff --git a/Damoose/Resources/popup.js b/Damoose/Resources/popup.js index 5c1aa869c..7d5cc9138 100644 --- a/Damoose/Resources/popup.js +++ b/Damoose/Resources/popup.js @@ -1 +1,40 @@ -console.log("Hello World!", browser); + +let requests = [] + +window.addEventListener('message', message => { + const { kind, reqId, payload } = message.data; + if (kind === 'requests') { + requests = requests.concat(payload.requests) + } else if (kind === 'request') { + requests.push(payload) + } + + update_view(payload.host, requests) +}); + +function update_view(host, rs) { + const reqs = document.getElementById("requests") + const req_lis = rs.map(r => `
  • ${r.kind}
  • `).join("\n") + + reqs.innerHTML = ` +
    ${host}
    is requesting: +
      + ${req_lis} +
    + ` +} + +// let the page know the popup iframe is ready to receive nip07 requests for +// approval/disapproval +function popup_initialized() { + window.parent.postMessage({ kind: "popup_initialized" }, '*'); +} + +function resolve_request(reqId, kind, payload) { + window.parent.postMessage({ kind, reqId, payload }, '*'); +} + +popup_initialized() + + +