diff --git a/src/client/dom/element.ts b/src/client/dom/element.ts index 20dd053..a9d0523 100644 --- a/src/client/dom/element.ts +++ b/src/client/dom/element.ts @@ -1,5 +1,5 @@ import { ScramjetClient } from "../client"; -import { config, decodeUrl, htmlRules } from "../shared"; +import { config, decodeUrl, htmlRules, unrewriteHtml } from "../shared"; import { encodeUrl, rewriteCss, @@ -8,11 +8,6 @@ import { rewriteSrcset, } from "../shared"; -declare global { - interface Element { - $origattrs: Record; - } -} export default function (client: ScramjetClient, self: typeof window) { const attrObject = { nonce: [self.HTMLElement], @@ -100,22 +95,27 @@ export default function (client: ScramjetClient, self: typeof window) { }, }); - 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); + client.Trap("Element.prototype.innerHTML", { + set(ctx, value: string) { + if (ctx.this instanceof self.HTMLScriptElement) { + return rewriteJs(value, client.url); + } else if (ctx.this instanceof self.HTMLStyleElement) { + return rewriteCss(value, client.url); } else { - value = rewriteHtml(value, client.cookieStore); + return rewriteHtml(value, client.cookieStore, client.url); } + }, + get(ctx) { + return unrewriteHtml(ctx.get()); + }, + }); - return innerHTML.set.call(this, value); + client.Trap("Element.prototype.outerHTML", { + set(ctx, value: string) { + return rewriteHtml(value, client.cookieStore, client.url); + }, + get(ctx) { + return unrewriteHtml(ctx.get()); }, }); diff --git a/src/client/shared.ts b/src/client/shared.ts index f36311d..743abdb 100644 --- a/src/client/shared.ts +++ b/src/client/shared.ts @@ -4,6 +4,7 @@ export const { rewrite: { rewriteCss, rewriteHtml, + unrewriteHtml, rewriteSrcset, rewriteJs, rewriteHeaders, diff --git a/src/shared/index.ts b/src/shared/index.ts index 53f210d..d65ae08 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -8,7 +8,7 @@ import { BareClient } from "@mercuryworkshop/bare-mux"; import { parseDomain } from "parse-domain"; import { ScramjetHeaders } from "./headers"; import { CookieStore } from "./cookie"; -import { htmlRules } from "./rewriters/html"; +import { htmlRules, unrewriteHtml } from "./rewriters/html"; self.$scramjet.shared = { util: { @@ -23,6 +23,7 @@ self.$scramjet.shared = { rewrite: { rewriteCss, rewriteHtml, + unrewriteHtml, rewriteSrcset, rewriteJs, rewriteHeaders, diff --git a/src/shared/rewriters/html.ts b/src/shared/rewriters/html.ts index 564a111..55eb032 100644 --- a/src/shared/rewriters/html.ts +++ b/src/shared/rewriters/html.ts @@ -48,28 +48,49 @@ export function rewriteHtml( } `; + const script = (src) => new Element("script", { src }); + head.children.unshift( - new Element("script", { - src: self.$scramjet.config["wasm"], - }), - new Element("script", { - src: self.$scramjet.config["codecs"], - }), - new Element("script", { - src: "data:application/javascript;base64," + btoa(injected), - }), - new Element("script", { - src: self.$scramjet.config["shared"], - }), - new Element("script", { - src: self.$scramjet.config["client"], - }) + script(self.$scramjet.config["wasm"]), + script(self.$scramjet.config["codecs"]), + script("data:application/javascript;base64," + btoa(injected)), + script(self.$scramjet.config["shared"]), + script(self.$scramjet.config["client"]) ); } return render(handler.root); } +export function unrewriteHtml(html: string) { + const handler = new DomHandler((err, dom) => dom); + const parser = new Parser(handler); + + parser.write(html); + parser.end(); + + function traverse(node: ChildNode) { + if ("attribs" in node) { + for (const key in node.attribs) { + if (key.startsWith("data-scramjet-")) { + node.attribs[key.slice(13)] = node.attribs[key]; + delete node.attribs[key]; + } + } + } + + if ("childNodes" in node) { + for (const child of node.childNodes) { + traverse(child); + } + } + } + + traverse(handler.root); + + return render(handler.root); +} + export const htmlRules: { [key: string]: "*" | string[] | Function; fn: ( diff --git a/src/types.d.ts b/src/types.d.ts index 478b253..5a2931f 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,7 +1,12 @@ import { ScramjetController } from "./bootsrapper/index"; import { encodeUrl, decodeUrl } from "./shared/rewriters/url"; import { rewriteCss } from "./shared/rewriters/css"; -import { htmlRules, rewriteHtml, rewriteSrcset } from "./shared/rewriters/html"; +import { + htmlRules, + rewriteHtml, + rewriteSrcset, + unrewriteHtml, +} from "./shared/rewriters/html"; import { rewriteJs } from "./shared/rewriters/js"; import { rewriteHeaders } from "./shared/rewriters/headers"; import { rewriteWorkers } from "./shared/rewriters/worker"; @@ -43,6 +48,7 @@ declare global { rewrite: { rewriteCss: typeof rewriteCss; rewriteHtml: typeof rewriteHtml; + unrewriteHtml: typeof unrewriteHtml; rewriteSrcset: typeof rewriteSrcset; rewriteJs: typeof rewriteJs; rewriteHeaders: typeof rewriteHeaders;