feat: add helper functions to natives store + refac

This commit is contained in:
Percs 2024-12-10 20:45:51 +00:00 committed by GitHub
parent 70f0315791
commit ef8735f95a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 164 additions and 121 deletions

View file

@ -18,6 +18,16 @@ import { createWrapFn } from "./shared/wrap";
import { NavigateEvent } from "./events"; import { NavigateEvent } from "./events";
import type { URLMeta } from "../shared/rewriters/url"; import type { URLMeta } from "../shared/rewriters/url";
type NativeStore = {
store: Record<string, any>;
call: (target: string, that: any, ...args) => any;
construct: (target: string, ...args) => any;
};
type DescriptorStore = {
store: Record<string, PropertyDescriptor>;
get: (target: string, that: any) => any;
set: (target: string, that: any, value: any) => void;
};
//eslint-disable-next-line //eslint-disable-next-line
export type AnyFunction = Function; export type AnyFunction = Function;
@ -65,8 +75,8 @@ export class ScramjetClient {
serviceWorker: ServiceWorkerContainer; serviceWorker: ServiceWorkerContainer;
bare: BareClientType; bare: BareClientType;
descriptors: Record<string, PropertyDescriptor> = {}; natives: NativeStore;
natives: Record<string, any>; descriptors: DescriptorStore;
wrapfn: (i: any, ...args: any) => any; wrapfn: (i: any, ...args: any) => any;
cookieStore = new CookieStore(); cookieStore = new CookieStore();
@ -120,48 +130,79 @@ export class ScramjetClient {
}) })
); );
} }
this.natives = new Proxy( this.natives = {
{}, store: new Proxy(
{ {},
get: (target, prop: string) => { {
if (prop in target) { 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]; return target[prop];
} },
}
),
construct(target: string, ...args) {
const original = this.store[target];
if (!original) return;
const split = prop.split("."); return new original(...args);
const realProp = split.pop(); },
const realTarget = split.reduce((a, b) => a?.[b], this.global); 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); const split = prop.split(".");
target[prop] = original; 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]; return target[prop];
} },
}
),
get(target: string, that: any) {
const original = this.store[target];
if (!original) return;
const split = prop.split("."); return original.get.call(that);
const realProp = split.pop(); },
const realTarget = split.reduce((a, b) => a?.[b], this.global); set(target: string, that: any, value: any) {
const original = this.store[target];
if (!original) return;
if (!realTarget) return; original.set.call(that, value);
},
const original = nativeGetOwnPropertyDescriptor(realTarget, realProp); };
target[prop] = original;
return target[prop];
},
}
);
// eslint-disable-next-line @typescript-eslint/no-this-alias // eslint-disable-next-line @typescript-eslint/no-this-alias
const client = this; const client = this;
this.meta = { this.meta = {
@ -285,7 +326,7 @@ export class ScramjetClient {
if (!target) return; if (!target) return;
const original = Reflect.get(target, prop); const original = Reflect.get(target, prop);
this.natives[name] = original; this.natives.store[name] = original;
this.RawProxy(target, prop, handler); this.RawProxy(target, prop, handler);
} }
@ -412,7 +453,7 @@ export class ScramjetClient {
if (!target) return; if (!target) return;
const original = nativeGetOwnPropertyDescriptor(target, prop); const original = nativeGetOwnPropertyDescriptor(target, prop);
this.descriptors[name] = original; this.descriptors.store[name] = original;
return this.RawTrap(target, prop, descriptor); return this.RawTrap(target, prop, descriptor);
} }

View file

@ -127,7 +127,8 @@ export default function (client: ScramjetClient, self: typeof window) {
} }
if ( if (
client.natives["Element.prototype.hasAttribute"].call( client.natives.call(
"Element.prototype.hasAttribute",
ctx.this, ctx.this,
`scramjet-attr-${name}` `scramjet-attr-${name}`
) )
@ -185,7 +186,7 @@ export default function (client: ScramjetClient, self: typeof window) {
// i actually need to do something with this // i actually need to do something with this
client.Proxy("Element.prototype.setAttributeNode", { client.Proxy("Element.prototype.setAttributeNode", {
apply(ctx) {}, apply(_ctx) {},
}); });
client.Proxy("Element.prototype.setAttributeNS", { client.Proxy("Element.prototype.setAttributeNS", {
@ -203,7 +204,8 @@ export default function (client: ScramjetClient, self: typeof window) {
if (ruleList) { if (ruleList) {
ctx.args[2] = ruleList.fn(value, client.meta, client.cookieStore); ctx.args[2] = ruleList.fn(value, client.meta, client.cookieStore);
client.natives["Element.prototype.setAttribute"].call( client.natives.call(
"Element.prototype.setAttribute",
ctx.this, ctx.this,
`scramjet-attr-${ctx.args[1]}`, `scramjet-attr-${ctx.args[1]}`,
value value
@ -216,7 +218,8 @@ export default function (client: ScramjetClient, self: typeof window) {
apply(ctx) { apply(ctx) {
if (ctx.args[0].startsWith("scramjet-attr")) return ctx.return(undefined); if (ctx.args[0].startsWith("scramjet-attr")) return ctx.return(undefined);
if ( if (
client.natives["Element.prototype.hasAttribute"].call( client.natives.call(
"Element.prototype.hasAttribute",
ctx.this, ctx.this,
ctx.args[0] ctx.args[0]
) )
@ -230,7 +233,8 @@ export default function (client: ScramjetClient, self: typeof window) {
apply(ctx) { apply(ctx) {
if (ctx.args[0].startsWith("scramjet-attr")) return ctx.return(false); if (ctx.args[0].startsWith("scramjet-attr")) return ctx.return(false);
if ( if (
client.natives["Element.prototype.hasAttribute"].call( client.natives.call(
"Element.prototype.hasAttribute",
ctx.this, ctx.this,
ctx.args[0] ctx.args[0]
) )
@ -245,7 +249,8 @@ export default function (client: ScramjetClient, self: typeof window) {
let newval; let newval;
if (ctx.this instanceof self.HTMLScriptElement) { if (ctx.this instanceof self.HTMLScriptElement) {
newval = rewriteJs(value, "(anonymous script element)", client.meta); newval = rewriteJs(value, "(anonymous script element)", client.meta);
client.natives["Element.prototype.setAttribute"].call( client.natives.call(
"Element.prototype.setAttribute",
ctx.this, ctx.this,
"scramjet-attr-script-source-src", "scramjet-attr-script-source-src",
bytesToBase64(encoder.encode(newval)) bytesToBase64(encoder.encode(newval))
@ -264,9 +269,11 @@ export default function (client: ScramjetClient, self: typeof window) {
}, },
get(ctx) { get(ctx) {
if (ctx.this instanceof self.HTMLScriptElement) { if (ctx.this instanceof self.HTMLScriptElement) {
const scriptSource = client.natives[ const scriptSource = client.natives.call(
"Element.prototype.getAttribute" "Element.prototype.getAttribute",
].call(ctx.this, "scramjet-attr-script-source-src"); ctx.this,
"scramjet-attr-script-source-src"
);
if (scriptSource) { if (scriptSource) {
return atob(scriptSource); return atob(scriptSource);
@ -358,11 +365,10 @@ export default function (client: ScramjetClient, self: typeof window) {
], ],
{ {
get(ctx) { get(ctx) {
const contentwindow = const realwin = client.descriptors.get(
client.descriptors[ `${ctx.this.constructor.name}.prototype.contentWindow`,
`${ctx.this.constructor.name}.prototype.contentWindow` ctx.this
].get; );
const realwin = contentwindow.apply(ctx.this);
if (!realwin) return realwin; if (!realwin) return realwin;
if (SCRAMJETCLIENT in realwin) { if (SCRAMJETCLIENT in realwin) {

View file

@ -67,8 +67,7 @@ export default function (client: ScramjetClient, _self: Self) {
url += "&type=module"; url += "&type=module";
} }
const nativeSharedWorker = client.natives["SharedWorker"]; const worker = client.natives.construct("SharedWorker", url);
const worker = new nativeSharedWorker(url);
const handle = worker.port; const handle = worker.port;

View file

@ -3,7 +3,7 @@ import { config } from "../../shared";
import { rewriteUrl } from "../../shared/rewriters/url"; import { rewriteUrl } from "../../shared/rewriters/url";
export default function (client: ScramjetClient, self: Self) { 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) { self[config.globals.importfn] = function (base: string) {
return function (url: string) { return function (url: string) {

View file

@ -5,7 +5,7 @@ import { ScramjetClient } from "../../client";
export default function (client: ScramjetClient, self: Self) { export default function (client: ScramjetClient, self: Self) {
let worker; let worker;
if (self.Worker && flagEnabled("syncxhr", client.url)) { 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 ARGS = Symbol("xhr original args");
const HEADERS = Symbol("xhr headers"); const HEADERS = Symbol("xhr headers");
@ -44,7 +44,7 @@ export default function (client: ScramjetClient, self: Self) {
const sab = new SharedArrayBuffer(1024, { maxByteLength: 2147483647 }); const sab = new SharedArrayBuffer(1024, { maxByteLength: 2147483647 });
const view = new DataView(sab); const view = new DataView(sab);
client.natives["Worker.prototype.postMessage"].call(worker, { client.natives.call("Worker.prototype.postMessage", worker, {
sab, sab,
args, args,
headers: ctx.this[HEADERS], headers: ctx.this[HEADERS],

View file

@ -1,76 +1,73 @@
import { iswindow } from "..";
import { BareMuxConnection } from "../../shared"; import { BareMuxConnection } from "../../shared";
import { rewriteUrl } from "../../shared"; import { rewriteUrl } from "../../shared";
import { ScramjetClient } from "../client"; import { ScramjetClient } from "../client";
export default function (client: ScramjetClient, self: typeof globalThis) { export default function (client: ScramjetClient, _self: typeof globalThis) {
if (self.Worker) { client.Proxy("Worker", {
client.Proxy("Worker", { construct({ args, call }) {
construct({ args, call }) { args[0] = rewriteUrl(args[0], client.meta) + "?dest=worker";
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"; args[0] += "&type=module";
} }
const worker = call(); if (args[1].name) {
const conn = new BareMuxConnection(); args[1].name = `${client.url.origin}@${args[1].name}`;
(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}`;
}
} }
}
const worker = call(); const worker = call();
const conn = new BareMuxConnection(); const conn = new BareMuxConnection();
(async () => { (async () => {
const port = await conn.getInnerPort(); const port = await conn.getInnerPort();
client.natives["MessagePort.prototype.postMessage"].call( client.natives.call(
worker.port, "MessagePort.prototype.postMessage",
{ worker.port,
$scramjet$type: "baremuxinit", {
port, $scramjet$type: "baremuxinit",
}, port,
[port] },
); [port]
})(); );
}, })();
}); },
} });
client.Proxy("Worklet.prototype.addModule", {
apply(ctx) {
if (ctx.args[0]) ctx.args[0] = rewriteUrl(ctx.args[0], client.meta);
},
});
} }