7

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?

gman
  • 100,619
  • 31
  • 269
  • 393
  • 1
    Same Origin Policy and CORS – epascarello Sep 25 '19 at 12:15
  • You're saying asking for a worker doesn't request CORS but fetch does? – gman Sep 25 '19 at 12:16
  • 1
    Read https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker what you asked is right there in the documentation on top along with the yellow box. – epascarello Sep 25 '19 at 12:19
  • your link doesn't answer my question. If the workaround was so trivial than what's the point of the error – gman Sep 25 '19 at 12:20
  • The HTML spec itself says that "Any same-origin URL, or a blob URL, or a data URL can be used" for workers. – Pointy Sep 25 '19 at 12:21
  • 1
    The link has the text ***"Note: that there is a disagreement among browser manufacturers about whether a data URI is of the same origin or not. Though Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0 / SeaMonkey 2.7) and later accept data URIs, that's not the case in all other browsers."*** – epascarello Sep 25 '19 at 12:22
  • @epascarello it seems like the OP is making a Blob URL however (I could be wrong; I don't do that kind of stuff generally). It *is* a little weird that the spec doesn't really even mention CORS or CSP; it just flat-out says "same-origin" and that's that. – Pointy Sep 25 '19 at 12:23
  • 1
    Or just move the file to your domain and be done with it and not risk someone altering the file to do bad or even delete it. – epascarello Sep 25 '19 at 12:25
  • The workaround works in Safari, Firefox, Chrome, buf fails in Edge. Still, my question stands, what's the point of the error if the workaround is so trival. As for the file being on the same domain, of course if I release a real product I'd put it on the same domain. 10000s of devs use CDNs though, at least for testing on sites like jsfiddle/codepen/S.O. Snippets etc... – gman Sep 25 '19 at 12:26
  • _"what's the point of the error if the workaround is so trival."_ - when specs are vague or in disagreement, browser vendors _are free to do whatever according to how they interpret the specification_ (this is also how you get browser inconsistencies in other areas). You may want to ask the browser vendors themselves as to why it was implemented this way. – Joseph Sep 25 '19 at 12:39

1 Answers1

6

Question #1: Why do I get that error?

Because that's what the specs ask. From fetch a classic worker

  1. 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>
Community
  • 1
  • 1
Kaiido
  • 123,334
  • 13
  • 219
  • 285