refactor window and document proxies

This commit is contained in:
velzie 2024-08-25 15:48:12 -04:00
parent f3f1a2ed41
commit 6f51642afb
No known key found for this signature in database
GPG key ID: 048413F95F0DDE1F
9 changed files with 99 additions and 99 deletions

View file

@ -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<string, PropertyDescriptor> = {};
descriptors: Record<string, PropertyDescriptor> = {};
natives: Record<string, any> = {};
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);
}

30
src/client/document.ts Normal file
View file

@ -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);
},
});
}

View file

@ -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;
}
},
});

View file

@ -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);
}
},
});

55
src/client/global.ts Normal file
View file

@ -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);
},
});
}

View file

@ -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;
},

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);
},
});
}