Search / static /reader /pdf /reader-engine-preload.js
vomebook's picture
perf(pdf): overlap Worker dependencies and validate retained prefixes cheaply
ebf63b4 verified
Raw History Blame Contribute Delete
2.57 kB
(function () {
"use strict";
const params = new URLSearchParams(location.search);
let pdf = params.get("ext")?.toLowerCase() === "pdf";
try { pdf ||= new URL(params.get("url")).pathname.toLowerCase().endsWith(".pdf"); } catch (_) {}
const base = new URL("../../", document.currentScript.src).href;
const extension = pdf ? "pdf" : (params.get("ext") || "").toLowerCase();
const suffix = new URL(base).pathname.startsWith("/search/") ? "?reader-v1" : "";
const workerWrapper = new URL("./pdf-worker-wrapper.mjs", document.currentScript.src).href;
const assets = VoiceOfMLReaderResources.engineAssets(pdf ? "pdf" : extension, base, suffix, workerWrapper);
// PDF.js loads its small ICC color engine synchronously in the Worker. Warm
// only those pinned bytes; actual color conversion and Worker creation remain
// document-owned, with exactly the same engine/quality and no book prefetch.
const support = [];
if (pdf) {
support.push(workerWrapper);
if (!navigator.connection?.saveData) support.push(...VoiceOfMLReaderResources.pdfSupportAssets(base));
const controller = new AbortController();
const dispose = () => controller.abort();
const timer = setTimeout(dispose, 8000);
addEventListener("pagehide", dispose, {once:true});
addEventListener("voice-reader-dispose", dispose, {once:true});
// Worker entry scripts and synchronous ICC XHR use no Origin header. A
// crossorigin link preload has a different Vary: Origin cache identity on
// HF, so warm these bytes with the same-origin fetch identity instead.
Promise.allSettled(support.map(href => fetch(href, {
mode:"same-origin", credentials:"same-origin", signal:controller.signal
}).then(response => {
if (!response.ok) { response.body?.cancel().catch(() => {}); return; }
return VoiceOfMLReaderSecurity.readBytes(response, 1024 * 1024);
}))).finally(() => {
clearTimeout(timer);
removeEventListener("pagehide", dispose);
removeEventListener("voice-reader-dispose", dispose);
});
}
// Fetch/compile bytes only. The Reader still owns engine execution, source
// validation and rendering; no book body is speculatively downloaded here.
for (const href of assets) {
if (support.includes(href)) continue;
const link = document.createElement("link");
link.rel = /\.mjs(?:\?|$)/.test(href) || href.includes("/foliate-reader/") ? "modulepreload" : "preload";
if (link.rel === "preload") link.as = "script";
link.href = href;
document.head.appendChild(link);
}
})();