Disable future calls to register() when extension has finished loading
This commit is contained in:
1 parent
1549d40868
commit
abdae22a19
1 file changed
+31
-4
@@ -3,7 +3,12 @@ const BlockType = require('../extension-support/block-type');
|
||||
const TargetType = require('../extension-support/target-type');
|
||||
const AsyncLimiter = require('../util/async-limiter');
|
||||
|
||||
const loadUnsandboxedExtension = (extensionURL, vm) => new Promise((resolve, reject) => {
|
||||
/**
|
||||
* Sets up the global.Scratch API for an unsandboxed extension.
|
||||
* @param {VirtualMachine} vm
|
||||
* @returns {Promise<object[]>} Resolves with a list of extension objects when Scratch.extensions.register is called.
|
||||
*/
|
||||
const createUnsandboxedExtensionAPI = vm => new Promise(resolve => {
|
||||
global.Scratch = global.Scratch || {};
|
||||
global.Scratch.vm = vm;
|
||||
global.Scratch.renderer = vm.runtime.renderer;
|
||||
@@ -12,9 +17,6 @@ const loadUnsandboxedExtension = (extensionURL, vm) => new Promise((resolve, rej
|
||||
global.Scratch.TargetType = TargetType;
|
||||
global.Scratch.ArgumentType = ArgumentType;
|
||||
|
||||
// When we load the script, wait for it to eventually call Scratch.extensions.register
|
||||
// before resolving. Note that extension scripts may register multiple extensions.
|
||||
|
||||
const extensionObjects = [];
|
||||
const register = extensionObject => {
|
||||
extensionObjects.push(extensionObject);
|
||||
@@ -24,11 +26,35 @@ const loadUnsandboxedExtension = (extensionURL, vm) => new Promise((resolve, rej
|
||||
unsandboxed: true,
|
||||
register
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Disable the existing global.Scratch unsandboxed extension APIs.
|
||||
* This helps debug poorly designed extensions.
|
||||
*/
|
||||
const teardownUnsandboxedExtensionAPI = () => {
|
||||
// We can assume global.Scratch already exists.
|
||||
global.Scratch.extensions.register = () => {
|
||||
throw new Error('Too late to register new extensions.');
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an unsandboxed extension from an arbitrary URL. This is dangerous.
|
||||
* @param {string} extensionURL
|
||||
* @param {Virtualmachine} vm
|
||||
* @returns {Promise<object[]>} Resolves with a list of extension objects if the extension was loaded successfully.
|
||||
*/
|
||||
const loadUnsandboxedExtension = (extensionURL, vm) => new Promise((resolve, reject) => {
|
||||
createUnsandboxedExtensionAPI(vm).then(resolve);
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.onerror = () => reject(new Error(`Error in unsandboxed script ${extensionURL}`));
|
||||
script.src = extensionURL;
|
||||
document.body.appendChild(script);
|
||||
}).then(objects => {
|
||||
teardownUnsandboxedExtensionAPI();
|
||||
return objects;
|
||||
});
|
||||
|
||||
// Because loading unsandboxed extensions requires messing with global state (global.Scratch),
|
||||
@@ -37,5 +63,6 @@ const limiter = new AsyncLimiter(loadUnsandboxedExtension, 1);
|
||||
const load = (extensionURL, vm) => limiter.do(extensionURL, vm);
|
||||
|
||||
module.exports = {
|
||||
createUnsandboxedExtensionAPI,
|
||||
load
|
||||
};
|
||||
Reference in new issue
Block a user