5

I keep getting back error code: 18, SecurityError from Edge and Safari while trying to use a Web Worker. The workers are fine in Firefox / Chrome however. I'm using an inlined worker that I pass zero-dependency data processing functions to.

My CSP has looked:

add_header Content-Security-Policy "default-src 'self'; worker-src 'self' 'inline' *.example.com";

I can add in other nice to haves like local stylesheets and googleapis.com on my own, but I'm curious how to get the Worker to not throw a Security error

Snippet from worker method

// Create an "inline" worker (1:1 at definition time)
    const worker = new Worker(
        // Use a data URI for the worker's src. It inlines the target function and an RPC handler:
        'data:,$$='+asyncFunction+';onmessage='+(e => {
            /* global $$ */

            // Invoking within then() captures exceptions in the supplied async function as rejections
            Promise.resolve(e.data[1]).then(
                v => $$.apply($$, v)
            ).then(
                // success handler - callback(id, SUCCESS(0), result)
                // if `d` is transferable transfer zero-copy
                d => {
                    postMessage([e.data[0], 0, d], [d].filter(x => (
                        (x instanceof ArrayBuffer) ||
                        (x instanceof MessagePort) ||
                        (x instanceof ImageBitmap)
                    )));
                },
                // error handler - callback(id, ERROR(1), error)
                er => { postMessage([e.data[0], 1, '' + er]); }
            );
        })
    );

Edge throws this error for the worker:

  [object DOMException]: {code: 18, message: "SecurityError", name: 
    "SecurityError"}
    code: 18
    message: "SecurityError"
    name: "SecurityError"
Sean Kelly
  • 901
  • 7
  • 27

1 Answers1

1

I'm not sure why the data url is causing a security error but you can use URL.createObjectURL to load a worker script, which seems to work properly in Edge (I didn't test it in safari).

Here's what that would look like:

// Create the worker script as a string
const script = '$$='+asyncFunction+';onmessage='+(e => {
        /* global $$ */

        // Invoking within then() captures exceptions in the supplied async function as rejections
        Promise.resolve(e.data[1]).then(
            v => $$.apply($$, v)
        ).then(
            // success handler - callback(id, SUCCESS(0), result)
            // if `d` is transferable transfer zero-copy
            d => {
                postMessage([e.data[0], 0, d], [d].filter(x => (
                    (x instanceof ArrayBuffer) ||
                    (x instanceof MessagePort) ||
                    (x instanceof ImageBitmap)
                )));
            },
            // error handler - callback(id, ERROR(1), error)
            er => { postMessage([e.data[0], 1, '' + er]); }
        );
    });

// Create a local url to load the worker
const blob = new Blob([script]);
const workerUrl = URL.createObjectURL(blob);
const worker = new Worker(workerUrl);

Let me know if you need any clarification!

gman
  • 100,619
  • 31
  • 269
  • 393
Garrett Johnson
  • 2,413
  • 9
  • 17
  • Ding ding ding, winner winner. Such an oversight on my part. Thanks for pointing that out. Additionally, the ImageBitmap isn't supported in Edge so that needed to come out. – Sean Kelly Mar 04 '19 at 18:08