From d1a427d18b80106930d9a56ec3cc0cb313cc0abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pasteau?= <4895034+ClementPasteau@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:08:30 +0200 Subject: [PATCH] Fix file uploads on web browsers like Firefox --- .../BrowserEventsFunctionsExtensionOpener.js | 26 ++++++++++++++++++- newIDE/app/src/ServiceWorkerSetup.js | 11 +++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/newIDE/app/src/EventsFunctionsExtensionsLoader/Storage/BrowserEventsFunctionsExtensionOpener.js b/newIDE/app/src/EventsFunctionsExtensionsLoader/Storage/BrowserEventsFunctionsExtensionOpener.js index 7fd0354d81c0..344e960c7e04 100644 --- a/newIDE/app/src/EventsFunctionsExtensionsLoader/Storage/BrowserEventsFunctionsExtensionOpener.js +++ b/newIDE/app/src/EventsFunctionsExtensionsLoader/Storage/BrowserEventsFunctionsExtensionOpener.js @@ -30,8 +30,18 @@ export default class BrowserEventsFunctionsExtensionOpener { adhocInput.type = 'file'; adhocInput.multiple = true; adhocInput.accept = 'application/json,.json'; + adhocInput.style.display = 'none'; + + const removeInput = () => { + const body = document.body; + if (body && body.contains(adhocInput)) { + body.removeChild(adhocInput); + } + }; + adhocInput.onchange = e => { - return resolve(e.target.files); + removeInput(); + return resolve(Array.from(e.target.files)); }; // There is no built-in way to know if the user closed the file picking dialog @@ -58,12 +68,14 @@ export default class BrowserEventsFunctionsExtensionOpener { onFilePickingDialogFinishedClosing ); } + removeInput(); if (!adhocInput.files.length) { resolve([]); } }; window.addEventListener('focus', onFocusBackWindow); + if (document.body) document.body.appendChild(adhocInput); adhocInput.click(); } }); @@ -118,7 +130,17 @@ export default class BrowserEventsFunctionsExtensionOpener { adhocInput.type = 'file'; adhocInput.multiple = false; adhocInput.accept = 'application/gdo,.gdo'; + adhocInput.style.display = 'none'; + + const removeInput = () => { + const body = document.body; + if (body && body.contains(adhocInput)) { + body.removeChild(adhocInput); + } + }; + adhocInput.onchange = e => { + removeInput(); return resolve(e.target.files[0]); }; @@ -146,12 +168,14 @@ export default class BrowserEventsFunctionsExtensionOpener { onFilePickingDialogFinishedClosing ); } + removeInput(); if (!adhocInput.files.length) { resolve(''); } }; window.addEventListener('focus', onFocusBackWindow); + if (document.body) document.body.appendChild(adhocInput); adhocInput.click(); } }); diff --git a/newIDE/app/src/ServiceWorkerSetup.js b/newIDE/app/src/ServiceWorkerSetup.js index ce990b73e150..cc5173082cd2 100644 --- a/newIDE/app/src/ServiceWorkerSetup.js +++ b/newIDE/app/src/ServiceWorkerSetup.js @@ -66,11 +66,16 @@ export function registerServiceWorker() { console.error('Error during service worker registration:', error); }); - serviceWorker.ready.then(registration => { + if (!isDev) { // Forces a check right now for a newer service worker script. // If there is one, it will be installed (see the service worker script to verify how in development // a new service worker script does a `self.skipWaiting()` and `self.clients.claim()`). - registration.update(); - }); + // In development, the Date.now() cache-buster in the SW URL already ensures a fresh + // script on every load — calling update() on top of it causes a Firefox error because + // the ready registration's scriptURL no longer matches the newly registered URL. + serviceWorker.ready.then(registration => { + registration.update(); + }); + } }); }