mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-12 22:10:01 -04:00
support url base
This commit is contained in:
parent
a1ce4e33b3
commit
7a9c990b01
32 changed files with 213 additions and 158 deletions
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "../shared";
|
} from "../shared";
|
||||||
import { createWrapFn } from "./shared/wrap";
|
import { createWrapFn } from "./shared/wrap";
|
||||||
import { NavigateEvent } from "./events";
|
import { NavigateEvent } from "./events";
|
||||||
|
import type { URLMeta } from "../shared/rewriters/url";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -78,6 +79,8 @@ export class ScramjetClient {
|
||||||
]
|
]
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
|
||||||
|
meta: URLMeta;
|
||||||
|
|
||||||
constructor(public global: typeof globalThis) {
|
constructor(public global: typeof globalThis) {
|
||||||
this.serviceWorker = this.global.navigator.serviceWorker;
|
this.serviceWorker = this.global.navigator.serviceWorker;
|
||||||
|
|
||||||
|
@ -103,6 +106,27 @@ export class ScramjetClient {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let baseurl: URL;
|
||||||
|
if (iswindow) {
|
||||||
|
// setup base url
|
||||||
|
// base url can only be updated at document load time and it will affect all urls resolved by encodeurl/rewriteurl
|
||||||
|
const base = this.global.document.querySelector("base");
|
||||||
|
if (base) {
|
||||||
|
baseurl = new URL(decodeUrl(base.href));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = this;
|
||||||
|
this.meta = {
|
||||||
|
get origin() {
|
||||||
|
return client.url;
|
||||||
|
},
|
||||||
|
get base() {
|
||||||
|
return baseurl || client.url;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
global[SCRAMJETCLIENT] = this;
|
global[SCRAMJETCLIENT] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +193,7 @@ export class ScramjetClient {
|
||||||
}
|
}
|
||||||
if (ev.defaultPrevented) return;
|
if (ev.defaultPrevented) return;
|
||||||
|
|
||||||
self.location.href = encodeUrl(ev.url);
|
self.location.href = encodeUrl(ev.url, this.meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// below are the utilities for proxying and trapping dom APIs
|
// below are the utilities for proxying and trapping dom APIs
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function createDocumentProxy(
|
||||||
},
|
},
|
||||||
set(target, prop, newValue) {
|
set(target, prop, newValue) {
|
||||||
if (prop === "location") {
|
if (prop === "location") {
|
||||||
location.href = encodeUrl(newValue);
|
location.href = encodeUrl(newValue, client.meta);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ 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], client.meta);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,11 +63,11 @@ export default function (client: ScramjetClient, self: typeof window) {
|
||||||
} else if (
|
} else if (
|
||||||
["src", "data", "href", "action", "formaction"].includes(attr)
|
["src", "data", "href", "action", "formaction"].includes(attr)
|
||||||
) {
|
) {
|
||||||
value = encodeUrl(value);
|
value = encodeUrl(value, client.meta);
|
||||||
} else if (attr === "srcdoc") {
|
} else if (attr === "srcdoc") {
|
||||||
value = rewriteHtml(value, client.cookieStore, undefined, true);
|
value = rewriteHtml(value, client.cookieStore, undefined, true);
|
||||||
} else if (["srcset", "imagesrcset"].includes(attr)) {
|
} else if (["srcset", "imagesrcset"].includes(attr)) {
|
||||||
value = rewriteSrcset(value);
|
value = rewriteSrcset(value, client.meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor.set.call(this, value);
|
descriptor.set.call(this, value);
|
||||||
|
@ -106,6 +106,7 @@ export default function (client: ScramjetClient, self: typeof window) {
|
||||||
|
|
||||||
client.Trap("Node.prototype.baseURI", {
|
client.Trap("Node.prototype.baseURI", {
|
||||||
get() {
|
get() {
|
||||||
|
// TODO this should be using ownerdocument but who gaf
|
||||||
const base = self.document.querySelector("base");
|
const base = self.document.querySelector("base");
|
||||||
if (base) {
|
if (base) {
|
||||||
return new URL(base.href, client.url).href;
|
return new URL(base.href, client.url).href;
|
||||||
|
@ -132,7 +133,7 @@ export default function (client: ScramjetClient, self: typeof window) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rule) {
|
if (rule) {
|
||||||
ctx.args[1] = rule.fn(value, client.url, client.cookieStore);
|
ctx.args[1] = rule.fn(value, client.meta, client.cookieStore);
|
||||||
ctx.fn.call(ctx.this, `data-scramjet-${ctx.args[0]}`, value);
|
ctx.fn.call(ctx.this, `data-scramjet-${ctx.args[0]}`, value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -152,11 +153,11 @@ export default function (client: ScramjetClient, self: typeof window) {
|
||||||
set(ctx, value: string) {
|
set(ctx, value: string) {
|
||||||
let newval;
|
let newval;
|
||||||
if (ctx.this instanceof self.HTMLScriptElement) {
|
if (ctx.this instanceof self.HTMLScriptElement) {
|
||||||
newval = rewriteJs(value, client.url);
|
newval = rewriteJs(value, client.meta);
|
||||||
} else if (ctx.this instanceof self.HTMLStyleElement) {
|
} else if (ctx.this instanceof self.HTMLStyleElement) {
|
||||||
newval = rewriteCss(value, client.url);
|
newval = rewriteCss(value, client.meta);
|
||||||
} else {
|
} else {
|
||||||
newval = rewriteHtml(value, client.cookieStore, client.url);
|
newval = rewriteHtml(value, client.cookieStore, client.meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.set(newval);
|
ctx.set(newval);
|
||||||
|
@ -168,7 +169,7 @@ export default function (client: ScramjetClient, self: typeof window) {
|
||||||
|
|
||||||
client.Trap("Element.prototype.outerHTML", {
|
client.Trap("Element.prototype.outerHTML", {
|
||||||
set(ctx, value: string) {
|
set(ctx, value: string) {
|
||||||
ctx.set(rewriteHtml(value, client.cookieStore, client.url));
|
ctx.set(rewriteHtml(value, client.cookieStore, client.meta));
|
||||||
},
|
},
|
||||||
get(ctx) {
|
get(ctx) {
|
||||||
return unrewriteHtml(ctx.get());
|
return unrewriteHtml(ctx.get());
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { decodeUrl, rewriteCss } from "../../shared";
|
||||||
export default function (client: ScramjetClient, self: typeof window) {
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
client.Proxy("FontFace", {
|
client.Proxy("FontFace", {
|
||||||
construct(ctx) {
|
construct(ctx) {
|
||||||
ctx.args[1] = rewriteCss(ctx.args[1]);
|
ctx.args[1] = rewriteCss(ctx.args[1], client.meta);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { UrlChangeEvent } from "../events";
|
||||||
export default function (client: ScramjetClient, self: typeof globalThis) {
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
client.Proxy("history.pushState", {
|
client.Proxy("history.pushState", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
ctx.args[2] = encodeUrl(ctx.args[2]);
|
ctx.args[2] = encodeUrl(ctx.args[2], client.meta);
|
||||||
ctx.call();
|
ctx.call();
|
||||||
|
|
||||||
const ev = new UrlChangeEvent(client.url.href);
|
const ev = new UrlChangeEvent(client.url.href);
|
||||||
|
@ -15,7 +15,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
|
|
||||||
client.Proxy("history.replaceState", {
|
client.Proxy("history.replaceState", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
ctx.args[2] = encodeUrl(ctx.args[2]);
|
ctx.args[2] = encodeUrl(ctx.args[2], client.meta);
|
||||||
ctx.call();
|
ctx.call();
|
||||||
|
|
||||||
const ev = new UrlChangeEvent(client.url.href);
|
const ev = new UrlChangeEvent(client.url.href);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { SCRAMJETCLIENT } from "../../symbols";
|
||||||
export default function (client: ScramjetClient) {
|
export default function (client: ScramjetClient) {
|
||||||
client.Proxy("window.open", {
|
client.Proxy("window.open", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
if (ctx.args[0]) ctx.args[0] = encodeUrl(ctx.args[0]);
|
if (ctx.args[0]) ctx.args[0] = encodeUrl(ctx.args[0], client.meta);
|
||||||
|
|
||||||
if (["_parent", "_top", "_unfencedTop"].includes(ctx.args[1]))
|
if (["_parent", "_top", "_unfencedTop"].includes(ctx.args[1]))
|
||||||
ctx.args[1] = "_self";
|
ctx.args[1] = "_self";
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { decodeUrl } from "../../shared";
|
||||||
export default function (client: ScramjetClient, self: typeof window) {
|
export default function (client: ScramjetClient, self: typeof window) {
|
||||||
client.Trap("origin", {
|
client.Trap("origin", {
|
||||||
get() {
|
get() {
|
||||||
|
// this isn't right!!
|
||||||
return client.url.origin;
|
return client.url.origin;
|
||||||
},
|
},
|
||||||
set() {
|
set() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default function (client: ScramjetClient, self: Self) {
|
||||||
|
|
||||||
client.Proxy("Worklet.prototype.addModule", {
|
client.Proxy("Worklet.prototype.addModule", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
ctx.args[0] = encodeUrl(ctx.args[0]);
|
ctx.args[0] = encodeUrl(ctx.args[0], client.meta);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export default function (client: ScramjetClient, self: Self) {
|
||||||
client.Proxy("navigator.serviceWorker.register", {
|
client.Proxy("navigator.serviceWorker.register", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
if (ctx.args[0] instanceof URL) ctx.args[0] = ctx.args[0].href;
|
if (ctx.args[0] instanceof URL) ctx.args[0] = ctx.args[0].href;
|
||||||
let url = encodeUrl(ctx.args[0]) + "?dest=serviceworker";
|
let url = encodeUrl(ctx.args[0], client.meta) + "?dest=serviceworker";
|
||||||
if (ctx.args[1] && ctx.args[1].type === "module") {
|
if (ctx.args[1] && ctx.args[1].type === "module") {
|
||||||
url += "&type=module";
|
url += "&type=module";
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ export default function (client: ScramjetClient, self: typeof window) {
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (prop in Object.prototype) {
|
if (prop in Object.prototype || typeof prop === "symbol") {
|
||||||
return Reflect.get(target, prop);
|
return Reflect.get(target, prop);
|
||||||
}
|
}
|
||||||
console.log("GET", prop, target == realLocalStorage);
|
console.log("GET", prop, target == realLocalStorage);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { ScramjetClient } from "./client";
|
import { ScramjetClient } from "./client";
|
||||||
import { nativeGetOwnPropertyDescriptor } from "./natives";
|
import { nativeGetOwnPropertyDescriptor } from "./natives";
|
||||||
import { encodeUrl, decodeUrl } from "../shared";
|
import { decodeUrl, encodeUrl } from "../shared";
|
||||||
import { iswindow } from ".";
|
import { iswindow } from ".";
|
||||||
|
|
||||||
export function createLocationProxy(
|
export function createLocationProxy(
|
||||||
|
@ -76,7 +76,7 @@ export function createLocationProxy(
|
||||||
if (self.location.assign)
|
if (self.location.assign)
|
||||||
fakeLocation.assign = new Proxy(self.location.assign, {
|
fakeLocation.assign = new Proxy(self.location.assign, {
|
||||||
apply(target, thisArg, args) {
|
apply(target, thisArg, args) {
|
||||||
args[0] = encodeUrl(args[0]);
|
args[0] = encodeUrl(args[0], client.meta);
|
||||||
Reflect.apply(target, self.location, args);
|
Reflect.apply(target, self.location, args);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -89,7 +89,7 @@ export function createLocationProxy(
|
||||||
if (self.location.replace)
|
if (self.location.replace)
|
||||||
fakeLocation.replace = new Proxy(self.location.replace, {
|
fakeLocation.replace = new Proxy(self.location.replace, {
|
||||||
apply(target, thisArg, args) {
|
apply(target, thisArg, args) {
|
||||||
args[0] = encodeUrl(args[0]);
|
args[0] = encodeUrl(args[0], client.meta);
|
||||||
Reflect.apply(target, self.location, args);
|
Reflect.apply(target, self.location, args);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,25 +2,34 @@ import { config } from "../../shared";
|
||||||
import { ScramjetClient } from "../client";
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
export const enabled = () => config.flags.captureErrors;
|
export const enabled = () => config.flags.captureErrors;
|
||||||
|
export function argdbg(arg, recurse = []) {
|
||||||
|
switch (typeof arg) {
|
||||||
|
case "string":
|
||||||
|
if (arg.includes("localhost:1337/scramjet/") && arg.includes("m3u8"))
|
||||||
|
debugger;
|
||||||
|
break;
|
||||||
|
case "object":
|
||||||
|
// if (arg instanceof Location) debugger;
|
||||||
|
if (
|
||||||
|
arg &&
|
||||||
|
arg[Symbol.iterator] &&
|
||||||
|
typeof arg[Symbol.iterator] === "function"
|
||||||
|
)
|
||||||
|
for (let prop in arg) {
|
||||||
|
// make sure it's not a getter
|
||||||
|
let desc = Object.getOwnPropertyDescriptor(arg, prop);
|
||||||
|
if (desc && desc.get) continue;
|
||||||
|
|
||||||
|
const ar = arg[prop];
|
||||||
|
if (recurse.includes(ar)) continue;
|
||||||
|
recurse.push(ar);
|
||||||
|
argdbg(ar, recurse);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function (client: ScramjetClient, self: typeof globalThis) {
|
export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
function argdbg(arg) {
|
|
||||||
switch (typeof arg) {
|
|
||||||
case "string":
|
|
||||||
if (arg.includes("scramjet") && !arg.includes("\n")) debugger;
|
|
||||||
break;
|
|
||||||
case "object":
|
|
||||||
if (arg instanceof Location) debugger;
|
|
||||||
if (
|
|
||||||
arg &&
|
|
||||||
arg[Symbol.iterator] &&
|
|
||||||
typeof arg[Symbol.iterator] === "function"
|
|
||||||
)
|
|
||||||
for (let ar of arg) argdbg(ar);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.$scramerr = function scramerr(e) {
|
self.$scramerr = function scramerr(e) {
|
||||||
console.warn("CAUGHT ERROR", e);
|
console.warn("CAUGHT ERROR", e);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default function (client: ScramjetClient, self: Self) {
|
||||||
value: function (js: any) {
|
value: function (js: any) {
|
||||||
if (typeof js !== "string") return js;
|
if (typeof js !== "string") return js;
|
||||||
|
|
||||||
const rewritten = rewriteJs(js, client.url);
|
const rewritten = rewriteJs(js, client.meta);
|
||||||
|
|
||||||
return rewritten;
|
return rewritten;
|
||||||
},
|
},
|
||||||
|
@ -23,5 +23,5 @@ export function indirectEval(this: ScramjetClient, js: any) {
|
||||||
|
|
||||||
const indirection = this.global.eval;
|
const indirection = this.global.eval;
|
||||||
|
|
||||||
return indirection(rewriteJs(js, this.url) as string);
|
return indirection(rewriteJs(js, this.meta) as string);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { ScramjetClient, ProxyCtx, Proxy } from "../client";
|
import { ScramjetClient, ProxyCtx, Proxy } from "../client";
|
||||||
import { rewriteJs } from "../../shared";
|
import { rewriteJs } from "../../shared";
|
||||||
|
|
||||||
function rewriteFunction(ctx: ProxyCtx) {
|
function rewriteFunction(ctx: ProxyCtx, client: ScramjetClient) {
|
||||||
const stringifiedFunction = ctx.call().toString();
|
const stringifiedFunction = ctx.call().toString();
|
||||||
|
|
||||||
ctx.return(ctx.fn(`return ${rewriteJs(stringifiedFunction)}`)());
|
ctx.return(ctx.fn(`return ${rewriteJs(stringifiedFunction, client.meta)}`)());
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (client: ScramjetClient, self: Self) {
|
export default function (client: ScramjetClient, self: Self) {
|
||||||
const handler: Proxy = {
|
const handler: Proxy = {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
rewriteFunction(ctx);
|
rewriteFunction(ctx, client);
|
||||||
},
|
},
|
||||||
construct(ctx) {
|
construct(ctx) {
|
||||||
rewriteFunction(ctx);
|
rewriteFunction(ctx, client);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ScramjetClient } from "../client";
|
import { ScramjetClient } from "../client";
|
||||||
import { config, encodeUrl } from "../../shared";
|
import { config } from "../../shared";
|
||||||
|
import { encodeUrl } 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.Function;
|
||||||
|
@ -8,7 +9,7 @@ export default function (client: ScramjetClient, self: Self) {
|
||||||
return function (url: string) {
|
return function (url: string) {
|
||||||
const resolved = new URL(url, base).href;
|
const resolved = new URL(url, base).href;
|
||||||
|
|
||||||
return Function(`return import("${encodeUrl(resolved)}")`)();
|
return Function(`return import("${encodeUrl(resolved, client.meta)}")`)();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
client.Proxy("fetch", {
|
client.Proxy("fetch", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) {
|
if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) {
|
||||||
ctx.args[0] = encodeUrl(ctx.args[0].toString());
|
ctx.args[0] = encodeUrl(ctx.args[0].toString(), client.meta);
|
||||||
|
|
||||||
if (isemulatedsw) ctx.args[0] += "?from=swruntime";
|
if (isemulatedsw) ctx.args[0] += "?from=swruntime";
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
client.Proxy("Request", {
|
client.Proxy("Request", {
|
||||||
construct(ctx) {
|
construct(ctx) {
|
||||||
if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) {
|
if (typeof ctx.args[0] === "string" || ctx.args[0] instanceof URL) {
|
||||||
ctx.args[0] = encodeUrl(ctx.args[0].toString());
|
ctx.args[0] = encodeUrl(ctx.args[0].toString(), client.meta);
|
||||||
|
|
||||||
if (isemulatedsw) ctx.args[0] += "?from=swruntime";
|
if (isemulatedsw) ctx.args[0] += "?from=swruntime";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import { decodeUrl, encodeUrl, rewriteHeaders } from "../../../shared";
|
import { decodeUrl, encodeUrl, rewriteHeaders } from "../../../shared";
|
||||||
|
import { ScramjetClient } from "../../client";
|
||||||
|
|
||||||
export default function (client, self) {
|
export default function (client: ScramjetClient, self: Self) {
|
||||||
client.Proxy("XMLHttpRequest.prototype.open", {
|
client.Proxy("XMLHttpRequest.prototype.open", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1]);
|
if (ctx.args[1]) ctx.args[1] = encodeUrl(ctx.args[1], client.meta);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
client.Proxy("XMLHttpRequest.prototype.setRequestHeader", {
|
client.Proxy("XMLHttpRequest.prototype.setRequestHeader", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
let headerObject = Object.fromEntries([ctx.args]);
|
let headerObject = Object.fromEntries([ctx.args]);
|
||||||
headerObject = rewriteHeaders(headerObject);
|
headerObject = rewriteHeaders(headerObject, client.meta);
|
||||||
|
|
||||||
ctx.args = Object.entries(headerObject)[0];
|
ctx.args = Object.entries(headerObject)[0];
|
||||||
},
|
},
|
||||||
|
@ -18,7 +19,7 @@ export default function (client, self) {
|
||||||
|
|
||||||
client.Trap("XMLHttpRequest.prototype.responseURL", {
|
client.Trap("XMLHttpRequest.prototype.responseURL", {
|
||||||
get(ctx) {
|
get(ctx) {
|
||||||
return decodeUrl(ctx.get());
|
return decodeUrl(ctx.get() as string);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { encodeUrl, BareMuxConnection } from "../../shared";
|
import { BareMuxConnection } from "../../shared";
|
||||||
|
import { encodeUrl } from "../../shared/rewriters/url";
|
||||||
import type { MessageC2W } from "../../worker";
|
import type { MessageC2W } from "../../worker";
|
||||||
import { ScramjetClient } from "../client";
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) {
|
||||||
id,
|
id,
|
||||||
} as MessageC2W);
|
} as MessageC2W);
|
||||||
} else {
|
} else {
|
||||||
args[0] = encodeUrl(args[0]) + "?dest=worker";
|
args[0] = encodeUrl(args[0], client.meta) + "?dest=worker";
|
||||||
|
|
||||||
if (args[1] && args[1].type === "module") {
|
if (args[1] && args[1].type === "module") {
|
||||||
args[0] += "&type=module";
|
args[0] += "&type=module";
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { iswindow, isworker } from "..";
|
||||||
import { SCRAMJETCLIENT } from "../../symbols";
|
import { SCRAMJETCLIENT } from "../../symbols";
|
||||||
import { ScramjetClient } from "../client";
|
import { ScramjetClient } from "../client";
|
||||||
import { config } from "../../shared";
|
import { config } from "../../shared";
|
||||||
|
import { argdbg } from "./err";
|
||||||
|
|
||||||
export function createWrapFn(client: ScramjetClient, self: typeof globalThis) {
|
export function createWrapFn(client: ScramjetClient, self: typeof globalThis) {
|
||||||
return function (identifier: any, args: any) {
|
return function (identifier: any, args: any) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ScramjetClient } from "./client";
|
import { ScramjetClient } from "./client";
|
||||||
import { decodeUrl, encodeUrl } from "../shared";
|
import { decodeUrl } from "../shared";
|
||||||
|
|
||||||
export class ScramjetServiceWorkerRuntime {
|
export class ScramjetServiceWorkerRuntime {
|
||||||
recvport: MessagePort;
|
recvport: MessagePort;
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { encodeUrl } from "../../shared";
|
import { encodeUrl } from "../../shared";
|
||||||
|
import { ScramjetClient } from "../client";
|
||||||
|
|
||||||
export default function (client, self) {
|
export default function (client: ScramjetClient, self: Self) {
|
||||||
client.Proxy("importScripts", {
|
client.Proxy("importScripts", {
|
||||||
apply(ctx) {
|
apply(ctx) {
|
||||||
for (const i in ctx.args) {
|
for (const i in ctx.args) {
|
||||||
ctx.args[i] = encodeUrl(ctx.args[i]);
|
ctx.args[i] = encodeUrl(ctx.args[i], client.meta);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class ScramjetController {
|
||||||
client: "/scramjet.client.js",
|
client: "/scramjet.client.js",
|
||||||
codecs: "/scramjet.codecs.js",
|
codecs: "/scramjet.codecs.js",
|
||||||
flags: {
|
flags: {
|
||||||
serviceworkers: true,
|
serviceworkers: false,
|
||||||
naiiveRewriter: false,
|
naiiveRewriter: false,
|
||||||
captureErrors: false,
|
captureErrors: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// thnank you node unblocker guy
|
||||||
import parse from "set-cookie-parser";
|
import parse from "set-cookie-parser";
|
||||||
|
|
||||||
export type Cookie = {
|
export type Cookie = {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// This CSS rewriter uses code from Meteor
|
// This CSS rewriter uses code from Meteor
|
||||||
// You can find the original source code at https://github.com/MeteorProxy/Meteor
|
// You can find the original source code at https://github.com/MeteorProxy/Meteor
|
||||||
|
|
||||||
import { encodeUrl } from "./url";
|
import { URLMeta, encodeUrl } from "./url";
|
||||||
|
|
||||||
export function rewriteCss(css: string, origin?: URL) {
|
export function rewriteCss(css: string, meta: URLMeta) {
|
||||||
const regex =
|
const regex =
|
||||||
/(@import\s+(?!url\())?\s*url\(\s*(['"]?)([^'")]+)\2\s*\)|@import\s+(['"])([^'"]+)\4/g;
|
/(@import\s+(?!url\())?\s*url\(\s*(['"]?)([^'")]+)\2\s*\)|@import\s+(['"])([^'"]+)\4/g;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export function rewriteCss(css: string, origin?: URL) {
|
||||||
importContent
|
importContent
|
||||||
) => {
|
) => {
|
||||||
const url = urlContent || importContent;
|
const url = urlContent || importContent;
|
||||||
const encodedUrl = encodeUrl(url.trim(), origin);
|
const encodedUrl = encodeUrl(url.trim(), meta);
|
||||||
|
|
||||||
if (importStatement) {
|
if (importStatement) {
|
||||||
return `@import url(${urlQuote}${encodedUrl}${urlQuote})`;
|
return `@import url(${urlQuote}${encodedUrl}${urlQuote})`;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { encodeUrl } from "./url";
|
// TODO this whole file should be inlined and deleted it's a weird relic from ssd era
|
||||||
|
|
||||||
|
import { URLMeta, encodeUrl } from "./url";
|
||||||
import { BareHeaders } from "@mercuryworkshop/bare-mux";
|
import { BareHeaders } from "@mercuryworkshop/bare-mux";
|
||||||
const cspHeaders = [
|
const cspHeaders = [
|
||||||
"cross-origin-embedder-policy",
|
"cross-origin-embedder-policy",
|
||||||
|
@ -24,11 +26,11 @@ const cspHeaders = [
|
||||||
|
|
||||||
const urlHeaders = ["location", "content-location", "referer"];
|
const urlHeaders = ["location", "content-location", "referer"];
|
||||||
|
|
||||||
function rewriteLinkHeader(link: string, origin?: URL) {
|
function rewriteLinkHeader(link: string, meta: URLMeta) {
|
||||||
return link.replace(/<(.*)>/gi, (match) => encodeUrl(match, origin));
|
return link.replace(/<(.*)>/gi, (match) => encodeUrl(match, meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rewriteHeaders(rawHeaders: BareHeaders, origin?: URL) {
|
export function rewriteHeaders(rawHeaders: BareHeaders, meta: URLMeta) {
|
||||||
const headers = {};
|
const headers = {};
|
||||||
|
|
||||||
for (const key in rawHeaders) {
|
for (const key in rawHeaders) {
|
||||||
|
@ -41,17 +43,14 @@ export function rewriteHeaders(rawHeaders: BareHeaders, origin?: URL) {
|
||||||
|
|
||||||
urlHeaders.forEach((header) => {
|
urlHeaders.forEach((header) => {
|
||||||
if (headers[header])
|
if (headers[header])
|
||||||
headers[header] = encodeUrl(
|
headers[header] = encodeUrl(headers[header]?.toString() as string, meta);
|
||||||
headers[header]?.toString() as string,
|
|
||||||
origin
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof headers["link"] === "string") {
|
if (typeof headers["link"] === "string") {
|
||||||
headers["link"] = rewriteLinkHeader(headers["link"], origin);
|
headers["link"] = rewriteLinkHeader(headers["link"], meta);
|
||||||
} else if (Array.isArray(headers["link"])) {
|
} else if (Array.isArray(headers["link"])) {
|
||||||
headers["link"] = headers["link"].map((link) =>
|
headers["link"] = headers["link"].map((link) =>
|
||||||
rewriteLinkHeader(link, origin)
|
rewriteLinkHeader(link, meta)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ElementType, Parser } from "htmlparser2";
|
import { ElementType, Parser } from "htmlparser2";
|
||||||
import { ChildNode, DomHandler, Element, Node, Text } from "domhandler";
|
import { ChildNode, DomHandler, Element, Node, Text } from "domhandler";
|
||||||
import render from "dom-serializer";
|
import render from "dom-serializer";
|
||||||
import { encodeUrl } from "./url";
|
import { URLMeta, encodeUrl } from "./url";
|
||||||
import { rewriteCss } from "./css";
|
import { rewriteCss } from "./css";
|
||||||
import { rewriteJs } from "./js";
|
import { rewriteJs } from "./js";
|
||||||
import { CookieStore } from "../cookie";
|
import { CookieStore } from "../cookie";
|
||||||
|
@ -9,7 +9,7 @@ import { CookieStore } from "../cookie";
|
||||||
export function rewriteHtml(
|
export function rewriteHtml(
|
||||||
html: string,
|
html: string,
|
||||||
cookieStore: CookieStore,
|
cookieStore: CookieStore,
|
||||||
origin?: URL,
|
meta: URLMeta,
|
||||||
fromTop: boolean = false
|
fromTop: boolean = false
|
||||||
) {
|
) {
|
||||||
const handler = new DomHandler((err, dom) => dom);
|
const handler = new DomHandler((err, dom) => dom);
|
||||||
|
@ -17,7 +17,7 @@ export function rewriteHtml(
|
||||||
|
|
||||||
parser.write(html);
|
parser.write(html);
|
||||||
parser.end();
|
parser.end();
|
||||||
traverseParsedHtml(handler.root, cookieStore, origin);
|
traverseParsedHtml(handler.root, cookieStore, meta);
|
||||||
|
|
||||||
function findhead(node) {
|
function findhead(node) {
|
||||||
if (node.type === ElementType.Tag && node.name === "head") {
|
if (node.type === ElementType.Tag && node.name === "head") {
|
||||||
|
@ -62,6 +62,11 @@ export function rewriteHtml(
|
||||||
return render(handler.root);
|
return render(handler.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ParseState = {
|
||||||
|
base: string;
|
||||||
|
origin?: URL;
|
||||||
|
};
|
||||||
|
|
||||||
export function unrewriteHtml(html: string) {
|
export function unrewriteHtml(html: string) {
|
||||||
const handler = new DomHandler((err, dom) => dom);
|
const handler = new DomHandler((err, dom) => dom);
|
||||||
const parser = new Parser(handler);
|
const parser = new Parser(handler);
|
||||||
|
@ -93,17 +98,11 @@ export function unrewriteHtml(html: string) {
|
||||||
|
|
||||||
export const htmlRules: {
|
export const htmlRules: {
|
||||||
[key: string]: "*" | string[] | Function;
|
[key: string]: "*" | string[] | Function;
|
||||||
fn: (
|
fn: (value: string, meta: URLMeta, cookieStore: CookieStore) => string | null;
|
||||||
value: string,
|
|
||||||
origin: URL | null,
|
|
||||||
cookieStore: CookieStore
|
|
||||||
) => string | null;
|
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
fn: (value: string, origin: URL) => {
|
fn: (value: string, meta: URLMeta) => {
|
||||||
if (["_parent", "_top", "_unfencedTop"].includes(value)) return "_self";
|
return encodeUrl(value, meta);
|
||||||
|
|
||||||
return encodeUrl(value, origin);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// url rewrites
|
// url rewrites
|
||||||
|
@ -133,34 +132,51 @@ export const htmlRules: {
|
||||||
csp: ["iframe"],
|
csp: ["iframe"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (value: string, origin?: URL) => rewriteSrcset(value, origin),
|
fn: (value: string, meta: URLMeta) => rewriteSrcset(value, meta),
|
||||||
|
|
||||||
// srcset
|
// srcset
|
||||||
srcset: ["img", "source"],
|
srcset: ["img", "source"],
|
||||||
imagesrcset: ["link"],
|
imagesrcset: ["link"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (value: string, origin: URL, cookieStore: CookieStore) =>
|
fn: (value: string, meta: URLMeta, cookieStore: CookieStore) =>
|
||||||
rewriteHtml(value, cookieStore, origin, true),
|
rewriteHtml(
|
||||||
|
value,
|
||||||
|
cookieStore,
|
||||||
|
{
|
||||||
|
// for srcdoc origin is the origin of the page that the iframe is on. base and path get dropped
|
||||||
|
origin: new URL(meta.origin.origin),
|
||||||
|
base: new URL(meta.origin.origin),
|
||||||
|
},
|
||||||
|
true
|
||||||
|
),
|
||||||
|
|
||||||
// srcdoc
|
// srcdoc
|
||||||
srcdoc: ["iframe"],
|
srcdoc: ["iframe"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (value: string, origin?: URL) => rewriteCss(value, origin),
|
fn: (value: string, meta: URLMeta) => rewriteCss(value, meta),
|
||||||
style: "*",
|
style: "*",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fn: (value: string) => {
|
fn: (value: string) => {
|
||||||
if (["_parent", "_top", "_unfencedTop"].includes(value)) return "_self";
|
if (["_parent", "_top", "_unfencedTop"].includes(value)) return "_self";
|
||||||
},
|
},
|
||||||
target: ["a"],
|
target: ["a", "base"],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// i need to add the attributes in during rewriting
|
// i need to add the attributes in during rewriting
|
||||||
|
|
||||||
function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
function traverseParsedHtml(
|
||||||
|
node: any,
|
||||||
|
cookieStore: CookieStore,
|
||||||
|
meta: URLMeta
|
||||||
|
) {
|
||||||
|
if (node.name === "base" && node.attribs.href !== undefined) {
|
||||||
|
meta.base = new URL(node.attribs.href, meta.origin);
|
||||||
|
}
|
||||||
|
|
||||||
if (node.attribs)
|
if (node.attribs)
|
||||||
for (const rule of htmlRules) {
|
for (const rule of htmlRules) {
|
||||||
for (const attr in rule) {
|
for (const attr in rule) {
|
||||||
|
@ -170,7 +186,7 @@ function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
||||||
if (sel === "*" || sel.includes(node.name)) {
|
if (sel === "*" || sel.includes(node.name)) {
|
||||||
if (node.attribs[attr] !== undefined) {
|
if (node.attribs[attr] !== undefined) {
|
||||||
const value = node.attribs[attr];
|
const value = node.attribs[attr];
|
||||||
let v = rule.fn(value, origin, cookieStore);
|
let v = rule.fn(value, meta, cookieStore);
|
||||||
|
|
||||||
if (v === null) delete node.attribs[attr];
|
if (v === null) delete node.attribs[attr];
|
||||||
else {
|
else {
|
||||||
|
@ -183,7 +199,7 @@ function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.name === "style" && node.children[0] !== undefined)
|
if (node.name === "style" && node.children[0] !== undefined)
|
||||||
node.children[0].data = rewriteCss(node.children[0].data, origin);
|
node.children[0].data = rewriteCss(node.children[0].data, meta);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
node.name === "script" &&
|
node.name === "script" &&
|
||||||
|
@ -195,7 +211,7 @@ function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
||||||
let js = node.children[0].data;
|
let js = node.children[0].data;
|
||||||
const htmlcomment = /<!--[\s\S]*?-->/g;
|
const htmlcomment = /<!--[\s\S]*?-->/g;
|
||||||
js = js.replace(htmlcomment, "");
|
js = js.replace(htmlcomment, "");
|
||||||
node.children[0].data = rewriteJs(js, origin);
|
node.children[0].data = rewriteJs(js, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.name === "meta" && node.attribs["http-equiv"] != undefined) {
|
if (node.name === "meta" && node.attribs["http-equiv"] != undefined) {
|
||||||
|
@ -209,7 +225,7 @@ function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
||||||
) {
|
) {
|
||||||
const contentArray = node.attribs.content.split("url=");
|
const contentArray = node.attribs.content.split("url=");
|
||||||
if (contentArray[1])
|
if (contentArray[1])
|
||||||
contentArray[1] = encodeUrl(contentArray[1].trim(), origin);
|
contentArray[1] = encodeUrl(contentArray[1].trim(), meta);
|
||||||
node.attribs.content = contentArray.join("url=");
|
node.attribs.content = contentArray.join("url=");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,7 +235,7 @@ function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
||||||
node.childNodes[childNode] = traverseParsedHtml(
|
node.childNodes[childNode] = traverseParsedHtml(
|
||||||
node.childNodes[childNode],
|
node.childNodes[childNode],
|
||||||
cookieStore,
|
cookieStore,
|
||||||
origin
|
meta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,14 +243,14 @@ function traverseParsedHtml(node: any, cookieStore: CookieStore, origin?: URL) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rewriteSrcset(srcset: string, origin?: URL) {
|
export function rewriteSrcset(srcset: string, meta: URLMeta) {
|
||||||
const urls = srcset.split(/ [0-9]+x,? ?/g);
|
const urls = srcset.split(/ [0-9]+x,? ?/g);
|
||||||
if (!urls) return "";
|
if (!urls) return "";
|
||||||
const sufixes = srcset.match(/ [0-9]+x,? ?/g);
|
const sufixes = srcset.match(/ [0-9]+x,? ?/g);
|
||||||
if (!sufixes) return "";
|
if (!sufixes) return "";
|
||||||
const rewrittenUrls = urls.map((url, i) => {
|
const rewrittenUrls = urls.map((url, i) => {
|
||||||
if (url && sufixes[i]) {
|
if (url && sufixes[i]) {
|
||||||
return encodeUrl(url, origin) + sufixes[i];
|
return encodeUrl(url, meta) + sufixes[i];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { decodeUrl } from "./url";
|
import { URLMeta, decodeUrl } from "./url";
|
||||||
|
|
||||||
// i am a cat. i like to be petted. i like to be fed. i like to be
|
// i am a cat. i like to be petted. i like to be fed. i like to be
|
||||||
import {
|
import {
|
||||||
|
@ -18,25 +18,21 @@ init();
|
||||||
|
|
||||||
Error.stackTraceLimit = 50;
|
Error.stackTraceLimit = 50;
|
||||||
|
|
||||||
global.rws = rewriteJs;
|
export function rewriteJs(js: string | ArrayBuffer, meta: URLMeta) {
|
||||||
export function rewriteJs(js: string | ArrayBuffer, origin?: URL) {
|
|
||||||
if ("window" in globalThis)
|
|
||||||
origin = origin ?? new URL(decodeUrl(location.href));
|
|
||||||
|
|
||||||
if (self.$scramjet.config.flags.naiiveRewriter) {
|
if (self.$scramjet.config.flags.naiiveRewriter) {
|
||||||
const text = typeof js === "string" ? js : new TextDecoder().decode(js);
|
const text = typeof js === "string" ? js : new TextDecoder().decode(js);
|
||||||
return rewriteJsNaiive(text, origin);
|
return rewriteJsNaiive(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = performance.now();
|
const before = performance.now();
|
||||||
if (typeof js === "string") {
|
if (typeof js === "string") {
|
||||||
js = new TextDecoder().decode(
|
js = new TextDecoder().decode(
|
||||||
rewrite_js(js, origin.toString(), self.$scramjet)
|
rewrite_js(js, meta.base.href, self.$scramjet)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
js = rewrite_js_from_arraybuffer(
|
js = rewrite_js_from_arraybuffer(
|
||||||
new Uint8Array(js),
|
new Uint8Array(js),
|
||||||
origin.toString(),
|
meta.base.href,
|
||||||
self.$scramjet
|
self.$scramjet
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -53,10 +49,7 @@ export function rewriteJs(js: string | ArrayBuffer, origin?: URL) {
|
||||||
// 4. i think the global state can get clobbered somehow
|
// 4. i think the global state can get clobbered somehow
|
||||||
//
|
//
|
||||||
// if you can ensure all the preconditions are met this is faster than full rewrites
|
// if you can ensure all the preconditions are met this is faster than full rewrites
|
||||||
export function rewriteJsNaiive(js: string | ArrayBuffer, origin?: URL) {
|
export function rewriteJsNaiive(js: string | ArrayBuffer) {
|
||||||
if ("window" in globalThis)
|
|
||||||
origin = origin ?? new URL(decodeUrl(location.href));
|
|
||||||
|
|
||||||
if (typeof js !== "string") {
|
if (typeof js !== "string") {
|
||||||
js = new TextDecoder().decode(js);
|
js = new TextDecoder().decode(js);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import { rewriteJs } from "./js";
|
import { rewriteJs } from "./js";
|
||||||
|
|
||||||
|
export type URLMeta = {
|
||||||
|
origin: URL;
|
||||||
|
base: URL;
|
||||||
|
};
|
||||||
|
|
||||||
function tryCanParseURL(url: string, origin?: string | URL): URL | null {
|
function tryCanParseURL(url: string, origin?: string | URL): URL | null {
|
||||||
try {
|
try {
|
||||||
return new URL(url, origin);
|
return new URL(url, origin);
|
||||||
|
@ -9,36 +14,34 @@ function tryCanParseURL(url: string, origin?: string | URL): URL | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
// something is broken with this but i didn't debug it
|
// something is broken with this but i didn't debug it
|
||||||
export function encodeUrl(url: string | URL, origin?: URL) {
|
export function encodeUrl(url: string | URL, meta: URLMeta) {
|
||||||
if (url instanceof URL) {
|
if (url instanceof URL) {
|
||||||
url = url.href;
|
url = url.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!origin) {
|
// if (!origin) {
|
||||||
if (location.pathname.startsWith(self.$scramjet.config.prefix + "worker")) {
|
// if (location.pathname.startsWith(self.$scramjet.config.prefix + "worker")) {
|
||||||
origin = new URL(new URL(location.href).searchParams.get("origin"));
|
// origin = new URL(new URL(location.href).searchParams.get("origin"));
|
||||||
} else
|
// } else
|
||||||
origin = new URL(
|
// origin = new URL(
|
||||||
self.$scramjet.codec.decode(
|
// self.$scramjet.codec.decode(
|
||||||
location.href.slice(
|
// location.href.slice(
|
||||||
(location.origin + self.$scramjet.config.prefix).length
|
// (location.origin + self.$scramjet.config.prefix).length
|
||||||
)
|
// )
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
// is this the correct behavior?
|
|
||||||
if (!url) url = origin.href;
|
|
||||||
|
|
||||||
if (url.startsWith("javascript:")) {
|
if (url.startsWith("javascript:")) {
|
||||||
return "javascript:" + rewriteJs(url.slice("javascript:".length), origin);
|
return "javascript:" + rewriteJs(url.slice("javascript:".length), meta);
|
||||||
} else if (/^(#|mailto|about|data|blob)/.test(url)) {
|
} else if (/^(#|mailto|about|data|blob)/.test(url)) {
|
||||||
|
// TODO this regex is jank but i'm not fixing it
|
||||||
return url;
|
return url;
|
||||||
} else if (tryCanParseURL(url, origin)) {
|
} else {
|
||||||
return (
|
return (
|
||||||
location.origin +
|
location.origin +
|
||||||
self.$scramjet.config.prefix +
|
self.$scramjet.config.prefix +
|
||||||
self.$scramjet.codec.encode(new URL(url, origin).href)
|
self.$scramjet.codec.encode(new URL(url, meta.base).href)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { rewriteJs } from "./js";
|
import { rewriteJs } from "./js";
|
||||||
|
import { URLMeta } from "./url";
|
||||||
|
|
||||||
const clientscripts = ["wasm", "shared", "client"];
|
const clientscripts = ["wasm", "shared", "client"];
|
||||||
export function rewriteWorkers(
|
export function rewriteWorkers(
|
||||||
js: string | ArrayBuffer,
|
js: string | ArrayBuffer,
|
||||||
type: string,
|
type: string,
|
||||||
origin?: URL
|
meta: URLMeta
|
||||||
) {
|
) {
|
||||||
origin.search = "";
|
|
||||||
|
|
||||||
let str = "";
|
let str = "";
|
||||||
|
|
||||||
str += `self.$scramjet = {}; self.$scramjet.config = ${JSON.stringify(self.$scramjet.config)};
|
str += `self.$scramjet = {}; self.$scramjet.config = ${JSON.stringify(self.$scramjet.config)};
|
||||||
|
@ -29,7 +28,7 @@ self.$scramjet.codec = self.$scramjet.codecs[self.$scramjet.config.codec];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rewritten = rewriteJs(js, origin);
|
let rewritten = rewriteJs(js, meta);
|
||||||
if (rewritten instanceof Uint8Array) {
|
if (rewritten instanceof Uint8Array) {
|
||||||
rewritten = new TextDecoder().decode(rewritten);
|
rewritten = new TextDecoder().decode(rewritten);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,15 @@ import {
|
||||||
rewriteWorkers,
|
rewriteWorkers,
|
||||||
} from "../shared";
|
} from "../shared";
|
||||||
|
|
||||||
|
import type { URLMeta } from "../shared/rewriters/url";
|
||||||
|
|
||||||
|
function newmeta(url: URL): URLMeta {
|
||||||
|
return {
|
||||||
|
origin: url,
|
||||||
|
base: url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function swfetch(
|
export async function swfetch(
|
||||||
this: ScramjetServiceWorker,
|
this: ScramjetServiceWorker,
|
||||||
request: Request,
|
request: Request,
|
||||||
|
@ -25,7 +34,7 @@ export async function swfetch(
|
||||||
|
|
||||||
if (urlParam.has("url")) {
|
if (urlParam.has("url")) {
|
||||||
return Response.redirect(
|
return Response.redirect(
|
||||||
encodeUrl(urlParam.get("url"), new URL(urlParam.get("url")))
|
encodeUrl(urlParam.get("url"), newmeta(new URL(urlParam.get("url"))))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +133,7 @@ async function handleResponse(
|
||||||
client: Client
|
client: Client
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
let responseBody: string | ArrayBuffer | ReadableStream;
|
let responseBody: string | ArrayBuffer | ReadableStream;
|
||||||
const responseHeaders = rewriteHeaders(response.rawHeaders, url);
|
const responseHeaders = rewriteHeaders(response.rawHeaders, newmeta(url));
|
||||||
|
|
||||||
let maybeHeaders = responseHeaders["set-cookie"] || [];
|
let maybeHeaders = responseHeaders["set-cookie"] || [];
|
||||||
for (const cookie in maybeHeaders) {
|
for (const cookie in maybeHeaders) {
|
||||||
|
@ -155,7 +164,7 @@ async function handleResponse(
|
||||||
responseBody = rewriteHtml(
|
responseBody = rewriteHtml(
|
||||||
await response.text(),
|
await response.text(),
|
||||||
cookieStore,
|
cookieStore,
|
||||||
url,
|
newmeta(url),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,19 +172,19 @@ async function handleResponse(
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "script":
|
case "script":
|
||||||
responseBody = rewriteJs(await response.arrayBuffer(), url);
|
responseBody = rewriteJs(await response.arrayBuffer(), newmeta(url));
|
||||||
// Disable threading for now, it's causing issues.
|
// Disable threading for now, it's causing issues.
|
||||||
// responseBody = await this.threadpool.rewriteJs(await responseBody.arrayBuffer(), url.toString());
|
// responseBody = await this.threadpool.rewriteJs(await responseBody.arrayBuffer(), url.toString());
|
||||||
break;
|
break;
|
||||||
case "style":
|
case "style":
|
||||||
responseBody = rewriteCss(await response.text(), url);
|
responseBody = rewriteCss(await response.text(), newmeta(url));
|
||||||
break;
|
break;
|
||||||
case "sharedworker":
|
case "sharedworker":
|
||||||
case "worker":
|
case "worker":
|
||||||
responseBody = rewriteWorkers(
|
responseBody = rewriteWorkers(
|
||||||
await response.arrayBuffer(),
|
await response.arrayBuffer(),
|
||||||
workertype,
|
workertype,
|
||||||
url
|
newmeta(url)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -145,7 +145,10 @@ export class ScramjetServiceWorker {
|
||||||
const data = await promise.promise;
|
const data = await promise.promise;
|
||||||
delete this.dataworkerpromises[id];
|
delete this.dataworkerpromises[id];
|
||||||
|
|
||||||
const rewritten = rewriteWorkers(data, type, new URL(origin));
|
const rewritten = rewriteWorkers(data, type, {
|
||||||
|
origin: new URL(origin),
|
||||||
|
base: new URL(origin),
|
||||||
|
});
|
||||||
|
|
||||||
return new Response(rewritten, {
|
return new Response(rewritten, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
32
static/ui.js
32
static/ui.js
|
@ -25,25 +25,6 @@ scramjet.init("./sw.js");
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
|
|
||||||
navigator.serviceWorker.onmessage = ({ data }) => {
|
|
||||||
if (data.scramjet$type === "getLocalStorage") {
|
|
||||||
const pairs = Object.entries(localStorage);
|
|
||||||
navigator.serviceWorker.controller.postMessage({
|
|
||||||
scramjet$type: "getLocalStorage",
|
|
||||||
scramjet$token: data.scramjet$token,
|
|
||||||
data: pairs,
|
|
||||||
});
|
|
||||||
} else if (data.scramjet$type === "setLocalStorage") {
|
|
||||||
for (const [key, value] of data.data) {
|
|
||||||
localStorage.setItem(key, value);
|
|
||||||
}
|
|
||||||
navigator.serviceWorker.controller.postMessage({
|
|
||||||
scramjet$type: "setLocalStorage",
|
|
||||||
scramjet$token: data.scramjet$token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const connection = new BareMux.BareMuxConnection("/baremux/worker.js");
|
const connection = new BareMux.BareMuxConnection("/baremux/worker.js");
|
||||||
const flex = css`
|
const flex = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -150,12 +131,23 @@ function App() {
|
||||||
background-color: #313131;
|
background-color: #313131;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
this.url = store.url;
|
||||||
|
|
||||||
const frame = scramjet.createFrame();
|
const frame = scramjet.createFrame();
|
||||||
|
|
||||||
frame.addEventListener("urlchange", (e) => {
|
frame.addEventListener("urlchange", (e) => {
|
||||||
|
if (!e.url) return;
|
||||||
this.url = e.url;
|
this.url = e.url;
|
||||||
});
|
});
|
||||||
|
frame.frame.addEventListener("load", () => {
|
||||||
|
let url = frame.frame.contentWindow.location.href;
|
||||||
|
if (!url) return;
|
||||||
|
if (url === "about:blank") return;
|
||||||
|
|
||||||
|
this.url = $scramjet.codecs.plain.decode(
|
||||||
|
url.substring((location.href + "/scramjet").length)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
|
@ -193,7 +185,7 @@ function App() {
|
||||||
e
|
e
|
||||||
) => {
|
) => {
|
||||||
this.url = e.target.value;
|
this.url = e.target.value;
|
||||||
}} on:keyup=${(e) => e.keyCode == 13 && frame.go(e.target.value) && (store.url = this.url)}></input>
|
}} on:keyup=${(e) => e.keyCode == 13 && (store.url = this.url) && frame.go(e.target.value)}></input>
|
||||||
<button on:click=${() => frame.forward()}>-></button>
|
<button on:click=${() => frame.forward()}>-></button>
|
||||||
</div>
|
</div>
|
||||||
${frame.frame}
|
${frame.frame}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue