From 6f51642afb07f697a8945f5ce85cd6f62d1ed7ff Mon Sep 17 00:00:00 2001 From: velzie Date: Sun, 25 Aug 2024 15:48:12 -0400 Subject: [PATCH] refactor window and document proxies --- src/client/client.ts | 12 ++--- src/client/document.ts | 30 +++++++++++++ src/client/dom/element.ts | 2 +- src/client/dom/open.ts | 2 +- src/client/global.ts | 55 +++++++++++++++++++++++ src/client/shared/event.ts | 2 +- src/client/shared/unproxy.ts | 4 +- src/client/shared/wrap.ts | 6 +-- src/client/window.ts | 85 ------------------------------------ 9 files changed, 99 insertions(+), 99 deletions(-) create mode 100644 src/client/document.ts create mode 100644 src/client/global.ts delete mode 100644 src/client/window.ts diff --git a/src/client/client.ts b/src/client/client.ts index e79ac58..2c3e361 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,6 +1,6 @@ import { createLocationProxy } from "./location"; import { CookieStore, decodeUrl } from "./shared"; -import { createDocumentProxy, createWindowProxy } from "./window"; +import { createDocumentProxy, createGlobalProxy } from "./window"; declare global { interface Window { @@ -43,11 +43,11 @@ export class ScramjetClient { static SCRAMJET = Symbol.for("scramjet client global"); documentProxy: any; - windowProxy: any; + globalProxy: any; locationProxy: any; serviceWorker: ServiceWorkerContainer; - nativeDescriptors: Record = {}; + descriptors: Record = {}; natives: Record = {}; cookieStore = new CookieStore(); @@ -69,7 +69,7 @@ export class ScramjetClient { } this.locationProxy = createLocationProxy(this, global); - this.windowProxy = createWindowProxy(this, global); + this.globalProxy = createGlobalProxy(this, global); global[ScramjetClient.SCRAMJET] = this; } @@ -124,7 +124,7 @@ export class ScramjetClient { const prop = split.pop(); const target = split.reduce((a, b) => a?.[b], this.global); const original = Reflect.get(target, prop); - this.natives[prop] = original; + this.natives[name] = original; this.RawProxy(target, prop, handler); } @@ -206,7 +206,7 @@ export class ScramjetClient { const target = split.reduce((a, b) => a?.[b], this.global); const original = Object.getOwnPropertyDescriptor(target, prop); - this.nativeDescriptors[name] = original; + this.descriptors[name] = original; return this.RawTrap(target, prop, descriptor); } diff --git a/src/client/document.ts b/src/client/document.ts new file mode 100644 index 0000000..4ac928e --- /dev/null +++ b/src/client/document.ts @@ -0,0 +1,30 @@ +import { encodeUrl } from "../shared/rewriters/url"; +import { ScramjetClient } from "./client"; + +export function createDocumentProxy( + client: ScramjetClient, + self: typeof globalThis +) { + return new Proxy(self.document, { + get(target, prop) { + if (prop === "location") { + return client.locationProxy; + } + + if (prop === "defaultView") { + return client.globalProxy; + } + + const value = Reflect.get(target, prop); + return value; + }, + set(target, prop, newValue) { + if (prop === "location") { + location.href = encodeUrl(newValue); + return; + } + + return Reflect.set(target, prop, newValue); + }, + }); +} diff --git a/src/client/dom/element.ts b/src/client/dom/element.ts index d3855e0..9d3c9ac 100644 --- a/src/client/dom/element.ts +++ b/src/client/dom/element.ts @@ -157,7 +157,7 @@ export default function (client: ScramjetClient, self: typeof window) { const newclient = new ScramjetClient(realwin.self); newclient.hook(); - return newclient.windowProxy; + return newclient.globalProxy; } }, }); diff --git a/src/client/dom/open.ts b/src/client/dom/open.ts index 9422a96..9783dd2 100644 --- a/src/client/dom/open.ts +++ b/src/client/dom/open.ts @@ -20,7 +20,7 @@ export default function (client: ScramjetClient) { // hook the opened window newclient.hook(); - return ctx.return(newclient.windowProxy); + return ctx.return(newclient.globalProxy); } }, }); diff --git a/src/client/global.ts b/src/client/global.ts new file mode 100644 index 0000000..5b3b6a1 --- /dev/null +++ b/src/client/global.ts @@ -0,0 +1,55 @@ +import { encodeUrl } from "./shared"; +import { ScramjetClient } from "./client"; +import { indirectEval } from "./shared/eval"; +import { config } from "./shared"; + +export function createGlobalProxy( + client: ScramjetClient, + self: typeof globalThis +): typeof globalThis { + return new Proxy(self, { + get(target, prop) { + if (prop === "location") return client.locationProxy; + + if ( + typeof prop === "string" && + ["window", "top", "self", "globalThis", "parent", "document"].includes( + prop + ) + ) + return self[config.wrapfn](self[prop]); + + if (prop === "$scramjet") return; + + if (prop === "eval") return indirectEval.bind(client); + + const value = Reflect.get(target, prop); + + return value; + }, + + set(target, prop, value) { + if (prop === "location") { + location.href = encodeUrl(value); + return; + } + + return Reflect.set(target, prop, value); + }, + has(target, prop) { + if (prop === "$scramjet") return false; + return Reflect.has(target, prop); + }, + ownKeys(target) { + return Reflect.ownKeys(target).filter((key) => key !== "$scramjet"); + }, + defineProperty(target, property, attributes) { + if (!attributes.get && !attributes.set) { + attributes.writable = true; + } + attributes.configurable = true; + + return Reflect.defineProperty(target, property, attributes); + }, + }); +} diff --git a/src/client/shared/event.ts b/src/client/shared/event.ts index 0e60b32..1e3046b 100644 --- a/src/client/shared/event.ts +++ b/src/client/shared/event.ts @@ -22,7 +22,7 @@ export default function (client: ScramjetClient, self: Self) { source() { let scram: ScramjetClient = this.source[ScramjetClient.SCRAMJET]; - if (scram) return scram.windowProxy; + if (scram) return scram.globalProxy; return this.source; }, diff --git a/src/client/shared/unproxy.ts b/src/client/shared/unproxy.ts index 7f21bc9..544aef6 100644 --- a/src/client/shared/unproxy.ts +++ b/src/client/shared/unproxy.ts @@ -45,11 +45,11 @@ export default function (client: ScramjetClient, self: typeof window) { export function unproxy(ctx: ProxyCtx, client: ScramjetClient) { const self = client.global; - if (ctx.this === client.windowProxy) ctx.this = self; + if (ctx.this === client.globalProxy) ctx.this = self; if (ctx.this === client.documentProxy) ctx.this = self.document; for (const i in ctx.args) { if (ctx.args[i] === client.documentProxy) ctx.args[i] = self.document; - if (ctx.args[i] === client.windowProxy) ctx.args[i] = self; + if (ctx.args[i] === client.globalProxy) ctx.args[i] = self; } } diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index 5f50d4e..c35bb8d 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -13,14 +13,14 @@ export default function (client: ScramjetClient, self: typeof globalThis) { argdbg(arg); } if (iswindow && identifier instanceof self.Window) { - return client.windowProxy; + return client.globalProxy; } else if (iswindow && identifier instanceof self.parent.self.Window) { if (ScramjetClient.SCRAMJET in self.parent.self) { // ... then we're in a subframe, and the parent frame is also in a proxy context, so we should return its proxy return self.parent.self[ScramjetClient.SCRAMJET].windowProxy; } else { // ... then we should pretend we aren't nested and return the current window - return client.windowProxy; + return client.globalProxy; } } else if (iswindow && identifier instanceof self.top.self.Window) { // instead of returning top, we need to return the uppermost parent that's inside a scramjet context @@ -46,7 +46,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) { } else if (iswindow && identifier instanceof self.Document) { return client.documentProxy; } else if (isworker && identifier instanceof self.WorkerGlobalScope) { - return client.windowProxy; + return client.globalProxy; } return identifier; diff --git a/src/client/window.ts b/src/client/window.ts deleted file mode 100644 index a100735..0000000 --- a/src/client/window.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { encodeUrl } from "./shared"; -import { ScramjetClient } from "./client"; -import { indirectEval } from "./shared/eval"; -import { config } from "./shared"; - -export function createWindowProxy( - client: ScramjetClient, - self: typeof globalThis -): typeof globalThis { - return new Proxy(self, { - get(target, prop) { - const propIsString = typeof prop === "string"; - if (prop === "location") return client.locationProxy; - - if ( - propIsString && - ["window", "top", "self", "globalThis", "parent"].includes(prop) - ) - return self[config.wrapfn](self[prop]); - - if (prop === "$scramjet") return; - - if (prop === "eval") return indirectEval.bind(client); - - const value = Reflect.get(target, prop); - - return value; - }, - - set(target, prop, newValue) { - // ensures that no apis are overwritten - if ( - typeof prop === "string" && - ["window", "top", "parent", "self", "globalThis", "location"].includes( - prop - ) - ) { - return false; - } - - return Reflect.set(target, prop, newValue); - }, - defineProperty(target, property, attributes) { - if (!attributes.get && !attributes.set) { - attributes.writable = true; - } - attributes.configurable = true; - - return Reflect.defineProperty(target, property, attributes); - }, - }); -} - -export function createDocumentProxy( - client: ScramjetClient, - self: typeof globalThis -) { - return new Proxy(self.document || {}, { - get(target, prop) { - const propIsString = typeof prop === "string"; - - if (propIsString && prop === "location") { - return client.locationProxy; - } - - if (propIsString && prop === "defaultView") { - return client.windowProxy; - } - - const value = Reflect.get(target, prop); - - return value; - }, - set(target, prop, newValue) { - if (typeof prop === "string" && prop === "location") { - //@ts-ignore - location = new URL(encodeUrl(newValue)); - - return; - } - - return Reflect.set(target, prop, newValue); - }, - }); -}