const ArgumentType = require('../extension-support/argument-type'); const BlockType = require('../extension-support/block-type'); const TargetType = require('../extension-support/target-type'); const Cast = require('./cast'); const Clone = require('./clone'); const Color = require('./color'); /** * Parse a URL object or return null. * @param {string} url * @returns {URL|null} */ const parseURL = url => { try { return new URL(url, location.href); } catch (e) { return null; } }; class CustomExtensionApi { constructor (followLimitations) { this.limited = followLimitations === true; } get ArgumentType () { return ArgumentType; } get BlockType () { return BlockType; } get TargetType () { return TargetType; } get Cast () { return Cast; } get Clone () { return Clone; } get Color () { return Color; } get vm () { return vm; } get renderer () { return CustomExtensionApi.vm.runtime.renderer; } async canFetch (url) { if (this.limited) { const parsed = parseURL(url); if (!parsed) { return false; } // Always allow protocols that don't involve a remote request. if (parsed.protocol === 'blob:' || parsed.protocol === 'data:') { return true; } return true; } return true; } async canOpenWindow (url) { if (this.limited) { const parsed = parseURL(url); if (!parsed) { return false; } // Always reject protocols that would allow code execution. // eslint-disable-next-line no-script-url if (parsed.protocol === 'javascript:') { return false; } return true; } return true; } async canRedirect (url) { if (this.limited) { const parsed = parseURL(url); if (!parsed) { return false; } // Always reject protocols that would allow code execution. // eslint-disable-next-line no-script-url if (parsed.protocol === 'javascript:') { return false; } return true; } return true; } async fetch (url, options) { if (this.limited) { const actualURL = url instanceof Request ? url.url : url; if (!await global.Scratch.canFetch(actualURL)) { throw new Error(`Permission to fetch ${actualURL} rejected.`); } return fetch(url, { ...options, redirect: 'error' }); } return fetch(url, { ...options, redirect: 'error' }); } async openWindow (url, features) { if (this.limited) { if (!await global.Scratch.canOpenWindow(url)) { throw new Error(`Permission to open tab ${url} rejected.`); } return window.open(url, '_blank', features); } return window.open(url, '_blank', features); } async redirect (url) { if (this.limited) { if (!await global.Scratch.canRedirect(url)) { throw new Error(`Permission to redirect to ${url} rejected.`); } location.href = url; return; } location.href = url; } get extensions () { return { unsandboxed: true, register: () => { throw new Error("Register cannot be replicated in custom-ext-api-to-core"); } }; } }; module.exports = CustomExtensionApi;