Skip to content

Commit

Permalink
Refactor default extension URL handling
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed Jul 29, 2023
1 parent 1d31418 commit 2158aef
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 30 deletions.
12 changes: 12 additions & 0 deletions src/extension-support/tw-default-extension-urls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// If a project uses an extension but does not specify a URL, it will default to
// the URLs given here, if it exists. This is useful for compatibility with other mods.

const defaults = new Map();

// Box2D (`griffpatch`) is not listed here because our extension is not actually
// compatible with the original version due to fields vs inputs.

// Scratch Lab Animated Text - https://lab.scratch.mit.edu/text/
defaults.set('text', 'https://extensions.turbowarp.org/lab/text.js');

module.exports = defaults;
3 changes: 1 addition & 2 deletions src/serialization/sb3.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const MathUtil = require('../util/math-util');
const StringUtil = require('../util/string-util');
const VariableUtil = require('../util/variable-util');
const compress = require('./tw-compress-sb3');
const defaultExtensionURLs = require('./tw-default-extension-urls');

const {loadCostume} = require('../import/load-costume.js');
const {loadSound} = require('../import/load-sound.js');
Expand Down Expand Up @@ -1411,7 +1410,7 @@ const replaceUnsafeCharsInVariableIds = function (targets) {
const deserialize = function (json, runtime, zip, isSingleSprite) {
const extensions = {
extensionIDs: new Set(),
extensionURLs: new Map(Object.entries(defaultExtensionURLs))
extensionURLs: new Map()
};

// Store the origin field (e.g. project originated at CSFirst) so that we can save it again.
Expand Down
11 changes: 0 additions & 11 deletions src/serialization/tw-default-extension-urls.js

This file was deleted.

4 changes: 2 additions & 2 deletions src/virtual-machine.js
Original file line number Diff line number Diff line change
Expand Up @@ -681,17 +681,17 @@ class VirtualMachine extends EventEmitter {
* @param {Map<string, string>} extensionURLs A map of extension ID to URL
*/
async _loadExtensions (extensionIDs, extensionURLs = new Map()) {
const defaultExtensionURLs = require('./extension-support/tw-default-extension-urls');
const extensionPromises = [];
for (const extensionID of extensionIDs) {
if (this.extensionManager.isExtensionLoaded(extensionID)) {
// Already loaded
} else if (this.extensionManager.isBuiltinExtension(extensionID)) {
// Builtin extension
this.extensionManager.loadExtensionIdSync(extensionID);
continue;
} else {
// Custom extension
const url = extensionURLs.get(extensionID);
const url = extensionURLs.get(extensionID) || defaultExtensionURLs.get(extensionID);
if (!url) {
throw new Error(`Unknown extension: ${extensionID}`);
}
Expand Down
55 changes: 40 additions & 15 deletions test/integration/tw_default_extension_url.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
const {test} = require('tap');
const {deserialize} = require('../../src/serialization/sb3');
const Runtime = require('../../src/engine/runtime');
const VirtualMachine = require('../../src/virtual-machine');

// Note that at some point it is likely that this extension will break
test('text extension defaults to URL', async t => {
t.plan(2);
const rt = new Runtime();
const deserialized = await deserialize({
test('Loading project uses default extension URLs', t => {
t.plan(1);

const vm = new VirtualMachine();
const events = [];
vm.securityManager.canLoadExtensionFromProject = url => {
events.push(`canLoadExtensionFromProject ${url}`);
return true;
};
vm.extensionManager.loadExtensionURL = url => {
events.push(`loadExtensionURL ${url}`);
return Promise.resolve();
};

vm.loadProject({
targets: [
{
isStage: true,
Expand Down Expand Up @@ -51,7 +60,16 @@ test('text extension defaults to URL', async t => {
},
comments: {},
currentCostume: 0,
costumes: [],
costumes: [
{
assetId: 'cd21514d0531fdffb22204e0ec5ed84a',
dataFormat: 'svg',
md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
name: 'backdrop1',
rotationCenterX: 240,
rotationCenterY: 180
}
],
sounds: [],
volume: 100,
layerOrder: 0,
Expand All @@ -62,18 +80,25 @@ test('text extension defaults to URL', async t => {
}
],
monitors: [],
// this list intentionally wrong to make sure we don't rely on its contents
extensions: [],
extensions: [
// this list intentionally wrong to make sure we don't rely on its contents
],
extensionURLs: {
griffpatch: 'https://extensions.turbowarp.org/fake-box2d-url.js'
griffpatch: 'https://example.com/box2d.js'
},
meta: {
semver: '3.0.0',
vm: '0.2.0',
agent: ''
}
}, rt);
t.equal(deserialized.extensions.extensionURLs.get('text'), 'https://extensions.turbowarp.org/lab/text.js');
t.equal(deserialized.extensions.extensionURLs.get('griffpatch'), 'https://extensions.turbowarp.org/fake-box2d-url.js');
t.end();
}).then(() => {
t.same(events, [
'canLoadExtensionFromProject https://extensions.turbowarp.org/lab/text.js',
'loadExtensionURL https://extensions.turbowarp.org/lab/text.js',
'canLoadExtensionFromProject https://example.com/box2d.js',
'loadExtensionURL https://example.com/box2d.js'
]);

t.end();
});
});

0 comments on commit 2158aef

Please sign in to comment.