Search code examples
javascriptgreasemonkeyuserscripts

How can I load a shared web worker with a user-script?


I want to load a shared worker with a user-script. The problem is the user-script is free, and has no business model for hosting a file - nor would I want to use a server, even a free one, to host one tiny file. Regardless, I tried it and I (of course) get a same origin policy error:

Uncaught SecurityError: Failed to construct 'SharedWorker': Script at
'https://cdn.rawgit.com/viziionary/Nacho-Bot/master/webworker.js'
cannot be accessed from origin 'http://stackoverflow.com'.

There's another way to load a web worker by converting the worker function to a string and then into a Blob and loading that as the worker but I tried that too:

var sharedWorkers = {};
var startSharedWorker = function(workerFunc){
    var funcString = workerFunc.toString();
    var index = funcString.indexOf('{');
    var funcStringClean = funcString.substring(index + 1, funcString.length - 1);
    var blob = new Blob([funcStringClean], { type: "text/javascript" });
    sharedWorkers.google = new SharedWorker(window.URL.createObjectURL(blob));
    sharedWorkers.google.port.start();
};

And that doesn't work either. Why? Because shared workers are shared based on the location their worker file is loaded from. Since createObjectURL generates a unique file name for each use, the workers will never have the same URL and will therefore never be shared.

How can I solve this problem?


Note: I tried asking about specific solutions, but at this point I think the best I can do is ask in a more broad manner for any solution to the problem, since all of my attempted solutions seem fundamentally impossible due to same origin policies or the way URL.createObjectURL works (from the specs, it seems impossible to alter the resulting file URL).

That being said, if my question can somehow be improved or clarified, please leave a comment.


Solution

  • You can use fetch(), response.blob() to create an Blob URL of type application/javascript from returned Blob; set SharedWorker() parameter to Blob URL created by URL.createObjectURL(); utilize window.open(), load event of newly opened window to define same SharedWorker previously defined at original window, attach message event to original SharedWorker at newly opened windows.

    javascript was tried at console at How to clear the contents of an iFrame from another iFrame, where current Question URL should be loaded at new tab with message from opening window through worker.port.postMessage() event handler logged at console.

    Opening window should also log message event when posted from newly opened window using worker.postMessage(/* message */), similarly at opening window

    window.worker = void 0, window.so = void 0;
    
    fetch("https://cdn.rawgit.com/viziionary/Nacho-Bot/master/webworker.js")
      .then(response => response.blob())
      .then(script => {
        console.log(script);
        var url = URL.createObjectURL(script);
        window.worker = new SharedWorker(url);
        console.log(worker);
        worker.port.addEventListener("message", (e) => console.log(e.data));
        worker.port.start();
    
        window.so = window.open("https://stackoverflow.com/questions/" 
                                + "38810002/" 
                                + "how-can-i-load-a-shared-web-worker-" 
                                + "with-a-user-script", "_blank");
    
        so.addEventListener("load", () => {
          so.worker = worker;
          so.console.log(so.worker);
          so.worker.port.addEventListener("message", (e) => so.console.log(e.data));
          so.worker.port.start();
          so.worker.port.postMessage("hi from " + so.location.href);
        });
    
        so.addEventListener("load", () => {
          worker.port.postMessage("hello from " + location.href)
        })
    
      });
    

    At console at either tab you can then use, e.g.; at How to clear the contents of an iFrame from another iFrame worker.postMessage("hello, again") at new window of current URL How can I load a shared web worker with a user-script?, worker.port.postMessage("hi, again"); where message events attached at each window, communication between the two windows can be achieved using original SharedWorker created at initial URL.