mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-14 06:50:01 -04:00
refactor: everything
This commit is contained in:
parent
78e666d314
commit
506d99f9b6
37 changed files with 925 additions and 885 deletions
|
@ -1,8 +0,0 @@
|
||||||
import { client } from ".";
|
|
||||||
|
|
||||||
// goodybye spyware~
|
|
||||||
client.Proxy("navigator.sendBeacon", {
|
|
||||||
apply(ctx) {
|
|
||||||
ctx.return(null);
|
|
||||||
},
|
|
||||||
});
|
|
229
src/client/client.ts
Normal file
229
src/client/client.ts
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
import { createLocationProxy } from "./location";
|
||||||
|
import { decodeUrl } from "./shared";
|
||||||
|
import { createDocumentProxy, createWindowProxy } from "./window";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
$s: any;
|
||||||
|
$tryset: any;
|
||||||
|
$sImport: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//eslint-disable-next-line
|
||||||
|
type AnyFunction = Function;
|
||||||
|
|
||||||
|
type ProxyCtx = {
|
||||||
|
fn: AnyFunction;
|
||||||
|
this: any;
|
||||||
|
args: any[];
|
||||||
|
newTarget: AnyFunction;
|
||||||
|
return: (r: any) => void;
|
||||||
|
};
|
||||||
|
type Proxy = {
|
||||||
|
construct?(ctx: ProxyCtx): any;
|
||||||
|
apply?(ctx: ProxyCtx): any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TrapCtx<T> = {
|
||||||
|
this: any;
|
||||||
|
get: () => T;
|
||||||
|
set: (v: T) => void;
|
||||||
|
};
|
||||||
|
type Trap<T> = {
|
||||||
|
writable?: boolean;
|
||||||
|
value?: any;
|
||||||
|
enumerable?: boolean;
|
||||||
|
configurable?: boolean;
|
||||||
|
get?: (ctx: TrapCtx<T>) => T;
|
||||||
|
set?: (ctx: TrapCtx<T>, v: T) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ScramjetClient {
|
||||||
|
static SCRAMJET = Symbol.for("scramjet client global");
|
||||||
|
|
||||||
|
documentProxy: any;
|
||||||
|
windowProxy: any;
|
||||||
|
locationProxy: any;
|
||||||
|
|
||||||
|
constructor(public global: typeof globalThis) {
|
||||||
|
if ("document" in self) {
|
||||||
|
this.documentProxy = createDocumentProxy(this, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locationProxy = createLocationProxy(this, self);
|
||||||
|
this.windowProxy = createWindowProxy(this, self);
|
||||||
|
|
||||||
|
self[ScramjetClient.SCRAMJET] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook() {
|
||||||
|
dbg.log("hwat");
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const context = import.meta.webpackContext(".", {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const key of context.keys()) {
|
||||||
|
if (!key.endsWith(".ts")) continue;
|
||||||
|
if (
|
||||||
|
(key.startsWith("./dom/") && "window" in self) ||
|
||||||
|
(key.startsWith("./worker/") && "WorkerGlobalScope" in self) ||
|
||||||
|
key.startsWith("./shared/")
|
||||||
|
) {
|
||||||
|
console.log("??", key);
|
||||||
|
const module = context(key);
|
||||||
|
module.default(this, this.global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Proxy(name: string | string[], handler: Proxy) {
|
||||||
|
if (Array.isArray(name)) {
|
||||||
|
for (const n of name) {
|
||||||
|
this.Proxy(n, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const split = name.split(".");
|
||||||
|
const prop = split.pop();
|
||||||
|
const target = split.reduce((a, b) => a?.[b], this.global);
|
||||||
|
this.RawProxy(target, prop, handler);
|
||||||
|
}
|
||||||
|
RawProxy(target: any, prop: string, handler: Proxy) {
|
||||||
|
if (!target) return;
|
||||||
|
if (!prop) return;
|
||||||
|
if (!Reflect.has(target, prop)) return;
|
||||||
|
|
||||||
|
const value = Reflect.get(target, prop);
|
||||||
|
delete target[prop];
|
||||||
|
|
||||||
|
const h: ProxyHandler<any> = {};
|
||||||
|
|
||||||
|
if (handler.construct) {
|
||||||
|
h.construct = function (
|
||||||
|
constructor: any,
|
||||||
|
argArray: any[],
|
||||||
|
newTarget: AnyFunction
|
||||||
|
) {
|
||||||
|
let returnValue: any = null;
|
||||||
|
|
||||||
|
const ctx: ProxyCtx = {
|
||||||
|
fn: constructor,
|
||||||
|
this: null,
|
||||||
|
args: argArray,
|
||||||
|
newTarget: newTarget,
|
||||||
|
return: (r: any) => {
|
||||||
|
returnValue = r;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
handler.construct(ctx);
|
||||||
|
|
||||||
|
if (returnValue) {
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.construct(ctx.fn, ctx.args, ctx.newTarget);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler.apply) {
|
||||||
|
h.apply = function (fn: any, thisArg: any, argArray: any[]) {
|
||||||
|
let returnValue: any = null;
|
||||||
|
|
||||||
|
const ctx: ProxyCtx = {
|
||||||
|
fn,
|
||||||
|
this: thisArg,
|
||||||
|
args: argArray,
|
||||||
|
newTarget: null,
|
||||||
|
return: (r: any) => {
|
||||||
|
returnValue = r;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
handler.apply(ctx);
|
||||||
|
|
||||||
|
if (returnValue) {
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.apply(ctx.fn, ctx.this, ctx.args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
target[prop] = new Proxy(value, h);
|
||||||
|
}
|
||||||
|
Trap<T>(name: string | string[], descriptor: Trap<T>) {
|
||||||
|
if (Array.isArray(name)) {
|
||||||
|
for (const n of name) {
|
||||||
|
this.Trap(n, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const split = name.split(".");
|
||||||
|
const prop = split.pop();
|
||||||
|
const target = split.reduce((a, b) => a?.[b], this.global);
|
||||||
|
|
||||||
|
this.RawTrap(target, prop, descriptor);
|
||||||
|
}
|
||||||
|
RawTrap<T>(target: any, prop: string, descriptor: Trap<T>) {
|
||||||
|
if (!target) return;
|
||||||
|
if (!prop) return;
|
||||||
|
if (!Reflect.has(target, prop)) return;
|
||||||
|
|
||||||
|
const oldDescriptor = Object.getOwnPropertyDescriptor(target, prop);
|
||||||
|
|
||||||
|
const ctx: TrapCtx<T> = {
|
||||||
|
this: null,
|
||||||
|
get: function () {
|
||||||
|
return oldDescriptor && oldDescriptor.get.call(this.this);
|
||||||
|
},
|
||||||
|
set: function (v: T) {
|
||||||
|
oldDescriptor && oldDescriptor.set.call(this.this, v);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
delete target[prop];
|
||||||
|
|
||||||
|
const desc: PropertyDescriptor = {};
|
||||||
|
|
||||||
|
if (descriptor.get) {
|
||||||
|
desc.get = function () {
|
||||||
|
ctx.this = this;
|
||||||
|
|
||||||
|
return descriptor.get(ctx);
|
||||||
|
};
|
||||||
|
} else if (oldDescriptor?.get) {
|
||||||
|
desc.get = oldDescriptor.get;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.set) {
|
||||||
|
desc.set = function (v: T) {
|
||||||
|
ctx.this = this;
|
||||||
|
|
||||||
|
descriptor.set(ctx, v);
|
||||||
|
};
|
||||||
|
} else if (oldDescriptor?.set) {
|
||||||
|
desc.set = oldDescriptor.set;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor.enumerable) desc.enumerable = descriptor.enumerable;
|
||||||
|
else if (oldDescriptor?.enumerable)
|
||||||
|
desc.enumerable = oldDescriptor.enumerable;
|
||||||
|
if (descriptor.configurable) desc.configurable = descriptor.configurable;
|
||||||
|
else if (oldDescriptor?.configurable)
|
||||||
|
desc.configurable = oldDescriptor.configurable;
|
||||||
|
|
||||||
|
Object.defineProperty(target, prop, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
get url(): URL {
|
||||||
|
return new URL(decodeUrl(location.href));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
import { parse } from "set-cookie-parser";
|
|
||||||
import { client } from ".";
|
|
||||||
|
|
||||||
client.Trap("Document.prototype.cookie", {
|
|
||||||
get(ctx) {
|
|
||||||
const cookiestring = ctx.get();
|
|
||||||
dbg.log("original cookiestring", cookiestring);
|
|
||||||
//
|
|
||||||
// if (!cookiestring) return "";
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// let string = "";
|
|
||||||
//
|
|
||||||
// for (const cookiestr of cookiestring.split(";")) {
|
|
||||||
// const cookie = parse(cookiestr.trim())[0];
|
|
||||||
// if (cookie.name.startsWith(client.url.hostname + "@")) {
|
|
||||||
// let name = cookie.name.substring(client.url.hostname.length + 1);
|
|
||||||
// string += `${name}=${cookie.value}; `;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return string.trimEnd();
|
|
||||||
|
|
||||||
return "sp_t=00246e00653d39d1341bbe9d10f138c4; OptanonConsent=isGpcEnabled=0&datestamp=Sat+Jul+20+2024+16%3A11%3A26+GMT-0400+(Eastern+Daylight+Time)&version=202405.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&landingPath=https%3A%2F%2Fopen.spotify.com%2F&groups=BG169%3A1%2Ct00%3A1%2Ci00%3A1%2CBG170%3A1%2Cs00%3A1%2Cf00%3A1%2Cm00%3A1%2Cf11%3A1";
|
|
||||||
},
|
|
||||||
set(ctx, value: string) {
|
|
||||||
dbg.debug("setting cookie", value);
|
|
||||||
const cookie = parse(value)[0];
|
|
||||||
|
|
||||||
let date = new Date();
|
|
||||||
let expires = cookie.expires;
|
|
||||||
|
|
||||||
dbg.error("expires", expires);
|
|
||||||
// if (expires instanceof Date) {
|
|
||||||
// if (isNaN(expires.getTime())) return;
|
|
||||||
// if (expires.getTime() < date.getTime()) return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// set.call(document, `${cookie.name}=${cookie.value}`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
delete self.cookieStore;
|
|
||||||
|
|
||||||
// sp_t=00e49dc8-59d0-4b5f-9beb-ec7b67368498; path=/; expires=Invalid Date
|
|
||||||
// OTZ=7653361_72_76_104100_72_446760;path=/;expires=Mon, 19 Aug 2024 20:01:06 GMT;secure
|
|
49
src/client/dom/cookie.ts
Normal file
49
src/client/dom/cookie.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { parse } from "set-cookie-parser";
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
|
client.Trap("Document.prototype.cookie", {
|
||||||
|
get(ctx) {
|
||||||
|
const cookiestring = ctx.get();
|
||||||
|
dbg.log("original cookiestring", cookiestring);
|
||||||
|
//
|
||||||
|
// if (!cookiestring) return "";
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// let string = "";
|
||||||
|
//
|
||||||
|
// for (const cookiestr of cookiestring.split(";")) {
|
||||||
|
// const cookie = parse(cookiestr.trim())[0];
|
||||||
|
// if (cookie.name.startsWith(client.url.hostname + "@")) {
|
||||||
|
// let name = cookie.name.substring(client.url.hostname.length + 1);
|
||||||
|
// string += `${name}=${cookie.value}; `;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return string.trimEnd();
|
||||||
|
|
||||||
|
return "sp_t=00246e00653d39d1341bbe9d10f138c4; OptanonConsent=isGpcEnabled=0&datestamp=Sat+Jul+20+2024+16%3A11%3A26+GMT-0400+(Eastern+Daylight+Time)&version=202405.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&landingPath=https%3A%2F%2Fopen.spotify.com%2F&groups=BG169%3A1%2Ct00%3A1%2Ci00%3A1%2CBG170%3A1%2Cs00%3A1%2Cf00%3A1%2Cm00%3A1%2Cf11%3A1";
|
||||||
|
},
|
||||||
|
set(ctx, value: string) {
|
||||||
|
dbg.debug("setting cookie", value);
|
||||||
|
const cookie = parse(value)[0];
|
||||||
|
|
||||||
|
let date = new Date();
|
||||||
|
let expires = cookie.expires;
|
||||||
|
|
||||||
|
dbg.error("expires", expires);
|
||||||
|
// if (expires instanceof Date) {
|
||||||
|
// if (isNaN(expires.getTime())) return;
|
||||||
|
// if (expires.getTime() < date.getTime()) return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// set.call(document, `${cookie.name}=${cookie.value}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
delete self.cookieStore;
|
||||||
|
|
||||||
|
// sp_t=00e49dc8-59d0-4b5f-9beb-ec7b67368498; path=/; expires=Invalid Date
|
||||||
|
// OTZ=7653361_72_76_104100_72_446760;path=/;expires=Mon, 19 Aug 2024 20:01:06 GMT;secure
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { client } from ".";
|
import { ScramjetClient } from "../client";
|
||||||
import { rewriteCss } from "./shared";
|
import { rewriteCss } from "../shared";
|
||||||
|
|
||||||
const cssProperties = [
|
const cssProperties = [
|
||||||
"background",
|
"background",
|
||||||
|
@ -14,9 +14,11 @@ const cssProperties = [
|
||||||
];
|
];
|
||||||
// const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"];
|
// const jsProperties = ["background", "backgroundImage", "mask", "maskImage", "listStyle", "listStyleImage", "borderImage", "borderImageSource", "cursor"];
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient) {
|
||||||
client.Proxy("CSSStyleDeclaration.prototype.setProperty", {
|
client.Proxy("CSSStyleDeclaration.prototype.setProperty", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
if (cssProperties.includes(ctx.args[0]))
|
if (cssProperties.includes(ctx.args[0]))
|
||||||
ctx.args[1] = rewriteCss(ctx.args[1]);
|
ctx.args[1] = rewriteCss(ctx.args[1]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
163
src/client/dom/element.ts
Normal file
163
src/client/dom/element.ts
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
import { decodeUrl } from "../shared";
|
||||||
|
import {
|
||||||
|
encodeUrl,
|
||||||
|
rewriteCss,
|
||||||
|
rewriteHtml,
|
||||||
|
rewriteJs,
|
||||||
|
rewriteSrcset,
|
||||||
|
} from "../shared";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Element {
|
||||||
|
$origattrs: Record<string, string>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
|
dbg.log("RAN");
|
||||||
|
const attrObject = {
|
||||||
|
nonce: [self.HTMLElement],
|
||||||
|
integrity: [self.HTMLScriptElement, self.HTMLLinkElement],
|
||||||
|
csp: [self.HTMLIFrameElement],
|
||||||
|
src: [
|
||||||
|
self.HTMLImageElement,
|
||||||
|
self.HTMLMediaElement,
|
||||||
|
self.HTMLIFrameElement,
|
||||||
|
self.HTMLEmbedElement,
|
||||||
|
self.HTMLScriptElement,
|
||||||
|
],
|
||||||
|
href: [self.HTMLAnchorElement, self.HTMLLinkElement],
|
||||||
|
data: [self.HTMLObjectElement],
|
||||||
|
action: [self.HTMLFormElement],
|
||||||
|
formaction: [self.HTMLButtonElement, self.HTMLInputElement],
|
||||||
|
srcdoc: [self.HTMLIFrameElement],
|
||||||
|
srcset: [self.HTMLImageElement, self.HTMLSourceElement],
|
||||||
|
imagesrcset: [self.HTMLLinkElement],
|
||||||
|
};
|
||||||
|
|
||||||
|
const attrs = Object.keys(attrObject);
|
||||||
|
|
||||||
|
for (const attr of attrs) {
|
||||||
|
for (const element of attrObject[attr]) {
|
||||||
|
const descriptor = Object.getOwnPropertyDescriptor(
|
||||||
|
element.prototype,
|
||||||
|
attr
|
||||||
|
);
|
||||||
|
Object.defineProperty(element.prototype, attr, {
|
||||||
|
get() {
|
||||||
|
if (["src", "data", "href", "action", "formaction"].includes(attr)) {
|
||||||
|
return decodeUrl(descriptor.get.call(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.$origattrs[attr]) {
|
||||||
|
return this.$origattrs[attr];
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptor.get.call(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
set(value) {
|
||||||
|
this.$origattrs[attr] = value;
|
||||||
|
|
||||||
|
if (["nonce", "integrity", "csp"].includes(attr)) {
|
||||||
|
return;
|
||||||
|
} else if (
|
||||||
|
["src", "data", "href", "action", "formaction"].includes(attr)
|
||||||
|
) {
|
||||||
|
value = encodeUrl(value);
|
||||||
|
} else if (attr === "srcdoc") {
|
||||||
|
value = rewriteHtml(value);
|
||||||
|
} else if (["srcset", "imagesrcset"].includes(attr)) {
|
||||||
|
value = rewriteSrcset(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.set.call(this, value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Element.prototype.$origattrs = {};
|
||||||
|
|
||||||
|
self.Element.prototype.getAttribute = new Proxy(
|
||||||
|
self.Element.prototype.getAttribute,
|
||||||
|
{
|
||||||
|
apply(target, thisArg, argArray) {
|
||||||
|
if (attrs.includes(argArray[0]) && thisArg.$origattrs[argArray[0]]) {
|
||||||
|
return thisArg.$origattrs[argArray[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.apply(target, thisArg, argArray);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
self.Element.prototype.setAttribute = new Proxy(
|
||||||
|
self.Element.prototype.setAttribute,
|
||||||
|
{
|
||||||
|
apply(target, thisArg, argArray) {
|
||||||
|
if (attrs.includes(argArray[0])) {
|
||||||
|
thisArg.$origattrs[argArray[0]] = argArray[1];
|
||||||
|
if (["nonce", "integrity", "csp"].includes(argArray[0])) {
|
||||||
|
return;
|
||||||
|
} else if (
|
||||||
|
["src", "data", "href", "action", "formaction"].includes(
|
||||||
|
argArray[0]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
argArray[1] = encodeUrl(argArray[1]);
|
||||||
|
} else if (argArray[0] === "srcdoc") {
|
||||||
|
// TODO: this will rewrite with the wrong url in mind for iframes!!
|
||||||
|
argArray[1] = rewriteHtml(argArray[1]);
|
||||||
|
} else if (["srcset", "imagesrcset"].includes(argArray[0])) {
|
||||||
|
argArray[1] = rewriteSrcset(argArray[1]);
|
||||||
|
} else if (argArray[1] === "style") {
|
||||||
|
argArray[1] = rewriteCss(argArray[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Reflect.apply(target, thisArg, argArray);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const innerHTML = Object.getOwnPropertyDescriptor(
|
||||||
|
self.Element.prototype,
|
||||||
|
"innerHTML"
|
||||||
|
);
|
||||||
|
|
||||||
|
Object.defineProperty(self.Element.prototype, "innerHTML", {
|
||||||
|
set(value) {
|
||||||
|
if (this instanceof self.HTMLScriptElement) {
|
||||||
|
value = rewriteJs(value);
|
||||||
|
} else if (this instanceof self.HTMLStyleElement) {
|
||||||
|
value = rewriteCss(value);
|
||||||
|
} else {
|
||||||
|
value = rewriteHtml(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return innerHTML.set.call(this, value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const target of [
|
||||||
|
self.Node.prototype,
|
||||||
|
self.MutationObserver.prototype,
|
||||||
|
self.document,
|
||||||
|
]) {
|
||||||
|
for (const prop in target) {
|
||||||
|
try {
|
||||||
|
if (typeof target[prop] === "function") {
|
||||||
|
client.RawProxy(target, prop, {
|
||||||
|
apply(ctx) {
|
||||||
|
for (const i in ctx.args) {
|
||||||
|
if (ctx.args[i] === client.documentProxy)
|
||||||
|
ctx.args[i] = self.document;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/client/dom/history.ts
Normal file
16
src/client/dom/history.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
import { encodeUrl } from "../shared";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
|
client.Proxy("history.pushState", {
|
||||||
|
apply(ctx) {
|
||||||
|
ctx.args[2] = encodeUrl(ctx.args[2]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Proxy("history.replaceState", {
|
||||||
|
apply(ctx) {
|
||||||
|
ctx.args[2] = encodeUrl(ctx.args[2]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
40
src/client/dom/origin.ts
Normal file
40
src/client/dom/origin.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
import { decodeUrl } from "../shared";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
|
client.Trap("origin", {
|
||||||
|
get() {
|
||||||
|
return client.url.origin;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Trap("document.URL", {
|
||||||
|
get() {
|
||||||
|
return client.url;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Trap("document.baseURI", {
|
||||||
|
get() {
|
||||||
|
return decodeUrl(self.location.href);
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Trap("document.domain", {
|
||||||
|
get() {
|
||||||
|
return client.url.hostname;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
9
src/client/dom/postmessage.ts
Normal file
9
src/client/dom/postmessage.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
|
client.Proxy("window.postMessage", {
|
||||||
|
apply(ctx) {
|
||||||
|
if (typeof ctx.args[1] === "string") ctx.args[1] = "*";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { client } from ".";
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
if ("window" in self) {
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
const handler: ProxyHandler<Storage> = {
|
const handler: ProxyHandler<Storage> = {
|
||||||
get(target, prop) {
|
get(target, prop) {
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
|
@ -85,15 +85,15 @@ if ("window" in self) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const realLocalStorage = window.localStorage;
|
const realLocalStorage = self.localStorage;
|
||||||
const realSessionStorage = window.sessionStorage;
|
const realSessionStorage = self.sessionStorage;
|
||||||
|
|
||||||
const localStorageProxy = new Proxy(window.localStorage, handler);
|
const localStorageProxy = new Proxy(self.localStorage, handler);
|
||||||
const sessionStorageProxy = new Proxy(window.sessionStorage, handler);
|
const sessionStorageProxy = new Proxy(self.sessionStorage, handler);
|
||||||
|
|
||||||
delete window.localStorage;
|
delete self.localStorage;
|
||||||
delete window.sessionStorage;
|
delete self.sessionStorage;
|
||||||
|
|
||||||
window.localStorage = localStorageProxy;
|
self.localStorage = localStorageProxy;
|
||||||
window.sessionStorage = sessionStorageProxy;
|
self.sessionStorage = sessionStorageProxy;
|
||||||
}
|
}
|
8
src/client/dom/trustedTypes.ts
Normal file
8
src/client/dom/trustedTypes.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export default function (client, self) {
|
||||||
|
delete self.TrustedHTML;
|
||||||
|
delete self.TrustedScript;
|
||||||
|
delete self.TrustedScriptURL;
|
||||||
|
delete self.TrustedTypePolicy;
|
||||||
|
delete self.TrustedTypePolicyFactory;
|
||||||
|
delete self.trustedTypes;
|
||||||
|
}
|
|
@ -1,150 +0,0 @@
|
||||||
import { client } from ".";
|
|
||||||
import { decodeUrl } from "../shared/rewriters/url";
|
|
||||||
import {
|
|
||||||
encodeUrl,
|
|
||||||
rewriteCss,
|
|
||||||
rewriteHtml,
|
|
||||||
rewriteJs,
|
|
||||||
rewriteSrcset,
|
|
||||||
} from "./shared";
|
|
||||||
import { documentProxy } from "./window";
|
|
||||||
|
|
||||||
if ("window" in self) {
|
|
||||||
const attrObject = {
|
|
||||||
nonce: [HTMLElement],
|
|
||||||
integrity: [HTMLScriptElement, HTMLLinkElement],
|
|
||||||
csp: [HTMLIFrameElement],
|
|
||||||
src: [
|
|
||||||
HTMLImageElement,
|
|
||||||
HTMLMediaElement,
|
|
||||||
HTMLIFrameElement,
|
|
||||||
HTMLEmbedElement,
|
|
||||||
HTMLScriptElement,
|
|
||||||
],
|
|
||||||
href: [HTMLAnchorElement, HTMLLinkElement],
|
|
||||||
data: [HTMLObjectElement],
|
|
||||||
action: [HTMLFormElement],
|
|
||||||
formaction: [HTMLButtonElement, HTMLInputElement],
|
|
||||||
srcdoc: [HTMLIFrameElement],
|
|
||||||
srcset: [HTMLImageElement, HTMLSourceElement],
|
|
||||||
imagesrcset: [HTMLLinkElement],
|
|
||||||
};
|
|
||||||
|
|
||||||
const attrs = Object.keys(attrObject);
|
|
||||||
|
|
||||||
for (const attr of attrs) {
|
|
||||||
for (const element of attrObject[attr]) {
|
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(
|
|
||||||
element.prototype,
|
|
||||||
attr
|
|
||||||
);
|
|
||||||
Object.defineProperty(element.prototype, attr, {
|
|
||||||
get() {
|
|
||||||
if (["src", "data", "href", "action", "formaction"].includes(attr)) {
|
|
||||||
return decodeUrl(descriptor.get.call(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.$origattrs[attr]) {
|
|
||||||
return this.$origattrs[attr];
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptor.get.call(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
set(value) {
|
|
||||||
this.$origattrs[attr] = value;
|
|
||||||
|
|
||||||
if (["nonce", "integrity", "csp"].includes(attr)) {
|
|
||||||
return;
|
|
||||||
} else if (
|
|
||||||
["src", "data", "href", "action", "formaction"].includes(attr)
|
|
||||||
) {
|
|
||||||
value = encodeUrl(value);
|
|
||||||
} else if (attr === "srcdoc") {
|
|
||||||
value = rewriteHtml(value);
|
|
||||||
} else if (["srcset", "imagesrcset"].includes(attr)) {
|
|
||||||
value = rewriteSrcset(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.set.call(this, value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Element {
|
|
||||||
$origattrs: Record<string, string>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Element.prototype.$origattrs = {};
|
|
||||||
|
|
||||||
Element.prototype.getAttribute = new Proxy(Element.prototype.getAttribute, {
|
|
||||||
apply(target, thisArg, argArray) {
|
|
||||||
if (attrs.includes(argArray[0]) && thisArg.$origattrs[argArray[0]]) {
|
|
||||||
return thisArg.$origattrs[argArray[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.apply(target, thisArg, argArray);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Element.prototype.setAttribute = new Proxy(Element.prototype.setAttribute, {
|
|
||||||
apply(target, thisArg, argArray) {
|
|
||||||
if (attrs.includes(argArray[0])) {
|
|
||||||
thisArg.$origattrs[argArray[0]] = argArray[1];
|
|
||||||
if (["nonce", "integrity", "csp"].includes(argArray[0])) {
|
|
||||||
return;
|
|
||||||
} else if (
|
|
||||||
["src", "data", "href", "action", "formaction"].includes(argArray[0])
|
|
||||||
) {
|
|
||||||
argArray[1] = encodeUrl(argArray[1]);
|
|
||||||
} else if (argArray[0] === "srcdoc") {
|
|
||||||
argArray[1] = rewriteHtml(argArray[1]);
|
|
||||||
} else if (["srcset", "imagesrcset"].includes(argArray[0])) {
|
|
||||||
argArray[1] = rewriteSrcset(argArray[1]);
|
|
||||||
} else if (argArray[1] === "style") {
|
|
||||||
argArray[1] = rewriteCss(argArray[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.apply(target, thisArg, argArray);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const innerHTML = Object.getOwnPropertyDescriptor(
|
|
||||||
Element.prototype,
|
|
||||||
"innerHTML"
|
|
||||||
);
|
|
||||||
|
|
||||||
Object.defineProperty(Element.prototype, "innerHTML", {
|
|
||||||
set(value) {
|
|
||||||
if (this instanceof HTMLScriptElement) {
|
|
||||||
value = rewriteJs(value);
|
|
||||||
} else if (this instanceof HTMLStyleElement) {
|
|
||||||
value = rewriteCss(value);
|
|
||||||
} else {
|
|
||||||
value = rewriteHtml(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return innerHTML.set.call(this, value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const target of [Node.prototype, MutationObserver.prototype, document]) {
|
|
||||||
for (const prop in target) {
|
|
||||||
try {
|
|
||||||
if (typeof target[prop] === "function") {
|
|
||||||
client.RawProxy(target, prop, {
|
|
||||||
apply(ctx) {
|
|
||||||
for (const i in ctx.args) {
|
|
||||||
if (ctx.args[i] === documentProxy) ctx.args[i] = document;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
// idk what shit has to be done on here but it has to be done
|
|
||||||
// i'm going to temporarily disable rewriting if a MemberExpression detects addEventListener
|
|
||||||
|
|
||||||
// window.addEventListener = new Proxy(window.addEventListener, {
|
|
||||||
// apply (target, thisArg, argArray) {
|
|
||||||
// //
|
|
||||||
|
|
||||||
// return Reflect.apply(target, thisArg, argArray);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// window.addEventListener = new Proxy(window.addEventListener, {
|
|
||||||
// apply(target1, thisArg, argArray) {
|
|
||||||
// window.addEventListener(argArray[0], argArray[1]);
|
|
||||||
// },
|
|
||||||
// });
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { client } from ".";
|
|
||||||
import { encodeUrl } from "./shared";
|
|
||||||
|
|
||||||
client.Proxy("history.pushState", {
|
|
||||||
apply(ctx) {
|
|
||||||
ctx.args[2] = encodeUrl(ctx.args[2]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Proxy("history.replaceState", {
|
|
||||||
apply(ctx) {
|
|
||||||
ctx.args[2] = encodeUrl(ctx.args[2]);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { encodeUrl } from "../shared/rewriters/url";
|
|
||||||
|
|
||||||
self.$sImport = function (base) {
|
|
||||||
return function (url) {
|
|
||||||
const resolved = new URL(url, base).href;
|
|
||||||
|
|
||||||
return function () {}.constructor(
|
|
||||||
`return import("${encodeUrl(resolved)}")`
|
|
||||||
)();
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,202 +1,10 @@
|
||||||
import { decodeUrl } from "./shared";
|
// entrypoint for scramjet.client.js
|
||||||
|
|
||||||
declare global {
|
import { ScramjetClient } from "./client";
|
||||||
interface Window {
|
|
||||||
$s: any;
|
dbg.log("scrammin");
|
||||||
$tryset: any;
|
// if it already exists, that means the handlers have probably already been setup by the parent document
|
||||||
$sImport: any;
|
if (!(ScramjetClient.SCRAMJET in self)) {
|
||||||
}
|
const client = new ScramjetClient(self);
|
||||||
}
|
client.hook();
|
||||||
|
|
||||||
//eslint-disable-next-line
|
|
||||||
type AnyFunction = Function;
|
|
||||||
|
|
||||||
type ProxyCtx = {
|
|
||||||
fn: AnyFunction;
|
|
||||||
this: any;
|
|
||||||
args: any[];
|
|
||||||
newTarget: AnyFunction;
|
|
||||||
return: (r: any) => void;
|
|
||||||
};
|
|
||||||
type Proxy = {
|
|
||||||
construct?(ctx: ProxyCtx): any;
|
|
||||||
apply?(ctx: ProxyCtx): any;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TrapCtx<T> = {
|
|
||||||
this: any;
|
|
||||||
get: () => T;
|
|
||||||
set: (v: T) => void;
|
|
||||||
};
|
|
||||||
type Trap<T> = {
|
|
||||||
writable?: boolean;
|
|
||||||
value?: any;
|
|
||||||
enumerable?: boolean;
|
|
||||||
configurable?: boolean;
|
|
||||||
get?: (ctx: TrapCtx<T>) => T;
|
|
||||||
set?: (ctx: TrapCtx<T>, v: T) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScramjetClient {
|
|
||||||
Proxy(name: string | string[], handler: Proxy) {
|
|
||||||
if (Array.isArray(name)) {
|
|
||||||
for (const n of name) {
|
|
||||||
this.Proxy(n, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const split = name.split(".");
|
|
||||||
const prop = split.pop();
|
|
||||||
const target = split.reduce((a, b) => a?.[b], self);
|
|
||||||
this.RawProxy(target, prop, handler);
|
|
||||||
}
|
|
||||||
RawProxy(target: any, prop: string, handler: Proxy) {
|
|
||||||
if (!target) return;
|
|
||||||
if (!prop) return;
|
|
||||||
if (!Reflect.has(target, prop)) return;
|
|
||||||
|
|
||||||
const value = Reflect.get(target, prop);
|
|
||||||
delete target[prop];
|
|
||||||
|
|
||||||
const h: ProxyHandler<any> = {};
|
|
||||||
|
|
||||||
if (handler.construct) {
|
|
||||||
h.construct = function (
|
|
||||||
constructor: any,
|
|
||||||
argArray: any[],
|
|
||||||
newTarget: AnyFunction
|
|
||||||
) {
|
|
||||||
let returnValue: any = null;
|
|
||||||
|
|
||||||
const ctx: ProxyCtx = {
|
|
||||||
fn: constructor,
|
|
||||||
this: null,
|
|
||||||
args: argArray,
|
|
||||||
newTarget: newTarget,
|
|
||||||
return: (r: any) => {
|
|
||||||
returnValue = r;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
handler.construct(ctx);
|
|
||||||
|
|
||||||
if (returnValue) {
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.construct(ctx.fn, ctx.args, ctx.newTarget);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler.apply) {
|
|
||||||
h.apply = function (fn: any, thisArg: any, argArray: any[]) {
|
|
||||||
let returnValue: any = null;
|
|
||||||
|
|
||||||
const ctx: ProxyCtx = {
|
|
||||||
fn,
|
|
||||||
this: thisArg,
|
|
||||||
args: argArray,
|
|
||||||
newTarget: null,
|
|
||||||
return: (r: any) => {
|
|
||||||
returnValue = r;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
handler.apply(ctx);
|
|
||||||
|
|
||||||
if (returnValue) {
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.apply(ctx.fn, ctx.this, ctx.args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
target[prop] = new Proxy(value, h);
|
|
||||||
}
|
|
||||||
Trap<T>(name: string | string[], descriptor: Trap<T>) {
|
|
||||||
if (Array.isArray(name)) {
|
|
||||||
for (const n of name) {
|
|
||||||
this.Trap(n, descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const split = name.split(".");
|
|
||||||
const prop = split.pop();
|
|
||||||
const target = split.reduce((a, b) => a?.[b], self);
|
|
||||||
|
|
||||||
this.RawTrap(target, prop, descriptor);
|
|
||||||
}
|
|
||||||
RawTrap<T>(target: any, prop: string, descriptor: Trap<T>) {
|
|
||||||
if (!target) return;
|
|
||||||
if (!prop) return;
|
|
||||||
if (!Reflect.has(target, prop)) return;
|
|
||||||
|
|
||||||
const oldDescriptor = Object.getOwnPropertyDescriptor(target, prop);
|
|
||||||
|
|
||||||
const ctx: TrapCtx<T> = {
|
|
||||||
this: null,
|
|
||||||
get: function () {
|
|
||||||
return oldDescriptor && oldDescriptor.get.call(this.this);
|
|
||||||
},
|
|
||||||
set: function (v: T) {
|
|
||||||
oldDescriptor && oldDescriptor.set.call(this.this, v);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
delete target[prop];
|
|
||||||
|
|
||||||
const desc: PropertyDescriptor = {};
|
|
||||||
|
|
||||||
if (descriptor.get) {
|
|
||||||
desc.get = function () {
|
|
||||||
ctx.this = this;
|
|
||||||
|
|
||||||
return descriptor.get(ctx);
|
|
||||||
};
|
|
||||||
} else if (oldDescriptor?.get) {
|
|
||||||
desc.get = oldDescriptor.get;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.set) {
|
|
||||||
desc.set = function (v: T) {
|
|
||||||
ctx.this = this;
|
|
||||||
|
|
||||||
descriptor.set(ctx, v);
|
|
||||||
};
|
|
||||||
} else if (oldDescriptor?.set) {
|
|
||||||
desc.set = oldDescriptor.set;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.enumerable) desc.enumerable = descriptor.enumerable;
|
|
||||||
else if (oldDescriptor?.enumerable)
|
|
||||||
desc.enumerable = oldDescriptor.enumerable;
|
|
||||||
if (descriptor.configurable) desc.configurable = descriptor.configurable;
|
|
||||||
else if (oldDescriptor?.configurable)
|
|
||||||
desc.configurable = oldDescriptor.configurable;
|
|
||||||
|
|
||||||
Object.defineProperty(target, prop, desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
get url(): URL {
|
|
||||||
return new URL(decodeUrl(location.href));
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const client = new ScramjetClient();
|
|
||||||
client.init();
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const context = import.meta.webpackContext("./", {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const key of context.keys()) {
|
|
||||||
context(key);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
import { ScramjetClient } from "./client";
|
||||||
import { encodeUrl, decodeUrl } from "./shared";
|
import { encodeUrl, decodeUrl } from "./shared";
|
||||||
|
|
||||||
|
export function createLocationProxy(
|
||||||
|
client: ScramjetClient,
|
||||||
|
self: typeof globalThis
|
||||||
|
) {
|
||||||
function createLocation() {
|
function createLocation() {
|
||||||
const loc = new URL(decodeUrl(location.href));
|
const loc = new URL(client.url.href);
|
||||||
loc.assign = (url: string) => location.assign(encodeUrl(url));
|
|
||||||
loc.reload = () => location.reload();
|
loc.assign = (url: string) => self.location.assign(encodeUrl(url));
|
||||||
loc.replace = (url: string) => location.replace(encodeUrl(url));
|
loc.reload = () => self.location.reload();
|
||||||
|
loc.replace = (url: string) => self.location.replace(encodeUrl(url));
|
||||||
loc.toString = () => loc.href;
|
loc.toString = () => loc.href;
|
||||||
|
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const locationProxy = new Proxy(
|
return new Proxy(
|
||||||
{
|
{
|
||||||
host: "",
|
host: "",
|
||||||
},
|
},
|
||||||
|
@ -26,7 +32,7 @@ export const locationProxy = new Proxy(
|
||||||
const loc = createLocation();
|
const loc = createLocation();
|
||||||
|
|
||||||
if (prop === "href") {
|
if (prop === "href") {
|
||||||
location.href = encodeUrl(value);
|
self.location.href = encodeUrl(value);
|
||||||
} else {
|
} else {
|
||||||
loc[prop] = value;
|
loc[prop] = value;
|
||||||
}
|
}
|
||||||
|
@ -35,3 +41,4 @@ export const locationProxy = new Proxy(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
import { rewriteJs } from "../shared";
|
|
||||||
|
|
||||||
const FunctionProxy = new Proxy(Function, {
|
|
||||||
construct(target, argArray) {
|
|
||||||
if (argArray.length === 1) {
|
|
||||||
return Reflect.construct(target, rewriteJs(argArray[0]));
|
|
||||||
} else {
|
|
||||||
return Reflect.construct(
|
|
||||||
target,
|
|
||||||
rewriteJs(argArray[argArray.length - 1])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
apply(target, thisArg, argArray) {
|
|
||||||
if (argArray.length === 1) {
|
|
||||||
return Reflect.apply(target, undefined, [rewriteJs(argArray[0])]);
|
|
||||||
} else {
|
|
||||||
return Reflect.apply(target, undefined, [
|
|
||||||
...argArray.map((x, index) => index === argArray.length - 1),
|
|
||||||
rewriteJs(argArray[argArray.length - 1]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
delete window.Function;
|
|
||||||
|
|
||||||
window.Function = FunctionProxy;
|
|
||||||
|
|
||||||
window.eval = new Proxy(window.eval, {
|
|
||||||
apply(target, thisArg, argArray) {
|
|
||||||
return Reflect.apply(target, thisArg, [rewriteJs(argArray[0])]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
*/
|
|
|
@ -1,38 +0,0 @@
|
||||||
import { client } from ".";
|
|
||||||
import { decodeUrl } from "../shared/rewriters/url";
|
|
||||||
|
|
||||||
client.Trap("origin", {
|
|
||||||
get() {
|
|
||||||
return client.url.origin;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Trap("document.URL", {
|
|
||||||
get() {
|
|
||||||
return client.url;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Trap("document.baseURI", {
|
|
||||||
get() {
|
|
||||||
return decodeUrl(location.href);
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Trap("document.domain", {
|
|
||||||
get() {
|
|
||||||
return client.url.hostname;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { client } from ".";
|
|
||||||
|
|
||||||
client.Proxy("window.postMessage", {
|
|
||||||
apply(ctx) {
|
|
||||||
if (typeof ctx.args[1] === "string") ctx.args[1] = "*";
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,28 +0,0 @@
|
||||||
// ts throws an error if you dont do window.fetch
|
|
||||||
|
|
||||||
import { client } from "..";
|
|
||||||
import { encodeUrl, rewriteHeaders } from "../shared";
|
|
||||||
|
|
||||||
client.Proxy("fetch", {
|
|
||||||
apply(ctx) {
|
|
||||||
ctx.args[0] = encodeUrl(ctx.args[0]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Proxy("Headers", {
|
|
||||||
construct(ctx) {
|
|
||||||
ctx.args[0] = rewriteHeaders(ctx.args[0]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Proxy("Request", {
|
|
||||||
construct(ctx) {
|
|
||||||
if (typeof ctx.args[0] === "string") ctx.args[0] = encodeUrl(ctx.args[0]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Proxy("Response.redirect", {
|
|
||||||
apply(ctx) {
|
|
||||||
ctx.args[0] = encodeUrl(ctx.args[0]);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { client } from "..";
|
|
||||||
import { encodeUrl, rewriteHeaders } from "../shared";
|
|
||||||
|
|
||||||
client.Proxy("XMLHttpRequest.prototype.open", {
|
|
||||||
apply(ctx) {
|
|
||||||
if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Proxy("XMLHttpRequest.prototype.setRequestHeader", {
|
|
||||||
apply(ctx) {
|
|
||||||
let headerObject = Object.fromEntries([ctx.args]);
|
|
||||||
headerObject = rewriteHeaders(headerObject);
|
|
||||||
|
|
||||||
ctx.args = Object.entries(headerObject)[0];
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { locationProxy } from "./location";
|
|
||||||
import { documentProxy, windowProxy } from "./window";
|
|
||||||
|
|
||||||
export const iswindow = "window" in self;
|
|
||||||
export const isworker = "WorkerGlobalScope" in self;
|
|
||||||
export const issw = "ServiceWorkerGlobalScope" in self;
|
|
||||||
export const isdedicated = "DedicatedWorkerGlobalScope" in self;
|
|
||||||
export const isshared = "SharedWorkerGlobalScope" in self;
|
|
||||||
|
|
||||||
function scope(identifier: any) {
|
|
||||||
// this will break iframe postmessage!
|
|
||||||
if (
|
|
||||||
iswindow &&
|
|
||||||
(identifier instanceof Window ||
|
|
||||||
identifier instanceof top.window.Window ||
|
|
||||||
identifier instanceof parent.window.Window)
|
|
||||||
) {
|
|
||||||
return windowProxy;
|
|
||||||
} else if (
|
|
||||||
(iswindow && identifier instanceof Location) ||
|
|
||||||
(isworker && identifier instanceof WorkerLocation)
|
|
||||||
) {
|
|
||||||
return locationProxy;
|
|
||||||
} else if (iswindow && identifier instanceof Document) {
|
|
||||||
return documentProxy;
|
|
||||||
} else if (isworker && identifier instanceof WorkerGlobalScope) {
|
|
||||||
return windowProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
return identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
// shorthand because this can get out of hand reall quickly
|
|
||||||
self.$s = scope;
|
|
||||||
|
|
||||||
self.$tryset = function (lhs: any, op: string, rhs: any) {
|
|
||||||
if (lhs instanceof Location) {
|
|
||||||
// @ts-ignore
|
|
||||||
locationProxy.href = rhs;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
18
src/client/shared/apply.ts
Normal file
18
src/client/shared/apply.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
|
// do we need this?
|
||||||
|
client.Proxy(
|
||||||
|
[
|
||||||
|
"Function.prototype.call",
|
||||||
|
"Function.prototype.bind",
|
||||||
|
"Function.prototype.apply",
|
||||||
|
],
|
||||||
|
{
|
||||||
|
apply(ctx) {
|
||||||
|
if (ctx.args[0] === client.windowProxy) ctx.args[0] = self;
|
||||||
|
if (ctx.args[0] === client.documentProxy) ctx.args[0] = self.document;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
38
src/client/shared/eval.ts
Normal file
38
src/client/shared/eval.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
export default function (client, self) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
import { rewriteJs } from "../shared";
|
||||||
|
|
||||||
|
const FunctionProxy = new Proxy(Function, {
|
||||||
|
construct(target, argArray) {
|
||||||
|
if (argArray.length === 1) {
|
||||||
|
return Reflect.construct(target, rewriteJs(argArray[0]));
|
||||||
|
} else {
|
||||||
|
return Reflect.construct(
|
||||||
|
target,
|
||||||
|
rewriteJs(argArray[argArray.length - 1])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apply(target, thisArg, argArray) {
|
||||||
|
if (argArray.length === 1) {
|
||||||
|
return Reflect.apply(target, undefined, [rewriteJs(argArray[0])]);
|
||||||
|
} else {
|
||||||
|
return Reflect.apply(target, undefined, [
|
||||||
|
...argArray.map((x, index) => index === argArray.length - 1),
|
||||||
|
rewriteJs(argArray[argArray.length - 1]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
delete window.Function;
|
||||||
|
|
||||||
|
window.Function = FunctionProxy;
|
||||||
|
|
||||||
|
window.eval = new Proxy(window.eval, {
|
||||||
|
apply(target, thisArg, argArray) {
|
||||||
|
return Reflect.apply(target, thisArg, [rewriteJs(argArray[0])]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
*/
|
13
src/client/shared/import.ts
Normal file
13
src/client/shared/import.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { encodeUrl } from "../shared";
|
||||||
|
|
||||||
|
export default function (client, self) {
|
||||||
|
self.$sImport = function (base) {
|
||||||
|
return function (url) {
|
||||||
|
const resolved = new URL(url, base).href;
|
||||||
|
|
||||||
|
return function () {}.constructor(
|
||||||
|
`return import("${encodeUrl(resolved)}")`
|
||||||
|
)();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
8
src/client/shared/requests/beacon.ts
Normal file
8
src/client/shared/requests/beacon.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export default function (client, self) {
|
||||||
|
// goodybye spyware~
|
||||||
|
client.Proxy("navigator.sendBeacon", {
|
||||||
|
apply(ctx) {
|
||||||
|
ctx.return(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
29
src/client/shared/requests/fetch.ts
Normal file
29
src/client/shared/requests/fetch.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// ts throws an error if you dont do window.fetch
|
||||||
|
|
||||||
|
import { encodeUrl, rewriteHeaders } from "../../shared";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
|
client.Proxy("fetch", {
|
||||||
|
apply(ctx) {
|
||||||
|
ctx.args[0] = encodeUrl(ctx.args[0]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Proxy("Headers", {
|
||||||
|
construct(ctx) {
|
||||||
|
ctx.args[0] = rewriteHeaders(ctx.args[0]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Proxy("Request", {
|
||||||
|
construct(ctx) {
|
||||||
|
if (typeof ctx.args[0] === "string") ctx.args[0] = encodeUrl(ctx.args[0]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Proxy("Response.redirect", {
|
||||||
|
apply(ctx) {
|
||||||
|
ctx.args[0] = encodeUrl(ctx.args[0]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { client } from "..";
|
import { ScramjetClient } from "../../client";
|
||||||
import { BareClient } from "../shared";
|
import { BareClient } from "../../shared";
|
||||||
|
|
||||||
if ("window" in self) {
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
const bare = new BareClient();
|
const bare = new BareClient();
|
||||||
|
|
||||||
client.Proxy("WebSocket", {
|
client.Proxy("WebSocket", {
|
||||||
|
@ -12,7 +12,7 @@ if ("window" in self) {
|
||||||
ctx.args[1],
|
ctx.args[1],
|
||||||
ctx.fn as typeof WebSocket,
|
ctx.fn as typeof WebSocket,
|
||||||
{
|
{
|
||||||
"User-Agent": navigator.userAgent,
|
"User-Agent": self.navigator.userAgent,
|
||||||
Origin: client.url.origin,
|
Origin: client.url.origin,
|
||||||
},
|
},
|
||||||
ArrayBuffer.prototype
|
ArrayBuffer.prototype
|
18
src/client/shared/requests/xmlhttprequest.ts
Normal file
18
src/client/shared/requests/xmlhttprequest.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { encodeUrl, rewriteHeaders } from "../../shared";
|
||||||
|
|
||||||
|
export default function (client, self) {
|
||||||
|
client.Proxy("XMLHttpRequest.prototype.open", {
|
||||||
|
apply(ctx) {
|
||||||
|
if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
client.Proxy("XMLHttpRequest.prototype.setRequestHeader", {
|
||||||
|
apply(ctx) {
|
||||||
|
let headerObject = Object.fromEntries([ctx.args]);
|
||||||
|
headerObject = rewriteHeaders(headerObject);
|
||||||
|
|
||||||
|
ctx.args = Object.entries(headerObject)[0];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
44
src/client/shared/scope.ts
Normal file
44
src/client/shared/scope.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
|
export const iswindow = "window" in self;
|
||||||
|
export const isworker = "WorkerGlobalScope" in self;
|
||||||
|
export const issw = "ServiceWorkerGlobalScope" in self;
|
||||||
|
export const isdedicated = "DedicatedWorkerGlobalScope" in self;
|
||||||
|
export const isshared = "SharedWorkerGlobalScope" in self;
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
|
function scope(identifier: any) {
|
||||||
|
// this will break iframe postmessage!
|
||||||
|
if (
|
||||||
|
iswindow &&
|
||||||
|
(identifier instanceof self.Window ||
|
||||||
|
identifier instanceof self.top.window.Window ||
|
||||||
|
identifier instanceof self.parent.window.Window)
|
||||||
|
) {
|
||||||
|
return client.windowProxy;
|
||||||
|
} else if (
|
||||||
|
(iswindow && identifier instanceof Location) ||
|
||||||
|
(isworker && identifier instanceof WorkerLocation)
|
||||||
|
) {
|
||||||
|
return client.locationProxy;
|
||||||
|
} else if (iswindow && identifier instanceof Document) {
|
||||||
|
return client.documentProxy;
|
||||||
|
} else if (isworker && identifier instanceof WorkerGlobalScope) {
|
||||||
|
return client.windowProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shorthand because this can get out of hand reall quickly
|
||||||
|
self.$s = scope;
|
||||||
|
|
||||||
|
self.$tryset = function (lhs: any, op: string, rhs: any) {
|
||||||
|
if (lhs instanceof Location) {
|
||||||
|
// @ts-ignore
|
||||||
|
locationProxy.href = rhs;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
20
src/client/shared/worker.ts
Normal file
20
src/client/shared/worker.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { encodeUrl } from "../../shared/rewriters/url";
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
|
client.Proxy("Worker", {
|
||||||
|
construct({ args }) {
|
||||||
|
if (args[0] instanceof URL) args[0] = args[0].href;
|
||||||
|
if (args[0].startsWith("blob:") || args[0].startsWith("data:")) {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args[0] = encodeUrl(args[0]) + "?dest=worker";
|
||||||
|
|
||||||
|
if (args[1] && args[1].type === "module") {
|
||||||
|
args[0] += "&type=module";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
// import { rewriteHtml, rewriteJs, encodeUrl } from "./shared";
|
|
||||||
|
|
||||||
// trustedTypes.createPolicy = new Proxy(trustedTypes.createPolicy, {
|
|
||||||
// apply(target, thisArg, argArray) {
|
|
||||||
// if (argArray[1].createHTML) {
|
|
||||||
// argArray[1].createHTML = new Proxy(argArray[1].createHTML, {
|
|
||||||
// apply(target1, thisArg1, argArray1) {
|
|
||||||
// return rewriteHtml(target1(...argArray1));
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (argArray[1].createScript) {
|
|
||||||
// argArray[1].createScript = new Proxy(argArray[1].createScript, {
|
|
||||||
// apply(target1, thisArg1, argArray1) {
|
|
||||||
// return rewriteJs(target1(...argArray1));
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (argArray[1].createScriptURL) {
|
|
||||||
// argArray[1].createScriptURL = new Proxy(argArray[1].createScriptURL, {
|
|
||||||
// apply(target1, thisArg1, argArray1) {
|
|
||||||
// return encodeUrl(target1(...argArray1));
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return Reflect.apply(target, thisArg, argArray);
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
if ("window" in self) {
|
|
||||||
//@ts-nocheck
|
|
||||||
delete window.TrustedHTML;
|
|
||||||
delete window.TrustedScript;
|
|
||||||
delete window.TrustedScriptURL;
|
|
||||||
delete window.TrustedTypePolicy;
|
|
||||||
delete window.TrustedTypePolicyFactory;
|
|
||||||
delete window.trustedTypes;
|
|
||||||
}
|
|
|
@ -1,17 +1,20 @@
|
||||||
import { client } from ".";
|
|
||||||
import { encodeUrl } from "../shared/rewriters/url";
|
import { encodeUrl } from "../shared/rewriters/url";
|
||||||
import { locationProxy } from "./location";
|
import { ScramjetClient } from "./client";
|
||||||
|
|
||||||
export const windowProxy = new Proxy(self, {
|
export function createWindowProxy(
|
||||||
|
client: ScramjetClient,
|
||||||
|
self: typeof globalThis
|
||||||
|
): typeof globalThis {
|
||||||
|
return new Proxy(self, {
|
||||||
get(target, prop) {
|
get(target, prop) {
|
||||||
const propIsString = typeof prop === "string";
|
const propIsString = typeof prop === "string";
|
||||||
if (propIsString && prop === "location") {
|
if (propIsString && prop === "location") {
|
||||||
return locationProxy;
|
return client.locationProxy;
|
||||||
} else if (
|
} else if (
|
||||||
propIsString &&
|
propIsString &&
|
||||||
["window", "top", "self", "globalThis"].includes(prop)
|
["window", "top", "self", "globalThis"].includes(prop)
|
||||||
) {
|
) {
|
||||||
return windowProxy;
|
return client.windowProxy;
|
||||||
} else if (propIsString && prop == "parent") {
|
} else if (propIsString && prop == "parent") {
|
||||||
return self.parent;
|
return self.parent;
|
||||||
} else if (propIsString && prop === "$scramjet") {
|
} else if (propIsString && prop === "$scramjet") {
|
||||||
|
@ -54,13 +57,18 @@ export const windowProxy = new Proxy(self, {
|
||||||
return Reflect.defineProperty(target, property, attributes);
|
return Reflect.defineProperty(target, property, attributes);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const documentProxy = new Proxy(self.document || {}, {
|
export function createDocumentProxy(
|
||||||
|
client: ScramjetClient,
|
||||||
|
self: typeof globalThis
|
||||||
|
) {
|
||||||
|
return new Proxy(self.document || {}, {
|
||||||
get(target, prop) {
|
get(target, prop) {
|
||||||
const propIsString = typeof prop === "string";
|
const propIsString = typeof prop === "string";
|
||||||
|
|
||||||
if (propIsString && prop === "location") {
|
if (propIsString && prop === "location") {
|
||||||
return locationProxy;
|
return client.locationProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = Reflect.get(target, prop);
|
const value = Reflect.get(target, prop);
|
||||||
|
@ -86,17 +94,4 @@ export const documentProxy = new Proxy(self.document || {}, {
|
||||||
return Reflect.set(target, prop, newValue);
|
return Reflect.set(target, prop, newValue);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
client.Proxy(
|
|
||||||
[
|
|
||||||
"Function.prototype.call",
|
|
||||||
"Function.prototype.bind",
|
|
||||||
"Function.prototype.apply",
|
|
||||||
],
|
|
||||||
{
|
|
||||||
apply(ctx) {
|
|
||||||
if (ctx.args[0] === windowProxy) ctx.args[0] = window;
|
|
||||||
if (ctx.args[0] === documentProxy) ctx.args[0] = document;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
import { client } from ".";
|
|
||||||
import { encodeUrl } from "./shared";
|
|
||||||
|
|
||||||
client.Proxy("Worker", {
|
|
||||||
construct({ args }) {
|
|
||||||
if (args[0] instanceof URL) args[0] = args[0].href;
|
|
||||||
if (args[0].startsWith("blob:") || args[0].startsWith("data:")) {
|
|
||||||
// TODO
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args[0] = encodeUrl(args[0]) + "?dest=worker";
|
|
||||||
|
|
||||||
if (args[1] && args[1].type === "module") {
|
|
||||||
args[0] += "&type=module";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if ("window" in self) {
|
|
||||||
Worklet.prototype.addModule = new Proxy(Worklet.prototype.addModule, {
|
|
||||||
apply(target, thisArg, argArray) {
|
|
||||||
argArray[0] = encodeUrl(argArray[0]);
|
|
||||||
|
|
||||||
return Reflect.apply(target, thisArg, argArray);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
client.Proxy("navigator.serviceWorker.register", {
|
|
||||||
apply(ctx) {
|
|
||||||
if (ctx.args[0] instanceof URL) ctx.args[0] = ctx.args[0].href;
|
|
||||||
let url = encodeUrl(ctx.args[0]) + "?dest=serviceworker";
|
|
||||||
if (ctx.args[1] && ctx.args[1].type === "module") {
|
|
||||||
url += "&type=module";
|
|
||||||
}
|
|
||||||
let worker = new SharedWorker(url);
|
|
||||||
|
|
||||||
let handle = worker.port;
|
|
||||||
|
|
||||||
navigator.serviceWorker.controller.postMessage({
|
|
||||||
scramjet$type: "registerServiceWorker",
|
|
||||||
port: handle,
|
|
||||||
});
|
|
||||||
|
|
||||||
const fakeRegistration = new Proxy(
|
|
||||||
{
|
|
||||||
__proto__: ServiceWorkerRegistration.prototype,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
get(target, prop) {
|
|
||||||
if (prop === "installing") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (prop === "waiting") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (prop === "active") {
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
if (prop === "scope") {
|
|
||||||
return ctx.args[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Reflect.get(target, prop);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.return(new Promise((resolve) => resolve(fakeRegistration)));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Proxy("importScripts", {
|
|
||||||
apply(ctx) {
|
|
||||||
for (const i in ctx.args) {
|
|
||||||
ctx.args[i] = encodeUrl(ctx.args[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
61
src/client/worker/worker.ts
Normal file
61
src/client/worker/worker.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// if ("window" in self) {
|
||||||
|
// Worklet.prototype.addModule = new Proxy(Worklet.prototype.addModule, {
|
||||||
|
// apply(target, thisArg, argArray) {
|
||||||
|
// argArray[0] = encodeUrl(argArray[0]);
|
||||||
|
//
|
||||||
|
// return Reflect.apply(target, thisArg, argArray);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// client.Proxy("navigator.serviceWorker.register", {
|
||||||
|
// apply(ctx) {
|
||||||
|
// if (ctx.args[0] instanceof URL) ctx.args[0] = ctx.args[0].href;
|
||||||
|
// let url = encodeUrl(ctx.args[0]) + "?dest=serviceworker";
|
||||||
|
// if (ctx.args[1] && ctx.args[1].type === "module") {
|
||||||
|
// url += "&type=module";
|
||||||
|
// }
|
||||||
|
// let worker = new SharedWorker(url);
|
||||||
|
//
|
||||||
|
// let handle = worker.port;
|
||||||
|
//
|
||||||
|
// navigator.serviceWorker.controller.postMessage({
|
||||||
|
// scramjet$type: "registerServiceWorker",
|
||||||
|
// port: handle,
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// const fakeRegistration = new Proxy(
|
||||||
|
// {
|
||||||
|
// __proto__: ServiceWorkerRegistration.prototype,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// get(target, prop) {
|
||||||
|
// if (prop === "installing") {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// if (prop === "waiting") {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// if (prop === "active") {
|
||||||
|
// return handle;
|
||||||
|
// }
|
||||||
|
// if (prop === "scope") {
|
||||||
|
// return ctx.args[0];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return Reflect.get(target, prop);
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// ctx.return(new Promise((resolve) => resolve(fakeRegistration)));
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// client.Proxy("importScripts", {
|
||||||
|
// apply(ctx) {
|
||||||
|
// for (const i in ctx.args) {
|
||||||
|
// ctx.args[i] = encodeUrl(ctx.args[i]);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// });
|
|
@ -4,7 +4,7 @@ if (!self.$scramjet) {
|
||||||
}
|
}
|
||||||
self.$scramjet.config = {
|
self.$scramjet.config = {
|
||||||
prefix: "/scramjet/",
|
prefix: "/scramjet/",
|
||||||
codec: self.$scramjet.codecs.base64,
|
codec: self.$scramjet.codecs.plain,
|
||||||
config: "/scram/scramjet.config.js",
|
config: "/scram/scramjet.config.js",
|
||||||
shared: "/scram/scramjet.shared.js",
|
shared: "/scram/scramjet.shared.js",
|
||||||
worker: "/scram/scramjet.worker.js",
|
worker: "/scram/scramjet.worker.js",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue