Issue
Recently I worked on a library that supports using workers for some heavy lifting.
I found out that, at least on most online code editors (snippets/jsfiddle/codepen/glitch) I can't seem to load a worker from another domain. I get a security error (or in firefox silent failure)
function startWorker(url) {
try {
const worker = new Worker(url);
console.log('started worker');
worker.onmessage = e => log('black', e.data);
worker.postMessage('Hi from page');
} catch (e) {
console.error('could not start worker:', e);
}
}
const workerURL = 'https://greggman.github.io/doodles/test/ping-worker.js';
startWorker(workerURL);
In Chrome and Safari I get
SecurityError: Failed to construct 'Worker': Script at 'https://greggman.github.io/doodles/test/ping-worker.js' cannot be accessed from origin 'https://...'.
Question #1: Why do I get that error?
What setting causes it? iframe options? http headers for the page? http headers for the iframe? http headers from the script?)
Question #2: Is there a way to detect this issue in firefox?
I can send a message from the worker and timeout but I'm wondering if there is some less indirect way of checking for success/failure
In any case I can work around this issue by fetching the text of the script myself
function startWorker(url) {
try {
const worker = new Worker(url);
console.log('started worker');
worker.onmessage = e => console.log(e.data);
worker.postMessage('Hi from page');
} catch (e) {
console.error('could not start worker:', e);
}
}
async function main() {
const workerURL = 'https://greggman.github.io/doodles/test/ping-worker.js';
const res = await fetch(workerURL);
const text = await res.text();
const blob = new Blob([text], {type: 'application/javascript'});
const url = URL.createObjectURL(blob);
startWorker(url);
}
main();
I asked the browser teams and was told fetching manually and making a blob url is okay which leads to my main question.
Question #3: What's the point of this security error given the workaround is trival?
Given there is a workaround in what situation is there no workaround? What is the point of the Security Error? The browser vendors say my workaround is fine and I've actually be using the ability launch things as blob urls for 7-8 years. (html, scripts, but not workers until now) but if my workaround is fine then what is the point of the error?
Solution
Question #1: Why do I get that error?
Because that's what the specs ask. From fetch a classic worker
- Let request be a new request whose url is url, client is fetch client settings object, destination is destination, mode is
"same-origin"
, credentials mode is"same-origin"
, parser metadata is"not parser-inserted"
, and whose use-URL-credentials flag is set.
So the request will have its mode set to "same-origin"
, and because of that, it will fail:
(async ()=>{
const url = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js";
try {
console.log( 'default ("no-cors")' )
await fetch( url )
console.log( 'success' );
}
catch(e) { console.log( 'failed' ); }
try {
console.log( 'like workers ("same-origin")' )
await fetch( url, { mode: "same-origin" } )
console.log( 'success' );
}
catch(e) { console.log( 'failed' ); }
})();
Question #2: Is there a way to detect this issue in firefox?
Sure, you just have to listen for the error
event that will be dispatched on your Worker object:
const url = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
const worker = new Worker( url );
worker.onerror = console.error;
Question #3: What's the point of this security error given the workaround is trival?
Because the internal origin of your Worker depends on this. ref
Set worker global scope's url to response's url.
So if they were to allow a "no-cors" request here, you would be able to fetch resources on that server from your Worker, bypassing the cross-origin restrictions.
By fetching it first, and then creating a same-origin (blob:URI) or an opaque origin (data:URI) context, there is no such problem.
Note that only the initial request to the Worker's script is subject to this limitation, so an other way to work around your initial issue is to use the importScripts
method from inside a "same-origin" Worker:
const worker = new Worker( getURL() );
worker.onmessage = (evt) => console.log(evt.data);
function getURL() {
const txt = document.getElementById( 'source' ).textContent;
return URL.createObjectURL( new Blob( [ txt ] ) );
}
<script id="source" type="worker">
importScripts("https://greggman.github.io/doodles/test/ping-worker.js");
</script>
Answered By - Kaiido
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.