rewrite location proxy (regressions!)

This commit is contained in:
velzie 2024-08-30 18:26:59 -04:00
parent 2bc31d7e56
commit 7f8956979a
No known key found for this signature in database
GPG key ID: 048413F95F0DDE1F
5 changed files with 132 additions and 95 deletions

View file

@ -1,10 +1,13 @@
import { SCRAMJETCLIENT } from "../symbols"; import { iswindow } from ".";
import { ScramjetFrame } from "../controller/frame";
import { SCRAMJETCLIENT, SCRAMJETFRAME } from "../symbols";
import { createDocumentProxy } from "./document"; import { createDocumentProxy } from "./document";
import { createGlobalProxy } from "./global"; import { createGlobalProxy } from "./global";
import { getOwnPropertyDescriptorHandler } from "./helpers"; import { getOwnPropertyDescriptorHandler } from "./helpers";
import { createLocationProxy } from "./location"; import { createLocationProxy } from "./location";
import { nativeGetOwnPropertyDescriptor } from "./natives"; import { nativeGetOwnPropertyDescriptor } from "./natives";
import { CookieStore, config, decodeUrl } from "./shared"; import { CookieStore, config, decodeUrl } from "./shared";
import { createWrapFn } from "./shared/wrap";
declare global { declare global {
interface Window { interface Window {
@ -51,6 +54,7 @@ export class ScramjetClient {
descriptors: Record<string, PropertyDescriptor> = {}; descriptors: Record<string, PropertyDescriptor> = {};
natives: Record<string, any> = {}; natives: Record<string, any> = {};
wrapfn: (i: any, ...args: any) => any;
cookieStore = new CookieStore(); cookieStore = new CookieStore();
@ -68,7 +72,7 @@ export class ScramjetClient {
constructor(public global: typeof globalThis) { constructor(public global: typeof globalThis) {
this.serviceWorker = this.global.navigator.serviceWorker; this.serviceWorker = this.global.navigator.serviceWorker;
if ("document" in global) { if (iswindow) {
this.documentProxy = createDocumentProxy(this, global); this.documentProxy = createDocumentProxy(this, global);
global.document[SCRAMJETCLIENT] = this; global.document[SCRAMJETCLIENT] = this;
@ -76,10 +80,23 @@ export class ScramjetClient {
this.locationProxy = createLocationProxy(this, global); this.locationProxy = createLocationProxy(this, global);
this.globalProxy = createGlobalProxy(this, global); this.globalProxy = createGlobalProxy(this, global);
this.wrapfn = createWrapFn(this, global);
global[SCRAMJETCLIENT] = this; global[SCRAMJETCLIENT] = this;
} }
get frame(): ScramjetFrame | null {
if (!iswindow) return null;
const frame = this.global.window.frameElement;
if (!frame) return null; // we're top level
const sframe = frame[SCRAMJETFRAME];
if (!sframe) return null; // we're a subframe. TODO handle propagation but not now
return sframe;
}
loadcookies(cookiestr: string) { loadcookies(cookiestr: string) {
this.cookieStore.load(cookiestr); this.cookieStore.load(cookiestr);
} }

View file

@ -8,11 +8,7 @@ export const order = 2;
export const enabled = () => self.$scramjet.config.flags.serviceworkers; export const enabled = () => self.$scramjet.config.flags.serviceworkers;
export function disabled(client: ScramjetClient, self: Self) { export function disabled(client: ScramjetClient, self: Self) {
client.Trap("navigator.serviceWorker", { Reflect.deleteProperty(Navigator.prototype, "serviceWorker");
get() {
return undefined;
},
});
} }
export default function (client: ScramjetClient, self: Self) { export default function (client: ScramjetClient, self: Self) {

View file

@ -1,44 +1,78 @@
// @ts-nocheck // @ts-nocheck
import { ScramjetClient } from "./client"; import { ScramjetClient } from "./client";
import { nativeGetOwnPropertyDescriptor } from "./natives";
import { encodeUrl, decodeUrl } from "./shared"; import { encodeUrl, decodeUrl } from "./shared";
export function createLocationProxy( export function createLocationProxy(
client: ScramjetClient, client: ScramjetClient,
self: typeof globalThis self: typeof globalThis
) { ) {
function createLocation() { // location cannot be Proxy()d
const loc = new URL(client.url.href); const fakeLocation = {};
Object.setPrototypeOf(fakeLocation, self.Location.prototype);
fakeLocation.constructor = self.Location;
loc.assign = (url: string) => self.location.assign(encodeUrl(url)); const urlprops = [
loc.reload = () => self.location.reload(); "hash",
loc.replace = (url: string) => self.location.replace(encodeUrl(url)); "host",
loc.toString = () => loc.href; "hostname",
"href",
return loc; "origin",
"pathname",
"port",
"search",
];
for (const prop of urlprops) {
const native = nativeGetOwnPropertyDescriptor(self.location, prop);
const desc = {
configurable: true,
enumerable: true,
get: new Proxy(native.get, {
apply() {
return client.url[prop];
},
}),
};
if (native.set) {
desc.set = new Proxy(native.set, {
apply(target, thisArg, args) {
let url = new URL(client.url.href);
url[prop] = args[0];
self.location.href = encodeUrl(url.href);
},
});
}
Object.defineProperty(fakeLocation, prop, desc);
} }
return new Proxy( // functions
{ fakeLocation.toString = new Proxy(self.location.toString, {
host: "", apply() {
return client.url.href;
}, },
{ });
get(target, prop) { fakeLocation.valueOf = new Proxy(self.location.valueOf, {
const loc = createLocation(); apply() {
return client.url.href;
return loc[prop];
}, },
});
set(obj, prop, value) { fakeLocation.assign = new Proxy(self.location.assign, {
const loc = createLocation(); apply(target, thisArg, args) {
args[0] = encodeUrl(args[0]);
if (prop === "href") { Reflect.apply(target, thisArg, args);
self.location.href = encodeUrl(value);
} else {
loc[prop] = value;
}
return true;
}, },
} });
); fakeLocation.reload = new Proxy(self.location.reload, {
apply(target, thisArg, args) {
Reflect.apply(target, thisArg, args);
},
});
fakeLocation.replace = new Proxy(self.location.replace, {
apply(target, thisArg, args) {
args[0] = encodeUrl(args[0]);
Reflect.apply(target, thisArg, args);
},
});
return fakeLocation;
} }

View file

@ -1,17 +1,3 @@
export const nativeFunction = self.Function; export const nativeFunction = self.Function;
export const nativeGetOwnPropertyDescriptor = export const nativeGetOwnPropertyDescriptor =
self.Object.getOwnPropertyDescriptor; self.Object.getOwnPropertyDescriptor;
// descriptors
export const nativeDefaultViewGetter = nativeGetOwnPropertyDescriptor(
Document.prototype,
"defaultView"
)!.get!;
export const nativeContentDocumentGetter = nativeGetOwnPropertyDescriptor(
HTMLIFrameElement.prototype,
"contentDocument"
)!.get!;
export const nativeContentWindowGetter = nativeGetOwnPropertyDescriptor(
HTMLIFrameElement.prototype,
"contentWindow"
)!.get!;

View file

@ -3,15 +3,11 @@ import { SCRAMJETCLIENT } from "../../symbols";
import { ScramjetClient } from "../client"; import { ScramjetClient } from "../client";
import { config } from "../shared"; import { config } from "../shared";
export default function (client: ScramjetClient, self: typeof globalThis) { export function createWrapFn(client: ScramjetClient, self: typeof globalThis) {
// the main magic of the proxy. all attempts to access any "banned objects" will be redirected here, and instead served a proxy object return function (identifier: any, args: any) {
// this contrasts from how other proxies will leave the root object alone and instead attempt to catch every member access
// this presents some issues (see element.ts), but makes us a good bit faster at runtime!
Object.defineProperty(self, config.wrapfn, {
value: function (identifier: any, args: any) {
if (args && typeof args === "object" && args.length === 0) if (args && typeof args === "object" && args.length === 0)
for (const arg of args) { for (const arg of args) {
argdbg(arg); // argdbg(arg);
} }
if (iswindow && identifier instanceof self.Window) { if (iswindow && identifier instanceof self.Window) {
return client.globalProxy; return client.globalProxy;
@ -51,7 +47,15 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
} }
return identifier; return identifier;
}, };
}
export default function (client: ScramjetClient, self: typeof globalThis) {
// the main magic of the proxy. all attempts to access any "banned objects" will be redirected here, and instead served a proxy object
// this contrasts from how other proxies will leave the root object alone and instead attempt to catch every member access
// this presents some issues (see element.ts), but makes us a good bit faster at runtime!
Object.defineProperty(self, config.wrapfn, {
value: client.wrapfn,
writable: false, writable: false,
configurable: false, configurable: false,
}); });