14

I'm trying to create a Web Worker from my extension's content script, but it's getting blocked by a SecurityError (same origin policy). What's the best way to do this?

From my content script:

var workerURL = chrome.extension.getURL("js/searchWorker.js");
var lunrWorker = new Worker(workerURL);

From the manifest:

"content_scripts": [
   {
     "matches": ["http://localhost:8000/*"],
     "js": ["js/jquery.min.js", "js/jquery.highlight.js", "js/index.js"],
     "css": ["css/bootstrap.css", "css/styles.css"]
   }
 ]

I also tried setting this in my manifest, but it didn't help:

"content_security_policy": "default-src 'none'; style-src 'self'; script-src 'self';",

(Sidenote: the CSS isn't being injected into the page, I'm not sure if that's a symptom of the same problem or unrelated)

CambridgeMike
  • 4,562
  • 1
  • 28
  • 37

1 Answers1

17

http://crbug.com/357664 is the bug report about not being able to load extension scripts as a web worker.

The workaround to this problem is to load the worker script using XMLHttpRequest, then load the worker from a string. When I faced this problem in the past, I created a wrapper that transparently modifies the Worker constructor, so you can use new Worker(chrome.runtime.getURL('worker.js')) without any problems.

See patch-worker.js (documentation) for the implementation of the previous idea.

patch-worker.js has some limitations (e.g. importScripts does not work as expected), mainly related to the fact that it does not run in the chrome-extension:-origin. To solve these problems, I created another library that uses an iframe to create the Worker. See worker_proxy for the source code and documentation.

Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Oh man, that's a bummer it's a bug. This script works great for loading my web worker. Now I've got the problem that importScript doesn't work inside the worker, I get `Failed to execute 'importScripts' on 'WorkerGlobalScope': The URL 'lunr.min.js' is invalid` – CambridgeMike Mar 28 '14 at 18:26
  • Actually, it looks like I need to do importScript in an eventlistener, and pass the path returned by chrome.extension.getURL. This feels pretty sloppy, since there are no guarantees my worker won't start receiving other messages before the importScript has completed and executed the imported script. Or will it halt execution since it is a single thread? – CambridgeMike Mar 28 '14 at 18:33
  • `importScript` / `importScripts` are synchronous functions, so it is not possible to polyfill these at runtime in the worker script (any communication with a worker is async). In theory, this can also be polyfilled by first scanning the worker string for `importScript(...)`, fetch the data and replace the content, but then I'm approaching a full-blown script loader, which is quite a lot of work (and it wouldn't be 100% reliable, because static analysis cannot tell what's going to be loaded when you use `var file = 'x.js';importScript(file)`. (to be continued) – Rob W Mar 28 '14 at 18:35
  • I suggest to write/use a build/shell script that inlines the `importScript` calls, so the worker can be loaded in a single "network" request. Although `chrome-extension://` resources have little network latency, having to load loads of them does affect the performance. Personally I use [r.js](https://github.com/jrburke/r.js/) to combine my JS modules into one before I deploy a contentscript-based extension. – Rob W Mar 28 '14 at 18:38
  • @CambridgeMike FYI, I have added another patch that allows the use of `importScripts` in content script `Worker`s. – Rob W Jun 14 '14 at 16:53
  • Thanks, I've actually been using it (although haven't noticed any issues). I'll grab the update! – CambridgeMike Jun 15 '14 at 15:37
  • I suspect this won't work with manifest v3, or does it? – Lizozom Jul 10 '23 at 10:42