From e295fd56c956cf33fa3696b9e85b299b21665f6b Mon Sep 17 00:00:00 2001 From: lxpollitt <630494+lxpollitt@users.noreply.github.com> Date: Fri, 17 Apr 2026 20:12:28 +0100 Subject: [PATCH] =?UTF-8?q?Fix=20ADD=20button=20not=20opening=20file=20pic?= =?UTF-8?q?ker=20in=20Safari=20Safari=20enforces=20the=20"user=20activatio?= =?UTF-8?q?n"=20rule=20for=20opening=20native=20file=20pickers=20more=20st?= =?UTF-8?q?rictly=20than=20Chrome:=20.click()=20on=20an=20=20must=20run=20in=20the=20same=20synchronous=20event=20?= =?UTF-8?q?stack=20as=20the=20user-gesture=20event.=20HomeScreen.importPro?= =?UTF-8?q?gram()=20was=20wrapping=20the=20call=20in=20Gdx.app.postRunnabl?= =?UTF-8?q?e(...),=20which=20defers=20by=20one=20frame=20=E2=80=94=20long?= =?UTF-8?q?=20enough=20for=20Safari=20to=20revoke=20the=20token=20and=20si?= =?UTF-8?q?lently=20refuse=20the=20picker.=20Calling=20synchronously=20is?= =?UTF-8?q?=20safe=20on=20all=20three=20platforms=20because=20each=20Dialo?= =?UTF-8?q?gHandler.openFileDialog=20implementation=20handles=20its=20own?= =?UTF-8?q?=20thread=20affinity=20internally=20(Desktop=20wraps=20for=20Sw?= =?UTF-8?q?ing,=20Android=20uses=20runOnUiThread,=20GWT=20runs=20synchrono?= =?UTF-8?q?usly).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/main/java/emu/joric/HomeScreen.java | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/emu/joric/HomeScreen.java b/core/src/main/java/emu/joric/HomeScreen.java index 189a659..44b3e2c 100644 --- a/core/src/main/java/emu/joric/HomeScreen.java +++ b/core/src/main/java/emu/joric/HomeScreen.java @@ -829,12 +829,20 @@ private void resetState() { } private void importProgram() { - Gdx.app.postRunnable(new Runnable() { - @Override - public void run() { - importProgramUsingOpenFileDialog(); - } - }); + // Invoked synchronously from the click handler so that Safari's + // "user activation" token is still valid when + // GwtDialogHandler.openFileDialog eventually calls .click() on the + // . Safari only allows the native file picker to + // open if .click() runs in the same synchronous event stack as the + // original user-gesture event; any intervening async step (e.g. + // Gdx.app.postRunnable, which defers to the next frame) causes + // Safari to revoke the token and silently refuse the picker. + // + // Each platform's DialogHandler openFileDialog implementation + // handles its own thread affinity internally (Desktop wraps in + // Gdx.app.postRunnable for Swing, Android uses runOnUiThread), so + // calling synchronously should be safe on all platforms. + importProgramUsingOpenFileDialog(); } private void importProgramUsingOpenFileDialog() {