Last active
January 23, 2024 06:45
-
-
Save willywongi/5780151 to your computer and use it in GitHub Desktop.
Load a Worker from a different domain (CORS + tricks)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
You want to use webworkers, but you host all of your js files on a different domain than where | |
your app lives (es. app.example.com and js.example.com). Given that the static file served from | |
js.example.com are CORS ready (in that the response header includes Accept-origin: *), I thought | |
I could load a Worker from other domains. I was almost wrong, but at last I found a solution: | |
XHRing the worker source and create an inline worker. This is tested only on Firefox (latest). | |
*/ | |
// This is an example webworker that is on js.example.com. It just echoes messages it receive. | |
self.onmessage = function(e) { | |
self.postMessage(e.data); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<!-- | |
This page is on app.example.com; we try to use a worker that is on js.example.com. | |
--> | |
<head> | |
<!-- Loading an external script through a <script> node doesn't work --> | |
<script src="http://js.example.com/get_worker_echo.js"></script> | |
<!-- | |
This inline worker sources are parsed later on; each of these workers loads another worker | |
when it receives a message like this: {url: 'http://some.url/to/worker.js'} and | |
responds with the same URL when it managed to load it. | |
Disclosure: this is not working; only loading through XHR the worker source works. | |
--> | |
<script id="static-load-worker" type="text/webworker"> | |
self.onmessage = function(e) { | |
if (e.data.url) { | |
try { | |
self.subworker = new Worker(e.data.url); | |
} catch (e) { | |
self.postMessage({'error': 'cannot loaded subworker from url '+e.data.url}) | |
} | |
if (self.subworker) { | |
self.subworker.onmessage = function(e) { | |
self.postMessage(e) | |
} | |
self.postMessage(e.data.url); | |
} | |
} else { | |
if (! self.subworker) { | |
self.postMessage({'error': 'subworker not loaded'}); | |
} else { | |
self.subworker.postMessage(e) | |
} | |
} | |
} | |
</script> | |
<script id="import-worker" type="text/webworker"> | |
self.onmessage = function(e) { | |
if (e.data.url) { | |
self.workerURL = e.data.url; | |
importScripts('http://js.example.it/loader.js'); | |
} | |
} | |
</script> | |
<script> | |
// This simply won't work | |
var w = new Worker('http://js.example.it/echo.js'); | |
function loadWorker(url, ready, scope) { | |
/* This creates a inline-worker that spans a subworker loading script from the desired URL. | |
inlineworker > subworker | |
This won't work because the subworker shares the origin with the parent (inline) worker, | |
and its origin is the same as the containing page (the URL you're visiting). | |
*/ | |
var worker = new Worker(window.URL.createObjectURL(new Blob([document.getElementById('static-load-worker').innerHTML]))), | |
h = worker.addEventListener('message', function(e) { | |
if (e.data === url) { | |
ready.call(scope, worker); | |
} | |
}); | |
worker.postMessage({'url': url}); | |
return worker; | |
} | |
function importWorker(url, ready, scope) { | |
/* This creates an inline worker with static-load-worker (see before), which in | |
turns loads a script through importScripts; the imported script spans a new | |
subworker located at the desired URL. | |
inline worker > importScripts > loader.js > subworker | |
This won't work because the spanned subworked shares the same origin with the | |
parent worker, which in turn shares the origin with the worker that loaded it | |
even if it's imported through importScripts. | |
*/ | |
var worker = new Worker(window.URL.createObjectURL(new Blob([document.getElementById('import-worker').innerHTML]))), | |
h = worker.addEventListener('message', function(e) { | |
if (e.data === url) { | |
ready.call(scope, worker); | |
} | |
}); | |
worker.postMessage({'url': url}); | |
return worker; | |
} | |
function XHRWorker(url, ready, scope) { | |
/* This loads the source of the worker through a XHR call. This is possible since the server | |
from which we pull the worker source serves files with CORS (Access-Control-Allow-Origin: *). | |
From the source (responseText) we build an inline worker. | |
This works but we need to delegeate using the worker when the resource is loaded (XHR call finishes) | |
*/ | |
var oReq = new XMLHttpRequest(); | |
oReq.addEventListener('load', function() { | |
var worker = new Worker(window.URL.createObjectURL(new Blob([this.responseText]))); | |
if (ready) { | |
ready.call(scope, worker); | |
} | |
}, oReq); | |
oReq.open("get", url, true); | |
oReq.send(); | |
} | |
function WorkerEcho() { | |
XHRWorker("http://js.example.it/echo.js", function(worker) { | |
this.worker = worker; | |
worker.onmessage = function(e) { | |
console.log(e.data); | |
} | |
}, this); | |
} | |
WorkerEcho.prototype = { | |
say: function(what) { | |
this.worker.postMessage(what); | |
} | |
} | |
</script> | |
</head> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Here you have: a HTML page on app.example.com with a script tag that points | |
to http://js.example.com/get_worker_echo.js; this script loads a worker on this | |
same domain, but it fails because at the end the worker has a different origin. | |
*/ | |
window.EchoWorker = new Worker('http://js.example.com/echo.js'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This piece of code will be imported through importScripts, and will | |
behave exactly as if it were in place of the importScript call. */ | |
var subworker = new Worker(self.workerURL); | |
subworker.onmessage = function(e) { | |
self.postMessage(e.data); | |
} | |
self.onmessage = function(e) { | |
subworker.postMessage(e.data); | |
} |
Loading using importScripts
seems to work fine on Edge and Chrome. importScripts
from another domain seems to fail - sometimes - for some reason - in Firefox. Still trying to work out why ...
Guys, how did it go eventually? Did it work for you?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Have you tried this on IE and Chrome? They are less forgiving about blob and data URIs....