Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 12788)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 12790)
@@ -149,4 +149,5 @@
 import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.bugreport.BugReportQueue;
+import org.openstreetmap.josm.tools.bugreport.BugReportSender;
 import org.xml.sax.SAXException;
 
@@ -778,4 +779,5 @@
         if (!GraphicsEnvironment.isHeadless()) {
             BugReportQueue.getInstance().setBugReportHandler(BugReportDialog::showFor);
+            BugReportSender.setBugReportSendingHandler(BugReportDialog.bugReportSendingHandler);
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java	(revision 12788)
+++ trunk/src/org/openstreetmap/josm/gui/bugreport/BugReportDialog.java	(revision 12790)
@@ -19,4 +19,5 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
 
@@ -32,7 +33,9 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.InputMapUtils;
+import org.openstreetmap.josm.tools.OpenBrowser;
 import org.openstreetmap.josm.tools.bugreport.BugReport;
 import org.openstreetmap.josm.tools.bugreport.BugReportQueue.SuppressionMode;
 import org.openstreetmap.josm.tools.bugreport.BugReportSender;
+import org.openstreetmap.josm.tools.bugreport.BugReportSender.BugReportSendingHandler;
 import org.openstreetmap.josm.tools.bugreport.ReportedException;
 
@@ -52,4 +55,31 @@
     private JCheckBox cbSuppressSingle;
     private JCheckBox cbSuppressAll;
+
+    /**
+     * Default bug report callback that opens the bug report form in user browser
+     * and displays a dialog in case of error.
+     * @since 12790
+     */
+    public static final BugReportSendingHandler bugReportSendingHandler = new BugReportSendingHandler() {
+        @Override
+        public String sendingBugReport(String bugUrl, String statusText) {
+            return OpenBrowser.displayUrl(bugUrl);
+        }
+
+        @Override
+        public void failed(String errorMessage, String statusText) {
+            SwingUtilities.invokeLater(() -> {
+                JPanel errorPanel = new JPanel(new GridBagLayout());
+                errorPanel.add(new JMultilineLabel(
+                        tr("Opening the bug report failed. Please report manually using this website:")),
+                        GBC.eol().fill(GridBagConstraints.HORIZONTAL));
+                errorPanel.add(new UrlLabel(Main.getJOSMWebsite() + "/newticket", 2), GBC.eop().insets(8, 0, 0, 0));
+                errorPanel.add(new DebugTextDisplay(statusText));
+
+                JOptionPane.showMessageDialog(Main.parent, errorPanel, tr("You have encountered a bug in JOSM"),
+                        JOptionPane.ERROR_MESSAGE);
+            });
+        }
+    };
 
     /**
Index: trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportQueue.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportQueue.java	(revision 12788)
+++ trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportQueue.java	(revision 12790)
@@ -4,4 +4,5 @@
 import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Predicate;
@@ -18,4 +19,8 @@
     private static final BugReportQueue INSTANCE = new BugReportQueue();
 
+    /**
+     * The fallback bug report handler if none is set. Prints the stacktrace on standard output.
+     * @since 12770
+     */
     public static final BugReportHandler FALLBACK_BUGREPORT_HANDLER = (e, index) -> {
         e.printStackTrace();
@@ -142,6 +147,11 @@
     }
 
+    /**
+     * Sets the {@link BugReportHandler} for this queue.
+     * @param bugReportHandler the handler in charge of displaying the bug report. Must not be null
+     * @since 12770
+     */
     public void setBugReportHandler(BugReportHandler bugReportHandler) {
-        this.bugReportHandler = bugReportHandler;
+        this.bugReportHandler = Objects.requireNonNull(bugReportHandler, "bugReportHandler");
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportSender.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportSender.java	(revision 12788)
+++ trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportSender.java	(revision 12790)
@@ -2,8 +2,4 @@
 package org.openstreetmap.josm.tools.bugreport;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
 import java.io.IOException;
 import java.io.InputStream;
@@ -12,8 +8,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
+import java.util.Objects;
 
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.SwingUtilities;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.xpath.XPath;
@@ -23,8 +17,4 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.bugreport.DebugTextDisplay;
-import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
-import org.openstreetmap.josm.gui.widgets.UrlLabel;
-import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.HttpClient.Response;
@@ -45,4 +35,43 @@
 public class BugReportSender extends Thread {
 
+    /**
+     * Called during bug submission to JOSM bugtracker. Completes the bug report submission and handles errors.
+     * @since 12790
+     */
+    public interface BugReportSendingHandler {
+        /**
+         * Called when a bug is sent to JOSM bugtracker.
+         * @param bugUrl URL to visit to effectively submit the bug report to JOSM website
+         * @param statusText the status text being sent
+         * @return <code>null</code> for success or a string in case of an error
+         */
+        String sendingBugReport(String bugUrl, String statusText);
+
+        /**
+         * Called when a bug failed to be sent to JOSM bugtracker.
+         * @param errorMessage the error message
+         * @param statusText the status text being sent
+         */
+        void failed(String errorMessage, String statusText);
+    }
+
+    /**
+     * The fallback bug report sending handler if none is set.
+     * @since xxx
+     */
+    public static final BugReportSendingHandler FALLBACK_BUGREPORT_SENDING_HANDLER = new BugReportSendingHandler() {
+        @Override
+        public String sendingBugReport(String bugUrl, String statusText) {
+            return OpenBrowser.displayUrl(bugUrl);
+        }
+
+        @Override
+        public void failed(String errorMessage, String statusText) {
+            Logging.error("Unable to send bug report: {0}\n{1}", errorMessage, statusText);
+        }
+    };
+
+    private static BugReportSendingHandler handler = FALLBACK_BUGREPORT_SENDING_HANDLER;
+
     private final String statusText;
     private String errorMessage;
@@ -62,14 +91,16 @@
             // first, send the debug text using post.
             String debugTextPasteId = pasteDebugText();
+            String bugUrl = getJOSMTicketURL() + "?pdata_stored=" + debugTextPasteId;
 
-            // then open a browser to display the pasted text.
-            String openBrowserError = OpenBrowser.displayUrl(getJOSMTicketURL() + "?pdata_stored=" + debugTextPasteId);
-            if (openBrowserError != null) {
-                Logging.warn(openBrowserError);
-                failed(openBrowserError);
+            // then notify handler
+            errorMessage = handler.sendingBugReport(bugUrl, statusText);
+            if (errorMessage != null) {
+                Logging.warn(errorMessage);
+                handler.failed(errorMessage, statusText);
             }
         } catch (BugReportSenderException e) {
             Logging.warn(e);
-            failed(e.getMessage());
+            errorMessage = e.getMessage();
+            handler.failed(errorMessage, statusText);
         }
     }
@@ -128,19 +159,4 @@
     }
 
-    private void failed(String string) {
-        errorMessage = string;
-        SwingUtilities.invokeLater(() -> {
-            JPanel errorPanel = new JPanel(new GridBagLayout());
-            errorPanel.add(new JMultilineLabel(
-                    tr("Opening the bug report failed. Please report manually using this website:")),
-                    GBC.eol().fill(GridBagConstraints.HORIZONTAL));
-            errorPanel.add(new UrlLabel(Main.getJOSMWebsite() + "/newticket", 2), GBC.eop().insets(8, 0, 0, 0));
-            errorPanel.add(new DebugTextDisplay(statusText));
-
-            JOptionPane.showMessageDialog(Main.parent, errorPanel, tr("You have encountered a bug in JOSM"),
-                    JOptionPane.ERROR_MESSAGE);
-        });
-    }
-
     /**
      * Returns the error message that could have occured during bug sending.
@@ -171,3 +187,12 @@
         return sender;
     }
+
+    /**
+     * Sets the {@link BugReportSendingHandler} for bug report sender.
+     * @param bugReportSendingHandler the handler in charge of completing the bug report submission and handle errors. Must not be null
+     * @since 12790
+     */
+    public static void setBugReportSendingHandler(BugReportSendingHandler bugReportSendingHandler) {
+        handler = Objects.requireNonNull(bugReportSendingHandler, "bugReportSendingHandler");
+    }
 }
