From 083eac248f457bcc6a9aa484bff8b0b4139a4327 Mon Sep 17 00:00:00 2001 From: MickaelK Date: Thu, 17 Jul 2025 15:56:53 +1000 Subject: [PATCH] fix (worker): iframe worker issue Whenever using worker through an iframe it was throwing: cannot open file SecurityError: Failed to construct 'Worker': Script at 'http://localhost:8000/assets/c06e8f0a220ae2c4e6fd03dd808e69ff1beee4c2/plugin/application_gis.zip/loader_dbf.worker.js' cannot be accessed from origin 'null'. at Module.default (loader_dbf.js:9:20) at application_table.js:58:50 So we made a helper function to run workers with an option to make it compatible with iframes. The issue is it does require some flag to be passed through the iframe: allow-same-origin and allow-scripts but this was the only way we had to get it done until a better solution comes in --- public/assets/helpers/loader.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/public/assets/helpers/loader.js b/public/assets/helpers/loader.js index 118ded60..e1935683 100644 --- a/public/assets/helpers/loader.js +++ b/public/assets/helpers/loader.js @@ -1,3 +1,5 @@ +import { onDestroy } from "../lib/skeleton/index.js"; + export async function loadJS(baseURL, path, opts = {}) { const $script = document.createElement("script"); const link = new URL(path, baseURL); @@ -26,6 +28,31 @@ export async function loadCSS(baseURL, path) { }); } +export async function loadWorker(baseURL, path, opts = {}) { + const { iframe = true } = opts; + let url = new URL(path, baseURL); + if (iframe) { + // eg: fix issue coming from loading worker through an iframe like this nasty: + // SecurityError: Failed to construct 'Worker': Script at 'xxx.worker.js' cannot be accessed from origin 'null'. + // at Module.default (loader_dbf.js:9:20) + // at application_table.js:58:50 + let code = await fetch(new URL(path, baseURL)).then((res) => res.text()); + const importPathRE = new RegExp("import\\s+(?:[^'\";]*?\\s+from\\s+)?[\"']([^\"']+)[\"']", "gm"); + code = code.replaceAll("import.meta.url", `"${baseURL}"`) + code.matchAll(importPathRE).forEach(([_, path]) => { + code = code.replaceAll(path, new URL(path, baseURL)); + }); + url = URL.createObjectURL(new Blob([code], { type: "application/javascript" })); + onDestroy(() => URL.revokeObjectURL(url)); + } + const worker = new Worker(url, { type: "module" }); + onDestroy(() => worker.terminate()); + await new Promise((resolve) => worker.addEventListener( + "message", resolve, { once: true }, + )); + return worker; +} + export async function loadCSSInline(baseURL, filename) { const res = await fetch(new URL(filename, baseURL).pathname, { cache: "force-cache",