Make builtinExtensions global
arpruss opened this issue · 17 comments
The window object (and some others) don't seem to be available to extensions loaded through the URL extension loading function. They do seem to be available to the built-in extensions.
Unofficial extensions are run inside web workers, which do not have access to window
. The global object in web workers is self
, and here's a list of what's available to them.
Is there an easy way to modify the code to run unofficial extensions, loaded from an external URL, outside of web workers?
Hmm, you could modify where it creates the worker to instead use ES6 dynamic imports (which don't work in all browsers) or adding a script tag, but it'd probably also require modifying Webpack so it builds properly
A really hackish and messy solution is to replace the index.js of a built-in extension with:
eval(prompt("enter extension code"))
and then have the user copy and paste the code into the prompt. I've actually tried this and it worked (it's important not to have any // comments in the code for it to work, as it all gets treated as on one line).
It looks to me that ES6 modules are subject to CORS policies, so unless one sets a CORS header on the server offering the extension, it won't work.
There's a load_plugin
URL parameter for loading a JS file into the web page
Thanks! That's promising. Do you know how to call Scratch.extensions.register() from the plug-in? I can't find the Scratch object in that context.
Yes, that's one of the complications with replacing the worker.
Perhaps you could change line 161 of src/extension-support/extension-manager.js to be
const ExtensionWorker = require('./extension-worker');
This way, it runs the script that adds the Scratch global in the main window
Thanks for all your help!
Alas, changing that line doesn't seem to give me access to the Scratch global in the plugin or in the main window (at least not via the Chrome console). Am I misunderstanding something?
Also, now when I load an ordinary extension (which doesn't use window or anything like that), I get:
vm Dispatch caught malformed message from a worker: {"isTrusted":true}
./node_modules/minilog/lib/web/formatters/minilog.js.logger.write @ lib.min.js:94542
lib.min.js:275673 Uncaught (in promise) TypeError: ExtensionWorker is not a constructor
Oh, then it's probably more complicated than that then, hmm, because it relies on the URL extension being a worker
Maybe you could make builtinExtensions
global so then an extension can add to it and then you can add the extension by an ID like a built-in extension
Yup, that works really nicely.
I added window.ScratchExtensions = builtinExtensions;
and then made the plugin be the extension code plus:
window.ScratchExtensions.Gamepad = function() {return ScratchGamepad}
window.vm.extensionManager.loadExtensionURL("Gamepad");
And it works great!
Any chance of your making builtinExtensions global in your version of your mod?
Sure why not
I now think we don't need to!
I put this at the bottom of the extension and it works perfectly as a plugin using your unmodified code:
(function() {
var extensionInstance = new ScratchGamepad(window.vm.extensionManager.runtime)
var serviceName = window.vm.extensionManager._registerInternalExtension(extensionInstance)
window.vm.extensionManager._loadedExtensions.set(extensionInstance.getInfo().id, serviceName)
})()
If you want to see the full extension, it's at http://pruss.mobi/js/gamepad.js
oh ok then I shall close this again!
Should I post about this solution on the Scratch forums or will this violate the userscript policy?
I think since this is for a Scratch extension and only affects my mod (unlike a browser extension that could wreak havoc globally), the ST should be fine with it. Just be careful with your wording
BTW, when I switched an extension to use load_plugin- instead of url=, it became an order of magnitude faster.