// The WebSocket shim tag, put into guest app documents so it runs before any app // script: as the first child of , or before the first tag when there is no // tag (the parser then opens head for it). Comments, doctypes and the contents // of script and style elements are skipped while looking. The document's bytes are // otherwise untouched; the scan reads them as latin1, one character per byte, so any // ASCII-compatible charset survives. import { WS_SHIM_PATH } from "../shared/vurls"; const TAG = new TextEncoder().encode(``); const RAW_TEXT = new Set(["script", "style"]); /** Where the shim tag goes in `html`. */ export function shimOffset(html: string): number { let firstTag = -1; let at = 0; while ((at = html.indexOf("<", at)) !== -1) { if (html.startsWith("", at + 4); if (end === -1) break; at = end + 3; continue; } const m = /^<\/?([A-Za-z][^\s/>]*)/.exec(html.slice(at, at + 64)); if (!m) { // , , or a stray "<" in text. at += 1; continue; } const close = html.indexOf(">", at); if (close === -1) break; if (firstTag === -1) firstTag = at; const name = m[1]!.toLowerCase(); const isEnd = html[at + 1] === "/"; if (!isEnd && name === "head") return close + 1; if (!isEnd && RAW_TEXT.has(name)) { const end = html.toLowerCase().indexOf(`]/i; /** Only a document runs scripts, and only an uncompressed HTML body can be edited. */ export function wantsShim(mode: RequestMode, headers: Headers, body: Uint8Array): boolean { const encoding = headers.get("content-encoding"); if (mode !== "navigate" || (encoding !== null && encoding.toLowerCase() !== "identity")) return false; const type = headers.get("content-type"); if (type !== null) return /^text\/html\b/i.test(type); return SNIFFED_HTML.test(new TextDecoder("latin1").decode(body.subarray(0, 512))); }