From 59efe3dd8509fb364d6300a7d1d40a1f12c6804c Mon Sep 17 00:00:00 2001 From: grypez <143971198+grypez@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:19:38 -0500 Subject: [PATCH 1/3] pure background <--> kernel doesn't seem to work --- packages/extension/src/background.ts | 13 +++++++++++++ packages/extension/src/message.ts | 5 ++++- packages/extension/src/offscreen.ts | 8 ++++++++ packages/extension/src/poc-iframe.html | 9 +++++++++ packages/extension/src/poc-iframe.ts | 11 +++++++++++ 5 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 packages/extension/src/poc-iframe.html create mode 100644 packages/extension/src/poc-iframe.ts diff --git a/packages/extension/src/background.ts b/packages/extension/src/background.ts index 82d3a4adb..136041c1b 100644 --- a/packages/extension/src/background.ts +++ b/packages/extension/src/background.ts @@ -7,6 +7,13 @@ import { Command } from './message.js'; const streams = makeBackgroundOffscreenStreamPair(); +const { + port1: portBackgroundKernel, + port2: portKernelBackground, +} = new MessageChannel(); + +portBackgroundKernel.onmessage = console.log; + // globalThis.kernel will exist due to dev-console.js in background-trusted-prelude.js Object.defineProperties(globalThis.kernel, { capTpCall: { @@ -32,6 +39,11 @@ Object.defineProperties(globalThis.kernel, { value: async (message: CommandMessage) => await streams.writer.next(message), }, + helloThere: { + value: async () => { + await streams.writer.next({ type: Command.HelloThere, data: portKernelBackground }); + } + } }); harden(globalThis.kernel); @@ -54,6 +66,7 @@ async function handleMessages(): Promise { case Command.CapTpCall: case Command.CapTpInit: case Command.Ping: + case Command.HelloThere: console.log(message.data); break; default: diff --git a/packages/extension/src/message.ts b/packages/extension/src/message.ts index 96acc6a3a..0c9c825c5 100644 --- a/packages/extension/src/message.ts +++ b/packages/extension/src/message.ts @@ -5,6 +5,7 @@ export type MessageId = string; type DataObject = | Primitive + | Transferable | Promise | DataObject[] | { [key: string]: DataObject }; @@ -29,6 +30,7 @@ export enum Command { CapTpInit = 'makeCapTp', Evaluate = 'evaluate', Ping = 'ping', + HelloThere = 'helloThere' } export type CapTpPayload = { @@ -40,7 +42,8 @@ export type CommandMessage = | CommandLike | CommandLike | CommandLike - | CommandLike; + | CommandLike + | CommandLike; export type ExtensionMessage = CommandMessage; export type IframeMessage = CommandMessage; diff --git a/packages/extension/src/offscreen.ts b/packages/extension/src/offscreen.ts index 4f828733f..aaec31e34 100644 --- a/packages/extension/src/offscreen.ts +++ b/packages/extension/src/offscreen.ts @@ -1,7 +1,9 @@ +import { createWindow } from '@metamask/snaps-utils'; import { makeOffscreenBackgroundStreamPair } from './extension-stream-pairs.js'; import { IframeManager } from './iframe-manager.js'; import type { CommandMessage } from './message.js'; import { Command } from './message.js'; +import { initializeMessageChannel } from '../../streams/dist/message-channel.cjs'; main().catch(console.error); @@ -37,6 +39,12 @@ async function main(): Promise { case Command.Ping: await reply(Command.Ping, 'pong'); break; + case Command.HelloThere: + const iframe = await createWindow('poc-iframe.html', 'poc-iframe'); + console.log('offscreen received message', message); + const initMessage = { type: 'INIT_MESSAGE_CHANNEL' }; + iframe.postMessage(initMessage, '*', [message.data]); + break; default: console.error( // @ts-expect-error The type of `message` is `never`, but this could happen at runtime. diff --git a/packages/extension/src/poc-iframe.html b/packages/extension/src/poc-iframe.html new file mode 100644 index 000000000..24f9f843d --- /dev/null +++ b/packages/extension/src/poc-iframe.html @@ -0,0 +1,9 @@ + + + + + Poc Iframe + + + + diff --git a/packages/extension/src/poc-iframe.ts b/packages/extension/src/poc-iframe.ts new file mode 100644 index 000000000..3f7abec22 --- /dev/null +++ b/packages/extension/src/poc-iframe.ts @@ -0,0 +1,11 @@ +import { receiveMessagePort, } from '@ocap/streams'; + +main().catch(console.error); + +/** + * The main function for the iframe. + */ +async function main(): Promise { + const port = await receiveMessagePort(); + port.postMessage('the angel from my nightmare...'); +} \ No newline at end of file From cf3407c2a441bff49988b6c099c64e0d4ceb757c Mon Sep 17 00:00:00 2001 From: grypez <143971198+grypez@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:40:27 -0500 Subject: [PATCH 2/3] poc iframe-iframe introduction --- packages/extension/src/background.ts | 9 +--- packages/extension/src/manifest.json | 2 +- packages/extension/src/message.ts | 2 +- packages/extension/src/offscreen.ts | 11 ++--- packages/extension/src/poc-constants.ts | 3 ++ packages/extension/src/poc-iframe-angel.html | 9 ++++ packages/extension/src/poc-iframe-angel.ts | 17 +++++++ packages/extension/src/poc-iframe-shadow.html | 9 ++++ packages/extension/src/poc-iframe-shadow.ts | 16 +++++++ packages/extension/src/poc-iframe.html | 9 ---- packages/extension/src/poc-iframe.ts | 11 ----- packages/extension/vite.config.ts | 2 + packages/streams/src/index.test.ts | 1 + packages/streams/src/index.ts | 1 + packages/streams/src/message-channel.ts | 44 +++++++++++++++++++ 15 files changed, 111 insertions(+), 35 deletions(-) create mode 100644 packages/extension/src/poc-constants.ts create mode 100644 packages/extension/src/poc-iframe-angel.html create mode 100644 packages/extension/src/poc-iframe-angel.ts create mode 100644 packages/extension/src/poc-iframe-shadow.html create mode 100644 packages/extension/src/poc-iframe-shadow.ts delete mode 100644 packages/extension/src/poc-iframe.html delete mode 100644 packages/extension/src/poc-iframe.ts diff --git a/packages/extension/src/background.ts b/packages/extension/src/background.ts index 136041c1b..0ff526dcd 100644 --- a/packages/extension/src/background.ts +++ b/packages/extension/src/background.ts @@ -7,13 +7,6 @@ import { Command } from './message.js'; const streams = makeBackgroundOffscreenStreamPair(); -const { - port1: portBackgroundKernel, - port2: portKernelBackground, -} = new MessageChannel(); - -portBackgroundKernel.onmessage = console.log; - // globalThis.kernel will exist due to dev-console.js in background-trusted-prelude.js Object.defineProperties(globalThis.kernel, { capTpCall: { @@ -41,7 +34,7 @@ Object.defineProperties(globalThis.kernel, { }, helloThere: { value: async () => { - await streams.writer.next({ type: Command.HelloThere, data: portKernelBackground }); + await streams.writer.next({ type: Command.HelloThere, data: null }); } } }); diff --git a/packages/extension/src/manifest.json b/packages/extension/src/manifest.json index 8d1d99109..9ddb680b6 100644 --- a/packages/extension/src/manifest.json +++ b/packages/extension/src/manifest.json @@ -10,7 +10,7 @@ "action": {}, "permissions": ["offscreen"], "sandbox": { - "pages": ["iframe.html"] + "pages": ["iframe.html", "poc-iframe-angel.html", "poc-iframe-shadow.html"] }, "content_security_policy": { "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'none'; frame-ancestors 'none';", diff --git a/packages/extension/src/message.ts b/packages/extension/src/message.ts index 0c9c825c5..61d637c68 100644 --- a/packages/extension/src/message.ts +++ b/packages/extension/src/message.ts @@ -43,7 +43,7 @@ export type CommandMessage = | CommandLike | CommandLike | CommandLike - | CommandLike; + | CommandLike; export type ExtensionMessage = CommandMessage; export type IframeMessage = CommandMessage; diff --git a/packages/extension/src/offscreen.ts b/packages/extension/src/offscreen.ts index aaec31e34..b5d97e7d7 100644 --- a/packages/extension/src/offscreen.ts +++ b/packages/extension/src/offscreen.ts @@ -3,7 +3,8 @@ import { makeOffscreenBackgroundStreamPair } from './extension-stream-pairs.js'; import { IframeManager } from './iframe-manager.js'; import type { CommandMessage } from './message.js'; import { Command } from './message.js'; -import { initializeMessageChannel } from '../../streams/dist/message-channel.cjs'; +import { performIntroduction } from '@ocap/streams'; +import { HELLO_THERE } from './poc-constants.js'; main().catch(console.error); @@ -40,10 +41,10 @@ async function main(): Promise { await reply(Command.Ping, 'pong'); break; case Command.HelloThere: - const iframe = await createWindow('poc-iframe.html', 'poc-iframe'); - console.log('offscreen received message', message); - const initMessage = { type: 'INIT_MESSAGE_CHANNEL' }; - iframe.postMessage(initMessage, '*', [message.data]); + console.log(HELLO_THERE); + await performIntroduction( + await createWindow('poc-iframe-angel.html', 'poc-iframe-angel'), + await createWindow('poc-iframe-shadow.html', 'poc-iframe-shadow')); break; default: console.error( diff --git a/packages/extension/src/poc-constants.ts b/packages/extension/src/poc-constants.ts new file mode 100644 index 000000000..c5db32832 --- /dev/null +++ b/packages/extension/src/poc-constants.ts @@ -0,0 +1,3 @@ +export const HELLO_THERE = 'Hello there...'; +export const THE_ANGEL = 'The angel from my nightmare...'; +export const THE_SHADOW = 'The shadow in the background of the morgue.'; diff --git a/packages/extension/src/poc-iframe-angel.html b/packages/extension/src/poc-iframe-angel.html new file mode 100644 index 000000000..5ca47ee81 --- /dev/null +++ b/packages/extension/src/poc-iframe-angel.html @@ -0,0 +1,9 @@ + + + + + Poc Iframe Angel + + + + diff --git a/packages/extension/src/poc-iframe-angel.ts b/packages/extension/src/poc-iframe-angel.ts new file mode 100644 index 000000000..b2d95c390 --- /dev/null +++ b/packages/extension/src/poc-iframe-angel.ts @@ -0,0 +1,17 @@ +import { receiveMessagePort } from '@ocap/streams'; +import { delay } from '@ocap/test-utils'; +import { THE_ANGEL } from './poc-constants.js'; + +main().catch(console.error); + +/** + * The main function for the iframe. + */ +async function main(): Promise { + const port = await receiveMessagePort(); + + await delay(100); + + console.log(THE_ANGEL); + port.postMessage(THE_ANGEL); +} diff --git a/packages/extension/src/poc-iframe-shadow.html b/packages/extension/src/poc-iframe-shadow.html new file mode 100644 index 000000000..063b86d2a --- /dev/null +++ b/packages/extension/src/poc-iframe-shadow.html @@ -0,0 +1,9 @@ + + + + + Poc Iframe Shadow + + + + diff --git a/packages/extension/src/poc-iframe-shadow.ts b/packages/extension/src/poc-iframe-shadow.ts new file mode 100644 index 000000000..9cac29c79 --- /dev/null +++ b/packages/extension/src/poc-iframe-shadow.ts @@ -0,0 +1,16 @@ +import { receiveMessagePort } from '@ocap/streams'; +import { THE_ANGEL, THE_SHADOW } from './poc-constants.js'; + +main().catch(console.error); + +/** + * The main function for the iframe. + */ +async function main(): Promise { + (await receiveMessagePort()).onmessage = (message: MessageEvent): void => { + if (message.data !== THE_ANGEL) { + return; + } + console.log(THE_SHADOW); + } +} diff --git a/packages/extension/src/poc-iframe.html b/packages/extension/src/poc-iframe.html deleted file mode 100644 index 24f9f843d..000000000 --- a/packages/extension/src/poc-iframe.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - Poc Iframe - - - - diff --git a/packages/extension/src/poc-iframe.ts b/packages/extension/src/poc-iframe.ts deleted file mode 100644 index 3f7abec22..000000000 --- a/packages/extension/src/poc-iframe.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { receiveMessagePort, } from '@ocap/streams'; - -main().catch(console.error); - -/** - * The main function for the iframe. - */ -async function main(): Promise { - const port = await receiveMessagePort(); - port.postMessage('the angel from my nightmare...'); -} \ No newline at end of file diff --git a/packages/extension/vite.config.ts b/packages/extension/vite.config.ts index 09d5a97d5..60de62854 100644 --- a/packages/extension/vite.config.ts +++ b/packages/extension/vite.config.ts @@ -41,6 +41,8 @@ export default defineConfig(({ mode }) => ({ background: path.resolve(projectRoot, 'background.ts'), offscreen: path.resolve(projectRoot, 'offscreen.html'), iframe: path.resolve(projectRoot, 'iframe.html'), + iframe1: path.resolve(projectRoot, 'poc-iframe-angel.html'), + iframe2: path.resolve(projectRoot, 'poc-iframe-shadow.html'), }, output: { entryFileNames: '[name].js', diff --git a/packages/streams/src/index.test.ts b/packages/streams/src/index.test.ts index 0a66aa6ee..40736dfd1 100644 --- a/packages/streams/src/index.test.ts +++ b/packages/streams/src/index.test.ts @@ -12,6 +12,7 @@ describe('index', () => { 'makeMessagePortWriter', 'initializeMessageChannel', 'receiveMessagePort', + 'performIntroduction', ]), ); }); diff --git a/packages/streams/src/index.ts b/packages/streams/src/index.ts index e7e93db4b..50dde45c1 100644 --- a/packages/streams/src/index.ts +++ b/packages/streams/src/index.ts @@ -1,6 +1,7 @@ export { initializeMessageChannel, receiveMessagePort, + performIntroduction, } from './message-channel.js'; export type { StreamPair } from './stream-pair.js'; export type { Connection } from './connection.js'; diff --git a/packages/streams/src/message-channel.ts b/packages/streams/src/message-channel.ts index 42ce3f1cd..b7929358b 100644 --- a/packages/streams/src/message-channel.ts +++ b/packages/streams/src/message-channel.ts @@ -110,3 +110,47 @@ export async function receiveMessagePort(): Promise { window.addEventListener('message', listener); return promise; } + +/** + * Expects both target windows are awaiting a message port with receiveMessagePort(); + * + * @param targetWindow1 + * @param targetWindow2 + * @returns + */ +export async function performIntroduction( + targetWindow1: Window, + targetWindow2: Window, +): Promise { + const { port1, port2 } = new MessageChannel(); + + const { promise, resolve, reject } = makePromiseKit(); + // Assigning to the `onmessage` property initializes the port's message queue. + port1.onmessage = (message: MessageEvent): void => { + if (!isAckMessage(message.data)) { + reject( + new Error( + `Received unexpected message via message port:\n${stringify( + message.data, + )}`, + ), + ); + return; + } + + resolve(port1); + }; + + const initMessage: InitializeMessage = { + type: MessageType.Initialize, + }; + targetWindow1.postMessage(initMessage, '*', [port2]); + + await promise.catch((error) => { + port1.close(); + throw error; + }) + .finally(() => (port1.onmessage = null)); + + targetWindow2.postMessage(initMessage, '*', [port1]); +} \ No newline at end of file From 99a3ae4367b59c15081ed86a5cd4046a7db111d0 Mon Sep 17 00:00:00 2001 From: grypez <143971198+grypez@users.noreply.github.com> Date: Mon, 23 Sep 2024 19:42:53 -0500 Subject: [PATCH 3/3] remove unnecessary delay --- packages/extension/src/poc-iframe-angel.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/extension/src/poc-iframe-angel.ts b/packages/extension/src/poc-iframe-angel.ts index b2d95c390..212c8fa9a 100644 --- a/packages/extension/src/poc-iframe-angel.ts +++ b/packages/extension/src/poc-iframe-angel.ts @@ -1,5 +1,4 @@ import { receiveMessagePort } from '@ocap/streams'; -import { delay } from '@ocap/test-utils'; import { THE_ANGEL } from './poc-constants.js'; main().catch(console.error); @@ -10,8 +9,6 @@ main().catch(console.error); async function main(): Promise { const port = await receiveMessagePort(); - await delay(100); - console.log(THE_ANGEL); port.postMessage(THE_ANGEL); }