/* eslint-env worker */ const ScratchCommon = require('./tw-extension-api-common'); const createScratchX = require('./tw-scratchx-compatibility-layer'); const dispatch = require('../dispatch/worker-dispatch'); const log = require('../util/log'); const {isWorker} = require('./tw-extension-worker-context'); const createTranslate = require('./tw-l10n'); const translate = createTranslate(null); const loadScripts = url => { if (isWorker) { importScripts(url); } else { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.onload = () => resolve(); script.onerror = () => { reject(new Error(`Error in sandboxed script: ${url}. Check the console for more information.`)); }; script.src = url; document.body.appendChild(script); }); } }; class ExtensionWorker { constructor () { this.nextExtensionId = 0; this.initialRegistrations = []; this.firstRegistrationPromise = new Promise(resolve => { this.firstRegistrationCallback = resolve; }); dispatch.waitForConnection.then(() => { dispatch.call('extensions', 'allocateWorker').then(async x => { const [id, extension] = x; this.workerId = id; try { await loadScripts(extension); await this.firstRegistrationPromise; const initialRegistrations = this.initialRegistrations; this.initialRegistrations = null; Promise.all(initialRegistrations).then(() => dispatch.call('extensions', 'onWorkerInit', id)); } catch (e) { log.error(e); dispatch.call('extensions', 'onWorkerInit', id, `${e}`); } }); }); this.extensions = []; } register (extensionObject) { const extensionId = this.nextExtensionId++; this.extensions.push(extensionObject); const serviceName = `extension.${this.workerId}.${extensionId}`; const promise = dispatch.setService(serviceName, extensionObject) .then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); if (this.initialRegistrations) { this.firstRegistrationCallback(); this.initialRegistrations.push(promise); } return promise; } } global.Scratch = global.Scratch || {}; Object.assign(global.Scratch, ScratchCommon, { canFetch: () => Promise.resolve(true), fetch: (url, options) => fetch(url, options), canOpenWindow: () => Promise.resolve(false), openWindow: () => Promise.reject(new Error('Scratch.openWindow not supported in sandboxed extensions')), canRedirect: () => Promise.resolve(false), redirect: () => Promise.reject(new Error('Scratch.redirect not supported in sandboxed extensions')), canRecordAudio: () => Promise.resolve(false), canRecordVideo: () => Promise.resolve(false), canReadClipboard: () => Promise.resolve(false), canNotify: () => Promise.resolve(false), canGeolocate: () => Promise.resolve(false), canEmbed: () => Promise.resolve(false), canUnsandbox: () => Promise.resolve(false), translate }); /** * Expose only specific parts of the worker to extensions. */ const extensionWorker = new ExtensionWorker(); global.Scratch.extensions = { register: extensionWorker.register.bind(extensionWorker) }; global.ScratchExtensions = createScratchX(global.Scratch);