diff --git a/src/client/client.ts b/src/client/client.ts index a39aad3..5ceeb9a 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -18,6 +18,16 @@ import { createWrapFn } from "./shared/wrap"; import { NavigateEvent } from "./events"; import type { URLMeta } from "../shared/rewriters/url"; +type NativeStore = { + store: Record; + call: (target: string, that: any, ...args) => any; + construct: (target: string, ...args) => any; +}; +type DescriptorStore = { + store: Record; + get: (target: string, that: any) => any; + set: (target: string, that: any, value: any) => void; +}; //eslint-disable-next-line export type AnyFunction = Function; @@ -65,8 +75,8 @@ export class ScramjetClient { serviceWorker: ServiceWorkerContainer; bare: BareClientType; - descriptors: Record = {}; - natives: Record; + natives: NativeStore; + descriptors: DescriptorStore; wrapfn: (i: any, ...args: any) => any; cookieStore = new CookieStore(); @@ -120,48 +130,79 @@ export class ScramjetClient { }) ); } - this.natives = new Proxy( - {}, - { - get: (target, prop: string) => { - if (prop in target) { + this.natives = { + store: new Proxy( + {}, + { + get: (target, prop: string) => { + if (prop in target) { + return target[prop]; + } + + const split = prop.split("."); + const realProp = split.pop(); + const realTarget = split.reduce((a, b) => a?.[b], this.global); + + if (!realTarget) return; + + const original = Reflect.get(realTarget, realProp); + target[prop] = original; + return target[prop]; - } + }, + } + ), + construct(target: string, ...args) { + const original = this.store[target]; + if (!original) return; - const split = prop.split("."); - const realProp = split.pop(); - const realTarget = split.reduce((a, b) => a?.[b], this.global); + return new original(...args); + }, + call(target: string, that: any, ...args) { + const original = this.store[target]; + if (!original) return; - if (!realTarget) return; + return original.call(that, ...args); + }, + }; + this.descriptors = { + store: new Proxy( + {}, + { + get: (target, prop: string) => { + if (prop in target) { + return target[prop]; + } - const original = Reflect.get(realTarget, realProp); - target[prop] = original; + const split = prop.split("."); + const realProp = split.pop(); + const realTarget = split.reduce((a, b) => a?.[b], this.global); + + if (!realTarget) return; + + const original = nativeGetOwnPropertyDescriptor( + realTarget, + realProp + ); + target[prop] = original; - return target[prop]; - }, - } - ); - this.descriptors = new Proxy( - {}, - { - get: (target, prop: string) => { - if (prop in target) { return target[prop]; - } + }, + } + ), + get(target: string, that: any) { + const original = this.store[target]; + if (!original) return; - const split = prop.split("."); - const realProp = split.pop(); - const realTarget = split.reduce((a, b) => a?.[b], this.global); + return original.get.call(that); + }, + set(target: string, that: any, value: any) { + const original = this.store[target]; + if (!original) return; - if (!realTarget) return; - - const original = nativeGetOwnPropertyDescriptor(realTarget, realProp); - target[prop] = original; - - return target[prop]; - }, - } - ); + original.set.call(that, value); + }, + }; // eslint-disable-next-line @typescript-eslint/no-this-alias const client = this; this.meta = { @@ -285,7 +326,7 @@ export class ScramjetClient { if (!target) return; const original = Reflect.get(target, prop); - this.natives[name] = original; + this.natives.store[name] = original; this.RawProxy(target, prop, handler); } @@ -412,7 +453,7 @@ export class ScramjetClient { if (!target) return; const original = nativeGetOwnPropertyDescriptor(target, prop); - this.descriptors[name] = original; + this.descriptors.store[name] = original; return this.RawTrap(target, prop, descriptor); } diff --git a/src/client/dom/element.ts b/src/client/dom/element.ts index 7bcab41..72cd663 100644 --- a/src/client/dom/element.ts +++ b/src/client/dom/element.ts @@ -127,7 +127,8 @@ export default function (client: ScramjetClient, self: typeof window) { } if ( - client.natives["Element.prototype.hasAttribute"].call( + client.natives.call( + "Element.prototype.hasAttribute", ctx.this, `scramjet-attr-${name}` ) @@ -185,7 +186,7 @@ export default function (client: ScramjetClient, self: typeof window) { // i actually need to do something with this client.Proxy("Element.prototype.setAttributeNode", { - apply(ctx) {}, + apply(_ctx) {}, }); client.Proxy("Element.prototype.setAttributeNS", { @@ -203,7 +204,8 @@ export default function (client: ScramjetClient, self: typeof window) { if (ruleList) { ctx.args[2] = ruleList.fn(value, client.meta, client.cookieStore); - client.natives["Element.prototype.setAttribute"].call( + client.natives.call( + "Element.prototype.setAttribute", ctx.this, `scramjet-attr-${ctx.args[1]}`, value @@ -216,7 +218,8 @@ export default function (client: ScramjetClient, self: typeof window) { apply(ctx) { if (ctx.args[0].startsWith("scramjet-attr")) return ctx.return(undefined); if ( - client.natives["Element.prototype.hasAttribute"].call( + client.natives.call( + "Element.prototype.hasAttribute", ctx.this, ctx.args[0] ) @@ -230,7 +233,8 @@ export default function (client: ScramjetClient, self: typeof window) { apply(ctx) { if (ctx.args[0].startsWith("scramjet-attr")) return ctx.return(false); if ( - client.natives["Element.prototype.hasAttribute"].call( + client.natives.call( + "Element.prototype.hasAttribute", ctx.this, ctx.args[0] ) @@ -245,7 +249,8 @@ export default function (client: ScramjetClient, self: typeof window) { let newval; if (ctx.this instanceof self.HTMLScriptElement) { newval = rewriteJs(value, "(anonymous script element)", client.meta); - client.natives["Element.prototype.setAttribute"].call( + client.natives.call( + "Element.prototype.setAttribute", ctx.this, "scramjet-attr-script-source-src", bytesToBase64(encoder.encode(newval)) @@ -264,9 +269,11 @@ export default function (client: ScramjetClient, self: typeof window) { }, get(ctx) { if (ctx.this instanceof self.HTMLScriptElement) { - const scriptSource = client.natives[ - "Element.prototype.getAttribute" - ].call(ctx.this, "scramjet-attr-script-source-src"); + const scriptSource = client.natives.call( + "Element.prototype.getAttribute", + ctx.this, + "scramjet-attr-script-source-src" + ); if (scriptSource) { return atob(scriptSource); @@ -358,11 +365,10 @@ export default function (client: ScramjetClient, self: typeof window) { ], { get(ctx) { - const contentwindow = - client.descriptors[ - `${ctx.this.constructor.name}.prototype.contentWindow` - ].get; - const realwin = contentwindow.apply(ctx.this); + const realwin = client.descriptors.get( + `${ctx.this.constructor.name}.prototype.contentWindow`, + ctx.this + ); if (!realwin) return realwin; if (SCRAMJETCLIENT in realwin) { diff --git a/src/client/dom/serviceworker.ts b/src/client/dom/serviceworker.ts index 39d9d23..89badee 100644 --- a/src/client/dom/serviceworker.ts +++ b/src/client/dom/serviceworker.ts @@ -67,8 +67,7 @@ export default function (client: ScramjetClient, _self: Self) { url += "&type=module"; } - const nativeSharedWorker = client.natives["SharedWorker"]; - const worker = new nativeSharedWorker(url); + const worker = client.natives.construct("SharedWorker", url); const handle = worker.port; diff --git a/src/client/shared/import.ts b/src/client/shared/import.ts index 8b23bf8..83d93d6 100644 --- a/src/client/shared/import.ts +++ b/src/client/shared/import.ts @@ -3,7 +3,7 @@ import { config } from "../../shared"; import { rewriteUrl } from "../../shared/rewriters/url"; export default function (client: ScramjetClient, self: Self) { - const Function = client.natives["Function"]; + const Function = client.natives.store["Function"]; self[config.globals.importfn] = function (base: string) { return function (url: string) { diff --git a/src/client/shared/requests/xmlhttprequest.ts b/src/client/shared/requests/xmlhttprequest.ts index c8eb384..52443f7 100644 --- a/src/client/shared/requests/xmlhttprequest.ts +++ b/src/client/shared/requests/xmlhttprequest.ts @@ -5,7 +5,7 @@ import { ScramjetClient } from "../../client"; export default function (client: ScramjetClient, self: Self) { let worker; if (self.Worker && flagEnabled("syncxhr", client.url)) { - worker = new client.natives["Worker"](config.files.sync); + worker = client.natives.construct("Worker", config.files.sync); } const ARGS = Symbol("xhr original args"); const HEADERS = Symbol("xhr headers"); @@ -44,7 +44,7 @@ export default function (client: ScramjetClient, self: Self) { const sab = new SharedArrayBuffer(1024, { maxByteLength: 2147483647 }); const view = new DataView(sab); - client.natives["Worker.prototype.postMessage"].call(worker, { + client.natives.call("Worker.prototype.postMessage", worker, { sab, args, headers: ctx.this[HEADERS], diff --git a/src/client/shared/worker.ts b/src/client/shared/worker.ts index 1cb625b..ffa6a88 100644 --- a/src/client/shared/worker.ts +++ b/src/client/shared/worker.ts @@ -1,76 +1,73 @@ -import { iswindow } from ".."; import { BareMuxConnection } from "../../shared"; import { rewriteUrl } from "../../shared"; import { ScramjetClient } from "../client"; -export default function (client: ScramjetClient, self: typeof globalThis) { - if (self.Worker) { - client.Proxy("Worker", { - construct({ args, call }) { - args[0] = rewriteUrl(args[0], client.meta) + "?dest=worker"; +export default function (client: ScramjetClient, _self: typeof globalThis) { + client.Proxy("Worker", { + construct({ args, call }) { + args[0] = rewriteUrl(args[0], client.meta) + "?dest=worker"; - if (args[1] && args[1].type === "module") { + if (args[1] && args[1].type === "module") { + args[0] += "&type=module"; + } + + const worker = call(); + const conn = new BareMuxConnection(); + + (async () => { + const port = await conn.getInnerPort(); + client.natives.call( + "Worker.prototype.postMessage", + worker, + { + $scramjet$type: "baremuxinit", + port, + }, + [port] + ); + })(); + }, + }); + + // sharedworkers can only be constructed from window + client.Proxy("SharedWorker", { + construct({ args, call }) { + args[0] = rewriteUrl(args[0], client.meta) + "?dest=worker"; + + if (args[1] && typeof args[1] === "string") + args[1] = `${client.url.origin}@${args[1]}`; + + if (args[1] && typeof args[1] === "object") { + if (args[1].type === "module") { args[0] += "&type=module"; } - const worker = call(); - const conn = new BareMuxConnection(); - - (async () => { - const port = await conn.getInnerPort(); - client.natives["Worker.prototype.postMessage"].call( - worker, - { - $scramjet$type: "baremuxinit", - port, - }, - [port] - ); - })(); - }, - }); - } - - if (iswindow) { - client.Proxy("Worklet.prototype.addModule", { - apply(ctx) { - if (ctx.args[0]) ctx.args[0] = rewriteUrl(ctx.args[0], client.meta); - }, - }); - - // sharedworkers can only be constructed from window - client.Proxy("SharedWorker", { - construct({ args, call }) { - args[0] = rewriteUrl(args[0], client.meta) + "?dest=worker"; - - if (args[1] && typeof args[1] === "string") - args[1] = `${client.url.origin}@${args[1]}`; - - if (args[1] && typeof args[1] === "object") { - if (args[1].type === "module") { - args[0] += "&type=module"; - } - - if (args[1].name) { - args[1].name = `${client.url.origin}@${args[1].name}`; - } + if (args[1].name) { + args[1].name = `${client.url.origin}@${args[1].name}`; } + } - const worker = call(); - const conn = new BareMuxConnection(); + const worker = call(); + const conn = new BareMuxConnection(); - (async () => { - const port = await conn.getInnerPort(); - client.natives["MessagePort.prototype.postMessage"].call( - worker.port, - { - $scramjet$type: "baremuxinit", - port, - }, - [port] - ); - })(); - }, - }); - } + (async () => { + const port = await conn.getInnerPort(); + client.natives.call( + "MessagePort.prototype.postMessage", + worker.port, + { + $scramjet$type: "baremuxinit", + port, + }, + [port] + ); + })(); + }, + }); + + client.Proxy("Worklet.prototype.addModule", { + apply(ctx) { + if (ctx.args[0]) ctx.args[0] = rewriteUrl(ctx.args[0], client.meta); + }, + }); }