import { iswindow } from ".."; import { SCRAMJETCLIENT } from "../../symbols"; import { ScramjetClient } from "../client"; import { POLLUTANT } from "../shared/realm"; export default function (client: ScramjetClient) { if (iswindow) client.Proxy("window.postMessage", { apply(ctx) { // so we need to send the real origin here, since the recieving window can't possibly know. // except, remember that this code is being ran in a different realm than the invoker, so if we ask our `client` it may give us the wrong origin // if we were given any object that came from the real realm we can use that to get the real origin // and this works in every case EXCEPT for the fact that all three arguments can be strings which are copied instead of cloned // so we have to use `$setrealm` which will pollute this with an object from the real realm let pollutant; if (typeof ctx.args[0] === "object" && ctx.args[0] !== null) { pollutant = ctx.args[0]; // try to use the first object we can find because it's more reliable } else if (typeof ctx.args[2] === "object" && ctx.args[2] !== null) { pollutant = ctx.args[2]; // next try to use transfer } else if ( POLLUTANT in ctx.this && typeof ctx.this[POLLUTANT] === "object" && ctx.this[POLLUTANT] !== null ) { pollutant = ctx.this[POLLUTANT]; // lastly try to use the object from $setrealm } else { pollutant = {}; // give up } // and now we can steal Function from the caller's realm const { constructor: { constructor: Function }, } = pollutant; // invoking stolen function will give us the caller's globalThis, remember scramjet has already proxied it!!! const callerGlobalThisProxied: Self = Function("return globalThis")(); const callerClient = callerGlobalThisProxied[SCRAMJETCLIENT]; // this WOULD be enough but the source argument of MessageEvent has to return the caller's window // and if we just call it normally it would be coming from here, which WILL NOT BE THE CALLER'S because the accessor is from the parent // so with the stolen function we wrap postmessage so the source will truly be the caller's window (remember that function is scramjet's!!!) const wrappedPostMessage = Function("...args", "this(...args)"); ctx.args[0] = { $scramjet$messagetype: "window", $scramjet$origin: callerClient.url.origin, $scramjet$data: ctx.args[0], }; // * origin because obviously if (typeof ctx.args[1] === "string") ctx.args[1] = "*"; if (typeof ctx.args[1] === "object") ctx.args[1].targetOrigin = "*"; ctx.return(wrappedPostMessage.call(ctx.fn, ...ctx.args)); }, }); const toproxy = ["MessagePort.prototype.postMessage"]; if (self.Worker) toproxy.push("Worker.prototype.postMessage"); if (!iswindow) toproxy.push("self.postMessage"); // only do the generic version if we're in a worker client.Proxy(toproxy, { apply(ctx) { // origin/source doesn't need to be preserved - it's null in the message event ctx.args[0] = { $scramjet$messagetype: "worker", $scramjet$data: ctx.args[0], }; }, }); }