From bd946bdd2610ab2bce41fd7b22023010efeb359b Mon Sep 17 00:00:00 2001 From: gfrancine Date: Thu, 25 Jun 2026 22:53:59 +1000 Subject: [PATCH] fix: revoke all object URLs created by loadImage --- src/image/loading_displaying.js | 6 +++++- test/unit/image/loading.js | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/image/loading_displaying.js b/src/image/loading_displaying.js index cac31013ad..3d23cc4c0b 100644 --- a/src/image/loading_displaying.js +++ b/src/image/loading_displaying.js @@ -130,7 +130,11 @@ function loadingDisplaying(p5, fn){ const blob = new Blob([data], { type: contentType }); const url = URL.createObjectURL(blob); - img.onerror = e => reject(e); + img.onerror = e => { + URL.revokeObjectURL(url); + reject(e); + }; + img.onload = () => { URL.revokeObjectURL(url); resolve(img); diff --git a/test/unit/image/loading.js b/test/unit/image/loading.js index 759beb6cdd..94c4c02b77 100644 --- a/test/unit/image/loading.js +++ b/test/unit/image/loading.js @@ -254,6 +254,23 @@ suite('loading images', function() { assert.deepEqual(img.get(0, 0), GREEN); assert.deepEqual(img.get(47, 47), GREEN); }); + + test('revokes any object URLs created during loading', async () => { + const createSpy = vi.spyOn(URL, 'createObjectURL'); + const revokeSpy = vi.spyOn(URL, 'revokeObjectURL'); + + // successful load + await mockP5Prototype.loadImage(svgImage); + expect(revokeSpy).toHaveBeenCalledTimes(createSpy.mock.calls.length); + + // errored load + const INVALID_PATH = ''; + try { await mockP5Prototype.loadImage(INVALID_PATH); } catch {} + expect(revokeSpy).toHaveBeenCalledTimes(createSpy.mock.calls.length); + + createSpy.mockRestore(); + revokeSpy.mockRestore(); + }); }); suite.todo('displaying images', function() {