From eff7e35567d8fd80ac210a10fa2a6873659b97ee Mon Sep 17 00:00:00 2001 From: "taylor.smock" Date: Fri, 16 Aug 2024 12:19:50 +0000 Subject: [PATCH] See #23821: Show confirmation dialogs in the order in which the remote control commands were sent to JOSM git-svn-id: https://josm.openstreetmap.de/svn/trunk@19196 0c6e7542-c601-0410-84e7-c038aed88b3b --- .../remotecontrol/handler/RequestHandler.java | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java b/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java index 04b00fe9912..0f82544b766 100644 --- a/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java +++ b/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Pattern; @@ -26,6 +27,7 @@ import org.openstreetmap.josm.data.preferences.BooleanProperty; import org.openstreetmap.josm.data.preferences.IntegerProperty; import org.openstreetmap.josm.gui.MainApplication; +import org.openstreetmap.josm.gui.util.GuiHelper; import org.openstreetmap.josm.io.OsmApiException; import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault; import org.openstreetmap.josm.spi.preferences.Config; @@ -46,6 +48,8 @@ public abstract class RequestHandler { public static final BooleanProperty LOAD_IN_NEW_LAYER = new BooleanProperty("remotecontrol.new-layer", false); /** preference to define OSM download timeout in seconds */ public static final IntegerProperty OSM_DOWNLOAD_TIMEOUT = new IntegerProperty("remotecontrol.osm.download.timeout", 5*60); + /** A lock to ensure that messages are shown in the order the commands were sent from the server (see #23821) */ + private static final ReentrantLock MESSAGE_LOCK = new ReentrantLock(true); protected static final Pattern SPLITTER_COMMA = Pattern.compile(",\\s*"); protected static final Pattern SPLITTER_SEMIC = Pattern.compile(";\\s*"); @@ -97,7 +101,7 @@ public final void handle() throws RequestHandlerForbiddenException, RequestHandl /** * Handle a specific command sent as remote control. * Any time-consuming operation must be performed asynchronously to avoid delaying the HTTP response. - * + *

* This method of the subclass will do the real work. * * @throws RequestHandlerErrorException if an error occurs while processing request @@ -108,7 +112,7 @@ public final void handle() throws RequestHandlerForbiddenException, RequestHandl /** * Get a specific message to ask the user for permission for the operation * requested via remote control. - * + *

* This message will be displayed to the user if the preference * remotecontrol.always-confirm is true. * @@ -120,7 +124,7 @@ public final void handle() throws RequestHandlerForbiddenException, RequestHandl * Get a PermissionPref object containing the name of a special permission * preference to individually allow the requested operation and an error * message to be displayed when a disabled operation is requested. - * + *

* Default is not to check any special preference. Override this in a * subclass to define permission preference and error message. * @@ -200,18 +204,33 @@ public final void checkPermission() throws RequestHandlerForbiddenException { /* Does the user want to confirm everything? * If yes, display specific confirmation message. */ - if (GLOBAL_CONFIRMATION.get()) { + if (Boolean.TRUE.equals(GLOBAL_CONFIRMATION.get())) { // Ensure dialog box does not exceed main window size - int maxWidth = (int) Math.max(200, MainApplication.getMainFrame().getWidth() * 0.6); - String message = "

" + getPermissionMessage() + + final int maxWidth = (int) Math.max(200, MainApplication.getMainFrame().getWidth() * 0.6); + final String message = "
" + getPermissionMessage() + "
" + tr("Do you want to allow this?") + "
"; - JLabel label = new JLabel(message); - if (label.getPreferredSize().width > maxWidth) { - label.setText(message.replaceFirst("
", "
")); + final Object[] choices = {tr("Yes, always"), tr("Yes, once"), tr("No")}; + final int choice; + // The ordering of the requests can be important, so we use a fair lock to ensure ordering + // Note that the EDT will be more than happy to show multiple dialogs without blocking. + try { + MESSAGE_LOCK.lock(); + final Integer tChoice = GuiHelper.runInEDTAndWaitAndReturn(() -> { + final JLabel label = new JLabel(message); + if (label.getPreferredSize().width > maxWidth) { + label.setText(message.replaceFirst("
", "
")); + } + return JOptionPane.showOptionDialog(MainApplication.getMainFrame(), label, tr("Confirm Remote Control action"), + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, choices[1]); + }); + if (tChoice == null) { + // I have no clue how this would ever happen, but just in case. + throw new RequestHandlerForbiddenException(MessageFormat.format("RemoteControl: ''{0}'' forbidden due to NPE", myCommand)); + } + choice = tChoice; + } finally { + MESSAGE_LOCK.unlock(); } - Object[] choices = {tr("Yes, always"), tr("Yes, once"), tr("No")}; - int choice = JOptionPane.showOptionDialog(MainApplication.getMainFrame(), label, tr("Confirm Remote Control action"), - JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, choices[1]); if (choice != JOptionPane.YES_OPTION && choice != JOptionPane.NO_OPTION) { // Yes/no refer to always/once String err = MessageFormat.format("RemoteControl: ''{0}'' forbidden by user''s choice", myCommand); throw new RequestHandlerForbiddenException(err); @@ -239,7 +258,7 @@ public void setUrl(String url) throws RequestHandlerBadRequestException { /** * Parse the request parameters as key=value pairs. * The result will be stored in {@code this.args}. - * + *

* Can be overridden by subclass. * @throws URISyntaxException if request URL is invalid */