changeset 11141:215e3963017f

8090930: Support mouse forward/back buttons Reviewed-by: kcr
author mennen
date Thu, 27 Dec 2018 07:51:20 -0800
parents 4e1e2f56c7af
children a34aad4d1110
files modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java modules/javafx.graphics/src/main/java/javafx/scene/Scene.java modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp modules/javafx.graphics/src/main/native-glass/win/Robot.cpp modules/javafx.graphics/src/main/native-glass/win/Utils.cpp modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java tests/manual/swt/FXCanvasMouseButtonEventsTest.java tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java
diffstat 41 files changed, 1023 insertions(+), 103 deletions(-) [+]
line wrap: on
line diff
--- a/modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/infrastructure/MouseEventFirer.java	Thu Dec 27 07:51:20 2018 -0800
@@ -194,6 +194,8 @@
                 button == MouseButton.PRIMARY,     // primary button
                 button == MouseButton.MIDDLE,      // middle button
                 button == MouseButton.SECONDARY,   // secondary button
+                button == MouseButton.BACK,        // back button
+                button == MouseButton.FORWARD,     // forward button
                 false,                             // synthesized
                 button == MouseButton.SECONDARY,   // is popup trigger
                 true,                              // still since pick
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java	Thu Dec 27 07:51:20 2018 -0800
@@ -65,6 +65,8 @@
     @Native public final static int MODIFIER_BUTTON_PRIMARY    = 1 << 5;
     @Native public final static int MODIFIER_BUTTON_SECONDARY  = 1 << 6;
     @Native public final static int MODIFIER_BUTTON_MIDDLE     = 1 << 7;
+    @Native public final static int MODIFIER_BUTTON_BACK       = 1 << 8;
+    @Native public final static int MODIFIER_BUTTON_FORWARD    = 1 << 9;
 
     /*
      * Key event key codes.
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/events/MouseEvent.java	Thu Dec 27 07:51:20 2018 -0800
@@ -31,6 +31,8 @@
     @Native final static public int BUTTON_LEFT     = 212;
     @Native final static public int BUTTON_RIGHT    = 213;
     @Native final static public int BUTTON_OTHER    = 214;
+    @Native final static public int BUTTON_BACK     = 215;
+    @Native final static public int BUTTON_FORWARD  = 216;
 
     @Native final static public int DOWN            = 221;
     @Native final static public int UP              = 222;
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/GlassRobot.java	Thu Dec 27 07:51:20 2018 -0800
@@ -44,6 +44,8 @@
     @Native public static final int MOUSE_LEFT_BTN    = 1 << 0;
     @Native public static final int MOUSE_RIGHT_BTN   = 1 << 1;
     @Native public static final int MOUSE_MIDDLE_BTN  = 1 << 2;
+    @Native public static final int MOUSE_BACK_BTN    = 1 << 3;
+    @Native public static final int MOUSE_FORWARD_BTN = 1 << 4;
 
     /**
      * Initializes any state necessary for this {@code Robot}. Called by
@@ -247,6 +249,8 @@
                 case PRIMARY: ret |= MOUSE_LEFT_BTN; break;
                 case SECONDARY: ret |= MOUSE_RIGHT_BTN; break;
                 case MIDDLE: ret |= MOUSE_MIDDLE_BTN; break;
+                case BACK: ret |= MOUSE_BACK_BTN; break;
+                case FORWARD: ret |= MOUSE_FORWARD_BTN; break;
                 default: throw new IllegalArgumentException("MouseButton: " + button + " not supported by Robot");
             }
         }
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/LinuxMouseProcessor.java	Thu Dec 27 07:51:20 2018 -0800
@@ -127,6 +127,10 @@
                 return MouseEvent.BUTTON_OTHER;
             case LinuxInput.BTN_RIGHT:
                 return MouseEvent.BUTTON_RIGHT;
+            case LinuxInput.BTN_BACK:
+                return MouseEvent.BUTTON_BACK;
+            case LinuxInput.BTN_FORWARD:
+                return MouseEvent.BUTTON_FORWARD;
             default:
                 return -1;
         }
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MonocleRobot.java	Thu Dec 27 07:51:20 2018 -0800
@@ -100,6 +100,20 @@
                         state.releaseButton(MouseEvent.BUTTON_OTHER);
                     }
                     break;
+                case BACK:
+                    if (press) {
+                        state.pressButton(MouseEvent.BUTTON_BACK);
+                    } else {
+                        state.releaseButton(MouseEvent.BUTTON_BACK);
+                    }
+                    break;
+                case FORWARD:
+                    if (press) {
+                        state.pressButton(MouseEvent.BUTTON_FORWARD);
+                    } else {
+                        state.releaseButton(MouseEvent.BUTTON_FORWARD);
+                    }
+                    break;
                 default: throw new IllegalArgumentException("MouseButton: " + button +
                         " not supported by Monocle Robot");
             }
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/MouseState.java	Thu Dec 27 07:51:20 2018 -0800
@@ -115,6 +115,12 @@
                 case MouseEvent.BUTTON_RIGHT:
                     modifiers |= KeyEvent.MODIFIER_BUTTON_SECONDARY;
                     break;
+                case MouseEvent.BUTTON_BACK:
+                    modifiers |= KeyEvent.MODIFIER_BUTTON_BACK;
+                    break;
+                case MouseEvent.BUTTON_FORWARD:
+                    modifiers |= KeyEvent.MODIFIER_BUTTON_FORWARD;
+                    break;
             }
         }
         return modifiers;
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java	Thu Dec 27 07:51:20 2018 -0800
@@ -275,6 +275,7 @@
                             final MouseState state = new MouseState();
                             state.setX(x);
                             state.setY(y);
+                            // These values are from RFC 6143 Section 7.5.5.
                             if (buttons.get(0)) {
                                 state.pressButton(MouseEvent.BUTTON_LEFT);
                             }
@@ -284,6 +285,20 @@
                             if (buttons.get(2)) {
                                 state.pressButton(MouseEvent.BUTTON_RIGHT);
                             }
+                            if (buttons.get(3)) {
+                                state.setWheel(MouseState.WHEEL_UP);
+                            }
+                            if (buttons.get(4)) {
+                                state.setWheel(MouseState.WHEEL_DOWN);
+                            }
+                            // TODO: Buttons from here on are not officially mentioned in the docs, can someone confirm
+                            // on a real device?
+                            if (buttons.get(5)) {
+                                state.pressButton(MouseEvent.BUTTON_BACK);
+                            }
+                            if (buttons.get(6)) {
+                                state.pressButton(MouseEvent.BUTTON_FORWARD);
+                            }
                             Platform.runLater(() -> MouseInput.getInstance().setState(state, false));
                             break;
                         }
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X.java	Thu Dec 27 07:51:20 2018 -0800
@@ -85,6 +85,10 @@
     static final int Button3 = 3;
     static final int Button4 = 4;
     static final int Button5 = 5;
+    // 4th button (aka browser backward button).
+    static final int Button8 = 8;
+    // 5th button (aka browser forward button).
+    static final int Button9 = 9;
 
     static final long _NET_WM_STATE_REMOVE = 0;
     static final long _NET_WM_STATE_ADD = 1;
--- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/X11InputDeviceRegistry.java	Thu Dec 27 07:51:20 2018 -0800
@@ -178,6 +178,8 @@
             case X.Button1: return MouseEvent.BUTTON_LEFT;
             case X.Button2: return MouseEvent.BUTTON_OTHER;
             case X.Button3: return MouseEvent.BUTTON_RIGHT;
+            case X.Button8: return MouseEvent.BUTTON_BACK;
+            case X.Button9: return MouseEvent.BUTTON_FORWARD;
             default: return MouseEvent.BUTTON_NONE;
         }
     }
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/AbstractEvents.java	Thu Dec 27 07:51:20 2018 -0800
@@ -57,6 +57,8 @@
     public final static int MOUSEEVENT_PRIMARY_BUTTON = 1;
     public final static int MOUSEEVENT_SECONDARY_BUTTON = 2;
     public final static int MOUSEEVENT_MIDDLE_BUTTON = 4;
+    public final static int MOUSEEVENT_BACK_BUTTON = 8;
+    public final static int MOUSEEVENT_FORWARD_BUTTON = 16;
 
     public final static int KEYEVENT_PRESSED = 0;
     public final static int KEYEVENT_RELEASED = 1;
@@ -118,6 +120,10 @@
                 return MouseButton.SECONDARY;
             case MOUSEEVENT_MIDDLE_BUTTON:
                 return MouseButton.MIDDLE;
+            case MOUSEEVENT_BACK_BUTTON:
+                return MouseButton.BACK;
+            case MOUSEEVENT_FORWARD_BUTTON:
+                return MouseButton.FORWARD;
         }
         // Should never reach here
         return MouseButton.NONE;
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/embed/EmbeddedSceneInterface.java	Thu Dec 27 07:51:20 2018 -0800
@@ -69,6 +69,7 @@
      */
     public void mouseEvent(int type, int button,
                            boolean primaryBtnDown, boolean middleBtnDown, boolean secondaryBtnDown,
+                           boolean backBtnDown, boolean forwardBtnDown,
                            int x, int y, int xAbs, int yAbs,
                            boolean shift, boolean ctrl, boolean alt, boolean meta,
                            boolean popupTrigger);
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/TKSceneListener.java	Thu Dec 27 07:51:20 2018 -0800
@@ -58,7 +58,8 @@
     public void mouseEvent(EventType<MouseEvent> type, double x, double y, double screenX, double screenY,
                            MouseButton button, boolean popupTrigger, boolean synthesized,
                            boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
-                           boolean primaryDown, boolean middleDown, boolean secondaryDown);
+                           boolean primaryDown, boolean middleDown, boolean secondaryDown,
+                           boolean backDown, boolean forwardDown);
 
     /**
      * Pass a key event to the scene to handle
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java	Thu Dec 27 07:51:20 2018 -0800
@@ -271,6 +271,7 @@
     @Override
     public void mouseEvent(final int type, final int button,
                            final boolean primaryBtnDown, final boolean middleBtnDown, final boolean secondaryBtnDown,
+                           final boolean backBtnDown, final boolean forwardBtnDown,
                            final int x, final int y, final int xAbs, final int yAbs,
                            final boolean shift, final boolean ctrl, final boolean alt, final boolean meta,
                            final boolean popupTrigger)
@@ -287,7 +288,9 @@
                             AbstractEvents.mouseButtonToFXMouseButton(button),
                             popupTrigger, false, // do we know if it's synthesized? RT-20142
                             shift, ctrl, alt, meta,
-                            primaryBtnDown, middleBtnDown, secondaryBtnDown);
+                            primaryBtnDown, middleBtnDown, secondaryBtnDown,
+                            backBtnDown, forwardBtnDown
+                        );
                 return null;
             }, getAccessControlContext());
         });
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassEventUtils.java	Thu Dec 27 07:51:20 2018 -0800
@@ -45,6 +45,10 @@
                 return "BUTTON_RIGHT";
             case MouseEvent.BUTTON_OTHER:
                 return "BUTTON_OTHER";
+            case MouseEvent.BUTTON_BACK:
+                return "BUTTON_BACK";
+            case MouseEvent.BUTTON_FORWARD:
+                return "BUTTON_FORWARD";
             case MouseEvent.DOWN:
                 return "DOWN";
             case MouseEvent.UP:
--- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassViewEventHandler.java	Thu Dec 27 07:51:20 2018 -0800
@@ -283,6 +283,10 @@
                 return MouseButton.SECONDARY;
             case com.sun.glass.events.MouseEvent.BUTTON_OTHER:
                 return MouseButton.MIDDLE;
+            case com.sun.glass.events.MouseEvent.BUTTON_BACK:
+                return MouseButton.BACK;
+            case com.sun.glass.events.MouseEvent.BUTTON_FORWARD:
+                return MouseButton.FORWARD;
             default:
                 return MouseButton.NONE;
         }
@@ -319,6 +323,12 @@
                 case MouseEvent.BUTTON_RIGHT:
                     buttonMask = KeyEvent.MODIFIER_BUTTON_SECONDARY;
                     break;
+                case MouseEvent.BUTTON_BACK:
+                    buttonMask = KeyEvent.MODIFIER_BUTTON_BACK;
+                    break;
+                case MouseEvent.BUTTON_FORWARD:
+                    buttonMask = KeyEvent.MODIFIER_BUTTON_FORWARD;
+                    break;
                 default:
                     buttonMask = 0;
                     break;
@@ -375,6 +385,8 @@
                     boolean primaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_PRIMARY) != 0;
                     boolean middleButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_MIDDLE) != 0;
                     boolean secondaryButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_SECONDARY) != 0;
+                    boolean backButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_BACK) != 0;
+                    boolean forwardButtonDown = (modifiers & KeyEvent.MODIFIER_BUTTON_FORWARD) != 0;
                     final Window w = view.getWindow();
                     double pScaleX, pScaleY, spx, spy, sx, sy;
                     if (w != null) {
@@ -399,7 +411,8 @@
                             sx + (xAbs - spx) / pScaleX, sy + (yAbs - spy) / pScaleY,
                             mouseEventButton(button), isPopupTrigger, isSynthesized,
                             shiftDown, controlDown, altDown, metaDown,
-                            primaryButtonDown, middleButtonDown, secondaryButtonDown);
+                            primaryButtonDown, middleButtonDown, secondaryButtonDown,
+                            backButtonDown, forwardButtonDown);
                 }
             } finally {
                 if (stage != null) {
--- a/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/Scene.java	Thu Dec 27 07:51:20 2018 -0800
@@ -2579,12 +2579,14 @@
         public void mouseEvent(EventType<MouseEvent> type, double x, double y, double screenX, double screenY,
                                MouseButton button, boolean popupTrigger, boolean synthesized,
                                boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
-                               boolean primaryDown, boolean middleDown, boolean secondaryDown)
+                               boolean primaryDown, boolean middleDown, boolean secondaryDown,
+                               boolean backDown, boolean forwardDown)
         {
             MouseEvent mouseEvent = new MouseEvent(type, x, y, screenX, screenY, button,
                     0, // click count will be adjusted by clickGenerator later anyway
                     shiftDown, controlDown, altDown, metaDown,
-                    primaryDown, middleDown, secondaryDown, synthesized, popupTrigger, false, null);
+                    primaryDown, middleDown, secondaryDown, backDown, forwardDown,
+                    synthesized, popupTrigger, false, null);
             processMouseEvent(mouseEvent);
         }
 
@@ -3518,7 +3520,8 @@
                 if (! e.isPrimaryButtonDown()) { counters.get(MouseButton.PRIMARY).clear(); }
                 if (! e.isSecondaryButtonDown()) { counters.get(MouseButton.SECONDARY).clear(); }
                 if (! e.isMiddleButtonDown()) { counters.get(MouseButton.MIDDLE).clear(); }
-
+                if (! e.isBackButtonDown()) { counters.get(MouseButton.BACK).clear(); }
+                if (! e.isForwardButtonDown()) { counters.get(MouseButton.FORWARD).clear(); }
                 cc.applyOut();
                 cc.inc();
                 cc.start(e.getSceneX(), e.getSceneY());
@@ -3530,6 +3533,7 @@
                     cc != null && e.getEventType() != MouseEvent.MOUSE_MOVED ? cc.get() : 0,
                     e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
                     e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(),
+                    e.isBackButtonDown(), e.isForwardButtonDown(),
                     e.isSynthesized(), e.isPopupTrigger(), still, e.getPickResult());
         }
 
@@ -3560,6 +3564,7 @@
                             cc.get(),
                             e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
                             e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(),
+                            e.isBackButtonDown(), e.isForwardButtonDown(),
                             e.isSynthesized(), e.isPopupTrigger(), lastPress.isStill(), e.getPickResult());
                     Event.fireEvent(clickedTarget, click);
                 }
@@ -3588,6 +3593,8 @@
         private boolean primaryButtonDown = false;
         private boolean secondaryButtonDown = false;
         private boolean middleButtonDown = false;
+        private boolean backButtonDown = false;
+        private boolean forwardButtonDown = false;
 
         private EventTarget fullPDRSource = null;
         private TargetWrapper fullPDRTmpTargetWrapper = new TargetWrapper();
@@ -3780,7 +3787,8 @@
             boolean gestureStarted = false;
             if (!onPulse) {
                 if (e.getEventType() == MouseEvent.MOUSE_PRESSED) {
-                    if (!(primaryButtonDown || secondaryButtonDown || middleButtonDown)) {
+                    if (!(primaryButtonDown || secondaryButtonDown || middleButtonDown ||
+                            backButtonDown || forwardButtonDown)) {
                         //old gesture ended and new one started
                         gestureStarted = true;
                         if (!PLATFORM_DRAG_GESTURE_INITIATION) {
@@ -3800,6 +3808,8 @@
                 primaryButtonDown = e.isPrimaryButtonDown();
                 secondaryButtonDown = e.isSecondaryButtonDown();
                 middleButtonDown = e.isMiddleButtonDown();
+                backButtonDown = e.isBackButtonDown();
+                forwardButtonDown = e.isForwardButtonDown();
             }
 
             pick(tmpTargetWrapper, e.getSceneX(), e.getSceneY());
@@ -3809,6 +3819,7 @@
                     e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(),
                     e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
                     e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(),
+                    e.isBackButtonDown(), e.isForwardButtonDown(),
                     e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), res);
             }
 
@@ -3885,7 +3896,8 @@
             }
 
             if (pdrInProgress &&
-                    !(primaryButtonDown || secondaryButtonDown || middleButtonDown)) {
+                    !(primaryButtonDown || secondaryButtonDown || middleButtonDown ||
+                            backButtonDown || forwardButtonDown)) {
                 clearPDREventTargets();
                 exitFullPDR(e);
                 // we need to do new picking in case the originally picked node
--- a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseButton.java	Thu Dec 27 07:51:20 2018 -0800
@@ -47,7 +47,21 @@
     MIDDLE,
 
     /**
-     * Represents seconday (button 3, usually the right) mouse button.
+     * Represents secondary (button 3, usually the right) mouse button.
      */
     SECONDARY,
+
+    /**
+     * Represents back (button 4) mouse button.
+     *
+     * @since 12
+     */
+    BACK,
+
+    /**
+     * Represents forward (button 5) mouse button.
+     *
+     * @since 12
+     */
+    FORWARD;
 }
--- a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseDragEvent.java	Thu Dec 27 07:51:20 2018 -0800
@@ -130,40 +130,104 @@
 
     /**
      * Constructs new MouseDragEvent event.
-     * @param source the source of the event. Can be null.
-     * @param target the target of the event. Can be null.
-     * @param eventType The type of the event.
-     * @param x The x with respect to the scene.
-     * @param y The y with respect to the scene.
-     * @param screenX The x coordinate relative to screen.
-     * @param screenY The y coordinate relative to screen.
+     *
+     * Both {@link #isBackButtonDown()}  and {@link #isForwardButtonDown()}
+     * will return {@literal false} for this event
+     *
+     * @param source the source of the event, which can be {@code null}
+     * @param target the target of the event, which can be {@code null}
+     * @param eventType The type of the event
+     * @param x The x with respect to the scene
+     * @param y The y with respect to the scene
+     * @param screenX The x coordinate relative to screen
+     * @param screenY The y coordinate relative to screen
      * @param button the mouse button used
      * @param clickCount number of click counts
-     * @param shiftDown true if shift modifier was pressed.
-     * @param controlDown true if control modifier was pressed.
-     * @param altDown true if alt modifier was pressed.
-     * @param metaDown true if meta modifier was pressed.
-     * @param primaryButtonDown true if primary button was pressed.
-     * @param middleButtonDown true if middle button was pressed.
-     * @param secondaryButtonDown true if secondary button was pressed.
+     * @param shiftDown true if shift modifier was pressed
+     * @param controlDown true if control modifier was pressed
+     * @param altDown true if alt modifier was pressed
+     * @param metaDown true if meta modifier was pressed
+     * @param primaryButtonDown true if primary button was pressed
+     * @param middleButtonDown true if middle button was pressed
+     * @param secondaryButtonDown true if secondary button was pressed
      * @param synthesized if this event was synthesized
      * @param popupTrigger whether this event denotes a popup trigger for current platform
      * @param pickResult pick result. Can be null, in this case a 2D pick result
      *                   without any further values is constructed
      *                   based on the scene coordinates and target
-     * @param gestureSource source object of the ongoing gesture.
+     * @param gestureSource source object of the ongoing gesture
      * @since JavaFX 8.0
      */
-    public MouseDragEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target, @NamedArg("eventType") EventType<MouseDragEvent> eventType,
-            @NamedArg("x") double x, @NamedArg("y") double y, @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
-            @NamedArg("button") MouseButton button, @NamedArg("clickCount") int clickCount,
-            @NamedArg("shiftDown") boolean shiftDown, @NamedArg("controlDown") boolean controlDown, @NamedArg("altDown") boolean altDown, @NamedArg("metaDown") boolean metaDown,
-            @NamedArg("primaryButtonDown") boolean primaryButtonDown, @NamedArg("middleButtonDown") boolean middleButtonDown, @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
-            @NamedArg("synthesized") boolean synthesized, @NamedArg("popupTrigger") boolean popupTrigger, @NamedArg("pickResult") PickResult pickResult,
-            @NamedArg("gestureSource") Object gestureSource) {
+    public MouseDragEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target,
+                          @NamedArg("eventType") EventType<MouseDragEvent> eventType,
+                          @NamedArg("x") double x, @NamedArg("y") double y,
+                          @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
+                          @NamedArg("button") MouseButton button, @NamedArg("clickCount") int clickCount,
+                          @NamedArg("shiftDown") boolean shiftDown, @NamedArg("controlDown") boolean controlDown,
+                          @NamedArg("altDown") boolean altDown, @NamedArg("metaDown") boolean metaDown,
+                          @NamedArg("primaryButtonDown") boolean primaryButtonDown,
+                          @NamedArg("middleButtonDown") boolean middleButtonDown,
+                          @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
+                          @NamedArg("synthesized") boolean synthesized,
+                          @NamedArg("popupTrigger") boolean popupTrigger,
+                          @NamedArg("pickResult") PickResult pickResult,
+                          @NamedArg("gestureSource") Object gestureSource) {
+        this(source, target, eventType, x, y, screenX, screenY, button,
+                clickCount, shiftDown, controlDown, altDown, metaDown,
+                primaryButtonDown, middleButtonDown, secondaryButtonDown,
+                false, false,
+                synthesized, popupTrigger, pickResult, gestureSource);
+    }
+
+    /**
+     * Constructs new MouseDragEvent event.
+     *
+     * @param source the source of the event, which can be {@code null}
+     * @param target the target of the event, which can be {@code null}
+     * @param eventType The type of the event
+     * @param x The x with respect to the scene
+     * @param y The y with respect to the scene
+     * @param screenX The x coordinate relative to screen
+     * @param screenY The y coordinate relative to screen
+     * @param button the mouse button used
+     * @param clickCount number of click counts
+     * @param shiftDown true if shift modifier was pressed
+     * @param controlDown true if control modifier was pressed
+     * @param altDown true if alt modifier was pressed
+     * @param metaDown true if meta modifier was pressed
+     * @param primaryButtonDown true if primary button was pressed
+     * @param middleButtonDown true if middle button was pressed
+     * @param secondaryButtonDown true if secondary button was pressed
+     * @param backButtonDown true if back button was pressed
+     * @param forwardButtonDown true if forward button was pressed
+     * @param synthesized if this event was synthesized
+     * @param popupTrigger whether this event denotes a popup trigger for current platform
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and target
+     * @param gestureSource source object of the ongoing gesture
+     * @since 12
+     */
+    public MouseDragEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target,
+                          @NamedArg("eventType") EventType<MouseDragEvent> eventType,
+                          @NamedArg("x") double x, @NamedArg("y") double y,
+                          @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
+                          @NamedArg("button") MouseButton button, @NamedArg("clickCount") int clickCount,
+                          @NamedArg("shiftDown") boolean shiftDown, @NamedArg("controlDown") boolean controlDown,
+                          @NamedArg("altDown") boolean altDown, @NamedArg("metaDown") boolean metaDown,
+                          @NamedArg("primaryButtonDown") boolean primaryButtonDown,
+                          @NamedArg("middleButtonDown") boolean middleButtonDown,
+                          @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
+                          @NamedArg("backButtonDown") boolean backButtonDown,
+                          @NamedArg("forwardButtonDown") boolean forwardButtonDown,
+                          @NamedArg("synthesized") boolean synthesized,
+                          @NamedArg("popupTrigger") boolean popupTrigger,
+                          @NamedArg("pickResult") PickResult pickResult,
+                          @NamedArg("gestureSource") Object gestureSource) {
         super(source, target, eventType, x, y, screenX, screenY, button,
                 clickCount, shiftDown, controlDown, altDown, metaDown,
                 primaryButtonDown, middleButtonDown, secondaryButtonDown,
+                backButtonDown, forwardButtonDown,
                 synthesized, popupTrigger, false, pickResult);
         this.gestureSource = gestureSource;
     }
@@ -171,35 +235,43 @@
     /**
      * Constructs new MouseDragEvent event with null source and target.
      *
-     * @param eventType The type of the event.
-     * @param x The x with respect to the scene.
-     * @param y The y with respect to the scene.
-     * @param screenX The x coordinate relative to screen.
-     * @param screenY The y coordinate relative to screen.
+     * Both {@link #isBackButtonDown()}  and {@link #isForwardButtonDown()}
+     * will return {@literal false} for this event.
+     *
+     * @param eventType The type of the event
+     * @param x The x with respect to the scene
+     * @param y The y with respect to the scene
+     * @param screenX The x coordinate relative to screen
+     * @param screenY The y coordinate relative to screen
      * @param button the mouse button used
      * @param clickCount number of click counts
-     * @param shiftDown true if shift modifier was pressed.
-     * @param controlDown true if control modifier was pressed.
-     * @param altDown true if alt modifier was pressed.
-     * @param metaDown true if meta modifier was pressed.
-     * @param primaryButtonDown true if primary button was pressed.
-     * @param middleButtonDown true if middle button was pressed.
-     * @param secondaryButtonDown true if secondary button was pressed.
+     * @param shiftDown true if shift modifier was pressed
+     * @param controlDown true if control modifier was pressed
+     * @param altDown true if alt modifier was pressed
+     * @param metaDown true if meta modifier was pressed
+     * @param primaryButtonDown true if primary button was pressed
+     * @param middleButtonDown true if middle button was pressed
+     * @param secondaryButtonDown true if secondary button was pressed
      * @param synthesized if this event was synthesized
      * @param popupTrigger whether this event denotes a popup trigger for current platform
      * @param pickResult pick result. Can be null, in this case a 2D pick result
      *                   without any further values is constructed
      *                   based on the scene coordinates
-     * @param gestureSource source object of the ongoing gesture.
+     * @param gestureSource source object of the ongoing gesture
      * @since JavaFX 8.0
      */
     public MouseDragEvent(@NamedArg("eventType") EventType<MouseDragEvent> eventType,
-            @NamedArg("x") double x, @NamedArg("y") double y, @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
-            @NamedArg("button") MouseButton button, @NamedArg("clickCount") int clickCount,
-            @NamedArg("shiftDown") boolean shiftDown, @NamedArg("controlDown") boolean controlDown, @NamedArg("altDown") boolean altDown, @NamedArg("metaDown") boolean metaDown,
-            @NamedArg("primaryButtonDown") boolean primaryButtonDown, @NamedArg("middleButtonDown") boolean middleButtonDown, @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
-            @NamedArg("synthesized") boolean synthesized, @NamedArg("popupTrigger") boolean popupTrigger, @NamedArg("pickResult") PickResult pickResult,
-            @NamedArg("gestureSource") Object gestureSource) {
+                          @NamedArg("x") double x, @NamedArg("y") double y,
+                          @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
+                          @NamedArg("button") MouseButton button, @NamedArg("clickCount") int clickCount,
+                          @NamedArg("shiftDown") boolean shiftDown, @NamedArg("controlDown") boolean controlDown,
+                          @NamedArg("altDown") boolean altDown, @NamedArg("metaDown") boolean metaDown,
+                          @NamedArg("primaryButtonDown") boolean primaryButtonDown,
+                          @NamedArg("middleButtonDown") boolean middleButtonDown,
+                          @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
+                          @NamedArg("synthesized") boolean synthesized, @NamedArg("popupTrigger") boolean popupTrigger,
+                          @NamedArg("pickResult") PickResult pickResult,
+                          @NamedArg("gestureSource") Object gestureSource) {
         this(null, null, eventType, x, y, screenX, screenY, button, clickCount,
                 shiftDown, controlDown, altDown, metaDown, primaryButtonDown,
                 middleButtonDown, secondaryButtonDown, synthesized, popupTrigger,
@@ -250,6 +322,12 @@
         if (isSecondaryButtonDown()) {
             sb.append(", secondaryButtonDown");
         }
+        if (isBackButtonDown()) {
+            sb.append(", backButtonDown");
+        }
+        if (isForwardButtonDown()) {
+            sb.append(", forwardButtonDown");
+        }
         if (isShiftDown()) {
             sb.append(", shiftDown");
         }
--- a/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/java/javafx/scene/input/MouseEvent.java	Thu Dec 27 07:51:20 2018 -0800
@@ -297,6 +297,10 @@
 
     /**
      * Constructs new MouseEvent event with null source and target.
+     *
+     * Both {@link #isBackButtonDown()}  and {@link #isForwardButtonDown()}
+     * will return {@literal false} for this event.
+     *
      * @param eventType The type of the event.
      * @param x The x with respect to the scene.
      * @param y The y with respect to the scene.
@@ -343,7 +347,63 @@
     }
 
     /**
+     * Constructs new MouseEvent event with null source and target.
+     * @param eventType The type of the event.
+     * @param x The x with respect to the scene.
+     * @param y The y with respect to the scene.
+     * @param screenX The x coordinate relative to screen.
+     * @param screenY The y coordinate relative to screen.
+     * @param button the mouse button used
+     * @param clickCount number of click counts
+     * @param shiftDown true if shift modifier was pressed.
+     * @param controlDown true if control modifier was pressed.
+     * @param altDown true if alt modifier was pressed.
+     * @param metaDown true if meta modifier was pressed.
+     * @param primaryButtonDown true if primary button was pressed.
+     * @param middleButtonDown true if middle button was pressed.
+     * @param secondaryButtonDown true if secondary button was pressed.
+     * @param backButtonDown true if back button was pressed.
+     * @param forwardButtonDown true if forward button was pressed
+     * @param synthesized if this event was synthesized
+     * @param popupTrigger whether this event denotes a popup trigger for current platform
+     * @param stillSincePress see {@link #isStillSincePress() }
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
+     * @since 12
+     */
+    public MouseEvent(
+            @NamedArg("eventType") EventType<? extends MouseEvent> eventType,
+            @NamedArg("x") double x, @NamedArg("y") double y,
+            @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
+            @NamedArg("button") MouseButton button,
+            @NamedArg("clickCount") int clickCount,
+            @NamedArg("shiftDown") boolean shiftDown,
+            @NamedArg("controlDown") boolean controlDown,
+            @NamedArg("altDown") boolean altDown,
+            @NamedArg("metaDown") boolean metaDown,
+            @NamedArg("primaryButtonDown") boolean primaryButtonDown,
+            @NamedArg("middleButtonDown") boolean middleButtonDown,
+            @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
+            @NamedArg("backButtonDown") boolean backButtonDown,
+            @NamedArg("forwardButtonDown") boolean forwardButtonDown,
+            @NamedArg("synthesized") boolean synthesized,
+            @NamedArg("popupTrigger") boolean popupTrigger,
+            @NamedArg("stillSincePress") boolean stillSincePress,
+            @NamedArg("pickResult") PickResult pickResult) {
+        this(null, null, eventType, x, y, screenX, screenY, button, clickCount,
+                shiftDown, controlDown, altDown, metaDown,
+                primaryButtonDown, middleButtonDown, secondaryButtonDown,
+                backButtonDown, forwardButtonDown,
+                synthesized, popupTrigger, stillSincePress, pickResult);
+    }
+
+    /**
      * Constructs new MouseEvent event.
+     *
+     * Both {@link #isBackButtonDown()}  and {@link #isForwardButtonDown()}
+     * will return {@literal false} for this event.
+     *
      * @param source the source of the event. Can be null.
      * @param target the target of the event. Can be null.
      * @param eventType The type of the event.
@@ -385,6 +445,59 @@
             @NamedArg("popupTrigger") boolean popupTrigger,
             @NamedArg("stillSincePress") boolean stillSincePress,
             @NamedArg("pickResult") PickResult pickResult) {
+        this(source, target, eventType, x, y, screenX, screenY, button, clickCount,
+                shiftDown, controlDown, altDown, metaDown,
+                primaryButtonDown, middleButtonDown, secondaryButtonDown, false, false,
+                synthesized, popupTrigger, stillSincePress, pickResult);
+    }
+
+    /**
+     * Constructs new MouseEvent event.
+     * @param source the source of the event. Can be null.
+     * @param target the target of the event. Can be null.
+     * @param eventType The type of the event.
+     * @param x The x with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
+     * @param y The y with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
+     * @param screenX The x coordinate relative to screen.
+     * @param screenY The y coordinate relative to screen.
+     * @param button the mouse button used
+     * @param clickCount number of click counts
+     * @param shiftDown true if shift modifier was pressed.
+     * @param controlDown true if control modifier was pressed.
+     * @param altDown true if alt modifier was pressed.
+     * @param metaDown true if meta modifier was pressed.
+     * @param primaryButtonDown true if primary button was pressed.
+     * @param middleButtonDown true if middle button was pressed.
+     * @param secondaryButtonDown true if secondary button was pressed.
+     * @param backButtonDown true if the back button was pressed
+     * @param forwardButtonDown true if the forward button was pressed
+     * @param synthesized if this event was synthesized
+     * @param popupTrigger whether this event denotes a popup trigger for current platform
+     * @param stillSincePress see {@link #isStillSincePress() }
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and target
+     * @since 12
+     */
+    public MouseEvent(@NamedArg("source") Object source, @NamedArg("target") EventTarget target,
+                      @NamedArg("eventType") EventType<? extends MouseEvent> eventType,
+                      @NamedArg("x") double x, @NamedArg("y") double y,
+                      @NamedArg("screenX") double screenX, @NamedArg("screenY") double screenY,
+                      @NamedArg("button") MouseButton button,
+                      @NamedArg("clickCount") int clickCount,
+                      @NamedArg("shiftDown") boolean shiftDown,
+                      @NamedArg("controlDown") boolean controlDown,
+                      @NamedArg("altDown") boolean altDown,
+                      @NamedArg("metaDown") boolean metaDown,
+                      @NamedArg("primaryButtonDown") boolean primaryButtonDown,
+                      @NamedArg("middleButtonDown") boolean middleButtonDown,
+                      @NamedArg("secondaryButtonDown") boolean secondaryButtonDown,
+                      @NamedArg("backButtonDown") boolean backButtonDown,
+                      @NamedArg("forwardButtonDown") boolean forwardButtonDown,
+                      @NamedArg("synthesized") boolean synthesized,
+                      @NamedArg("popupTrigger") boolean popupTrigger,
+                      @NamedArg("stillSincePress") boolean stillSincePress,
+                      @NamedArg("pickResult") PickResult pickResult) {
         super(source, target, eventType);
         this.x = x;
         this.y = y;
@@ -401,6 +514,8 @@
         this.primaryButtonDown = primaryButtonDown;
         this.middleButtonDown = middleButtonDown;
         this.secondaryButtonDown = secondaryButtonDown;
+        this.backButtonDown = backButtonDown;
+        this.forwardButtonDown = forwardButtonDown;
         this.synthesized = synthesized;
         this.stillSincePress = stillSincePress;
         this.popupTrigger = popupTrigger;
@@ -434,7 +549,7 @@
                 type, e.sceneX, e.sceneY, e.screenX, e.screenY,
                 e.button, e.clickCount, e.shiftDown, e.controlDown,
                 e.altDown, e.metaDown, e.primaryButtonDown, e.middleButtonDown,
-                e.secondaryButtonDown, e.synthesized, e.popupTrigger,
+                e.secondaryButtonDown, e.backButtonDown, e.forwardButtonDown, e.synthesized, e.popupTrigger,
                 pickResult, gestureSource);
         ev.recomputeCoordinatesToSource(e, source);
         return ev;
@@ -847,6 +962,52 @@
     }
 
     /**
+     * {@code true} if back button (button 4) is currently pressed.
+     * Note that this is different from the {@link #getButton() button} variable in
+     * that the {@code button} variable indicates which button press was
+     * responsible for this event while this variable indicates whether the
+     * back button is depressed.
+     */
+    private final boolean backButtonDown;
+
+    /**
+     * Returns {@code true} if back button (button 4)
+     * is currently pressed. Note that this is different from the
+     * {@code getButton()} method that indicates which button press was
+     * responsible for this event while this method indicates whether the
+     * back button is depressed.
+     *
+     * @return {@code true} if back button (button 4) is currently pressed
+     * @since 12
+     */
+    public final boolean isBackButtonDown() {
+        return backButtonDown;
+    }
+
+    /**
+     * {@code true} if forward button (button 5) is currently pressed.
+     * Note that this is different from the {@link #getButton() button} variable in
+     * that the {@code button} variable indicates which button press was
+     * responsible for this event while this variable indicates whether the
+     * forward button is depressed.
+     */
+    private final boolean forwardButtonDown;
+
+    /**
+     * Returns {@code true} if forward button (button 5)
+     * is currently pressed. Note that this is different from the
+     * {@code getButton()} method that indicates which button press was
+     * responsible for this event while this method indicates whether the
+     * back button is depressed.
+     *
+     * @return {@code true} if forward button (button 5) is currently pressed
+     * @since 12
+     */
+    public final boolean isForwardButtonDown() {
+        return forwardButtonDown;
+    }
+
+    /**
      * Returns a string representation of this {@code MouseEvent} object.
      * @return a string representation of this {@code MouseEvent} object.
      */
@@ -876,6 +1037,12 @@
         if (isSecondaryButtonDown()) {
             sb.append(", secondaryButtonDown");
         }
+        if (isBackButtonDown()) {
+            sb.append(", backButtonDown");
+        }
+        if (isForwardButtonDown()) {
+            sb.append(", forwardButtonDown");
+        }
         if (isShiftDown()) {
             sb.append(", shiftDown");
         }
--- a/modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/gtk/GlassRobot.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -38,6 +38,8 @@
 #include "glass_key.h"
 #include "glass_screen.h"
 
+#define MOUSE_BACK_BTN 8
+#define MOUSE_FORWARD_BTN 9
 
 static void checkXTest(JNIEnv* env) {
     int32_t major_opcode, first_event, first_error;
@@ -152,6 +154,12 @@
     if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_RIGHT_BTN) {
         XTestFakeButtonEvent(xdisplay, 3, press, CurrentTime);
     }
+    if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_BACK_BTN) {
+        XTestFakeButtonEvent(xdisplay, MOUSE_BACK_BTN, press, CurrentTime);
+    }
+    if (buttons & com_sun_glass_ui_GlassRobot_MOUSE_FORWARD_BTN) {
+        XTestFakeButtonEvent(xdisplay, MOUSE_FORWARD_BTN, press, CurrentTime);
+    }
 
     XSync(xdisplay, False);
 }
--- a/modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -293,6 +293,8 @@
     glass_mask |= (mask & GDK_BUTTON1_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY : 0;
     glass_mask |= (mask & GDK_BUTTON2_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE : 0;
     glass_mask |= (mask & GDK_BUTTON3_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY : 0;
+    glass_mask |= (mask & GDK_BUTTON4_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK : 0;
+    glass_mask |= (mask & GDK_BUTTON5_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD : 0;
     glass_mask |= (mask & GDK_SUPER_MASK) ? com_sun_glass_events_KeyEvent_MODIFIER_WINDOWS : 0; // XXX: is this OK?
 
     return glass_mask;
--- a/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -48,6 +48,9 @@
 
 #include <algorithm>
 
+#define MOUSE_BACK_BTN 8
+#define MOUSE_FORWARD_BTN 9
+
 WindowContext * WindowContextBase::sm_grab_window = NULL;
 WindowContext * WindowContextBase::sm_mouse_drag_window = NULL;
 
@@ -241,6 +244,10 @@
             return com_sun_glass_events_MouseEvent_BUTTON_OTHER;
         case 3:
             return com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
+        case MOUSE_BACK_BTN:
+            return com_sun_glass_events_MouseEvent_BUTTON_BACK;
+        case MOUSE_FORWARD_BTN:
+            return com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
         default:
             // Other buttons are not supported by quantum and are not reported by other platforms
             return com_sun_glass_events_MouseEvent_BUTTON_NONE;
@@ -265,6 +272,12 @@
         case 3:
             mask = GDK_BUTTON3_MASK;
             break;
+        case MOUSE_BACK_BTN:
+            mask = GDK_BUTTON4_MASK;
+            break;
+        case MOUSE_FORWARD_BTN:
+            mask = GDK_BUTTON5_MASK;
+            break;
     }
 
     if (press) {
@@ -290,9 +303,17 @@
     // We can grab mouse pointer for these needs.
     if (press) {
         grab_mouse_drag_focus();
-    } else if ((event->state & MOUSE_BUTTONS_MASK)
+    } else {
+        if ((event->state & MOUSE_BUTTONS_MASK)
             && !(state & MOUSE_BUTTONS_MASK)) { // all buttons released
-        ungrab_mouse_drag_focus();
+            ungrab_mouse_drag_focus();
+        } else if (event->button == 8 || event->button == 9) {
+            // GDK X backend interprets button press events for buttons 4-7 as
+            // scroll events so GDK_BUTTON4_MASK and GDK_BUTTON5_MASK will never
+            // be set on the event->state from GDK. Thus we cannot check if all
+            // buttons have been released in the usual way (as above).
+            ungrab_mouse_drag_focus();
+        }
     }
 
     jint button = gtk_button_number_to_mouse_button(event->button);
@@ -323,7 +344,9 @@
     jint isDrag = glass_modifier & (
             com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY |
             com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE |
-            com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY);
+            com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY |
+            com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK |
+            com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD);
     jint button = com_sun_glass_events_MouseEvent_BUTTON_NONE;
 
     if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY) {
@@ -332,6 +355,10 @@
         button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
     } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY) {
         button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
+    } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK) {
+        button = com_sun_glass_events_MouseEvent_BUTTON_BACK;
+    } else if (glass_modifier & com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD) {
+        button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
     }
 
     if (jview) {
--- a/modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/ios/GlassViewDelegate.m	Thu Dec 27 07:51:20 2018 -0800
@@ -501,6 +501,12 @@
             case com_sun_glass_events_MouseEvent_BUTTON_OTHER:
                 modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE;
                 break;
+            case com_sun_glass_events_MouseEvent_BUTTON_BACK:
+                modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK;
+                break;
+            case com_sun_glass_events_MouseEvent_BUTTON_FORWARD:
+                modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD;
+                break;
         }
     }
 
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m	Thu Dec 27 07:51:20 2018 -0800
@@ -218,6 +218,12 @@
     if (buttons & (1 << 2)) {
         jModifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE;
     }
+    if (buttons & (1 << 3)) {
+        jModifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK;
+    }
+    if (buttons & (1 << 4)) {
+        jModifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD;
+    }
     return jModifiers;
 }
 
--- a/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/mac/GlassViewDelegate.m	Thu Dec 27 07:51:20 2018 -0800
@@ -339,7 +339,17 @@
             break;
         case NSOtherMouseDown:
             type = com_sun_glass_events_MouseEvent_DOWN;
-            button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+            switch ([theEvent buttonNumber]) {
+                case 2:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+                    break;
+                case 3:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_BACK;
+                    break;
+                case 4:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
+                    break;
+            }
             break;
 
         case NSLeftMouseUp:
@@ -352,7 +362,17 @@
             break;
         case NSOtherMouseUp:
             type = com_sun_glass_events_MouseEvent_UP;
-            button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+            switch ([theEvent buttonNumber]) {
+                case 2:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+                    break;
+                case 3:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_BACK;
+                    break;
+                case 4:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
+                    break;
+            }
             break;
 
         case NSLeftMouseDragged:
@@ -365,7 +385,17 @@
             break;
         case NSOtherMouseDragged:
             type = com_sun_glass_events_MouseEvent_DRAG;
-            button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+            switch ([theEvent buttonNumber]) {
+                case 2:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+                    break;
+                case 3:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_BACK;
+                    break;
+                case 4:
+                    button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
+                    break;
+            }
             break;
 
         case NSMouseMoved:
@@ -491,6 +521,12 @@
                     case com_sun_glass_events_MouseEvent_BUTTON_OTHER:
                         modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE;
                         break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_BACK:
+                        modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK;
+                        break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_FORWARD:
+                        modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD;
+                        break;
                 }
             }
         }
@@ -515,9 +551,11 @@
             // prepare GlassDragSource for possible drag,
             case com_sun_glass_events_MouseEvent_DOWN:
                 switch (button) {
-                    case com_sun_glass_events_MouseEvent_BUTTON_LEFT:  self->mouseDownMask |= 1 << 0; break;
-                    case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask |= 1 << 1; break;
-                    case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask |= 1 << 2; break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_LEFT:    self->mouseDownMask |= 1 << 0; break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_RIGHT:   self->mouseDownMask |= 1 << 1; break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_OTHER:   self->mouseDownMask |= 1 << 2; break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_BACK:    self->mouseDownMask |= 1 << 3; break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: self->mouseDownMask |= 1 << 4; break;
                 }
                 //fall through
             case com_sun_glass_events_MouseEvent_DRAG:
@@ -529,9 +567,11 @@
                 break;
             case com_sun_glass_events_MouseEvent_UP:
                 switch (button) {
-                    case com_sun_glass_events_MouseEvent_BUTTON_LEFT:  self->mouseDownMask &= ~(1 << 0); break;
-                    case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask &= ~(1 << 1); break;
-                    case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask &= ~(1 << 2); break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_LEFT:    self->mouseDownMask &= ~(1 << 0); break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_RIGHT:   self->mouseDownMask &= ~(1 << 1); break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_OTHER:   self->mouseDownMask &= ~(1 << 2); break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_BACK:    self->mouseDownMask &= ~(1 << 3); break;
+                    case com_sun_glass_events_MouseEvent_BUTTON_FORWARD: self->mouseDownMask &= ~(1 << 4); break;
                 }
                 break;
 
@@ -988,7 +1028,7 @@
 static jstring convertNSStringToJString(id aString, int length)
 {
     GET_MAIN_JENV;
-    
+
     jstring jStr;
     if ([aString isKindOfClass:[NSAttributedString class]]) {
         NSData *data = [[aString string] dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
@@ -1001,7 +1041,7 @@
     } else {
         jStr = (*env)->NewStringUTF(env, [aString UTF8String]);
     }
-    
+
     GLASS_CHECK_EXCEPTION(env);
 
     return jStr;
--- a/modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/win/FullScreenWindow.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -323,6 +323,9 @@
         case WM_MBUTTONDOWN:
         case WM_MBUTTONUP:
         case WM_MBUTTONDBLCLK:
+        case WM_XBUTTONDOWN:
+        case WM_XBUTTONUP:
+        case WM_XBUTTONDBLCLK:
         case WM_MOUSEWHEEL:
         case WM_MOUSEHWHEEL:
         case WM_MOUSELEAVE: {
--- a/modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/win/GlassDnD.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -316,6 +316,12 @@
     case com_sun_glass_events_MouseEvent_BUTTON_OTHER:
         m_button = MK_MBUTTON;
         break;
+    case com_sun_glass_events_MouseEvent_BUTTON_BACK:
+        m_button = MK_XBUTTON1;
+        break;
+    case com_sun_glass_events_MouseEvent_BUTTON_FORWARD:
+        m_button = MK_XBUTTON2;
+        break;
     default:
         m_button = 0;
         break;
--- a/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/win/GlassWindow.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -291,12 +291,15 @@
         case WM_LBUTTONDOWN: return "WM_LBUTTONDOWN";
         case WM_RBUTTONDOWN: return "WM_RBUTTONDOWN";
         case WM_MBUTTONDOWN: return "WM_MBUTTONDOWN";
+        case WM_XBUTTONDOWN: return "WM_XBUTTONDOWN";
         case WM_LBUTTONUP: return "WM_LBUTTONUP";
         case WM_LBUTTONDBLCLK: return "WM_LBUTTONDBLCLK";
         case WM_RBUTTONUP: return "WM_RBUTTONUP";
         case WM_RBUTTONDBLCLK: return "WM_RBUTTONDBLCLK";
         case WM_MBUTTONUP: return "WM_MBUTTONUP";
         case WM_MBUTTONDBLCLK: return "WM_MBUTTONDBLCLK";
+        case WM_XBUTTONUP: return "WM_XBUTTONUP";
+        case WM_XBUTTONDBLCLK: return "WM_XBUTTONDBLCLK";
         case WM_MOUSEWHEEL: return "WM_MOUSEWHEEL";
         case WM_MOUSEHWHEEL: return "WM_MOUSEHWHEEL";
         case WM_MOUSELEAVE: return "WM_MOUSELEAVE";
@@ -499,6 +502,7 @@
         case WM_LBUTTONDOWN:
         case WM_RBUTTONDOWN:
         case WM_MBUTTONDOWN:
+        case WM_XBUTTONDOWN:
             CheckUngrab(); // check if other owned windows hierarchy holds the grab
             if (IsChild() && !IsFocused() && IsFocusable()) {
                 RequestFocus(com_sun_glass_events_WindowEvent_FOCUS_GAINED);
@@ -510,6 +514,8 @@
         case WM_RBUTTONDBLCLK:
         case WM_MBUTTONUP:
         case WM_MBUTTONDBLCLK:
+        case WM_XBUTTONUP:
+        case WM_XBUTTONDBLCLK:
         case WM_MOUSEWHEEL:
         case WM_MOUSEHWHEEL:
         case WM_MOUSELEAVE:
--- a/modules/javafx.graphics/src/main/native-glass/win/Robot.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/win/Robot.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -154,6 +154,8 @@
     (JNIEnv *env, jobject jrobot, jint buttons)
 {
     DWORD dwFlags = 0L;
+    DWORD mouseFlags = 0L;
+
     // According to MSDN: Software Driving Software
     // application should consider SM_SWAPBUTTON to correctly emulate user with
     // left handed mouse setup
@@ -168,21 +170,21 @@
     if (buttons & (1 << 2)) {
         dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
     }
+    // Support for extra buttons
+    if (buttons & (1 << 3)) {
+        dwFlags |= MOUSEEVENTF_XDOWN;
+        mouseFlags |= XBUTTON1;
+    }
+    if (buttons & (1 << 4)) {
+        dwFlags |= MOUSEEVENTF_XDOWN;
+        mouseFlags |= XBUTTON2;
+    }
 
     INPUT mouseInput = {0};
     mouseInput.type = INPUT_MOUSE;
     mouseInput.mi.time = 0;
     mouseInput.mi.dwFlags = dwFlags;
-
-    // Support for extra buttons
-    if (buttons & (1 << 3)) {
-        mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN;
-        mouseInput.mi.mouseData = XBUTTON1;
-    }
-    if (buttons & (1 << 4)) {
-        mouseInput.mi.dwFlags |= MOUSEEVENTF_XDOWN;
-        mouseInput.mi.mouseData = XBUTTON2;
-    }
+    mouseInput.mi.mouseData = mouseFlags;
 
     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 }
@@ -196,6 +198,8 @@
     (JNIEnv *env, jobject jrobot, jint buttons)
 {
     DWORD dwFlags = 0L;
+    DWORD mouseFlags = 0L;
+
     // According to MSDN: Software Driving Software
     // application should consider SM_SWAPBUTTON to correctly emulate user with
     // left handed mouse setup
@@ -210,21 +214,21 @@
     if (buttons & (1 << 2)) {
         dwFlags |= MOUSEEVENTF_MIDDLEUP;
     }
+    // Support for extra buttons
+    if (buttons & (1 << 3)) {
+        dwFlags |= MOUSEEVENTF_XUP;
+        mouseFlags |= XBUTTON1;
+    }
+    if (buttons & (1 << 4)) {
+        dwFlags |= MOUSEEVENTF_XUP;
+        mouseFlags |= XBUTTON2;
+    }
 
     INPUT mouseInput = {0};
     mouseInput.type = INPUT_MOUSE;
     mouseInput.mi.time = 0;
     mouseInput.mi.dwFlags = dwFlags;
-
-    // Support for extra buttons
-    if (buttons & (1 << 3)) {
-        mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP;
-        mouseInput.mi.mouseData = XBUTTON1;
-    }
-    if (buttons & (1 << 4)) {
-        mouseInput.mi.dwFlags |= MOUSEEVENTF_XUP;
-        mouseInput.mi.mouseData = XBUTTON2;
-    }
+    mouseInput.mi.mouseData = mouseFlags;
 
     ::SendInput(1, &mouseInput, sizeof(mouseInput));
 }
--- a/modules/javafx.graphics/src/main/native-glass/win/Utils.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/win/Utils.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -97,6 +97,12 @@
     if (HIBYTE(::GetKeyState(VK_LBUTTON)) != 0) {
         modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY;
     }
+    if (HIBYTE(::GetKeyState(VK_XBUTTON1)) != 0) {
+        modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_BACK;
+    }
+    if (HIBYTE(::GetKeyState(VK_XBUTTON2)) != 0) {
+        modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_FORWARD;
+    }
 
     return modifiers;
 }
--- a/modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/win/ViewContainer.cpp	Thu Dec 27 07:51:20 2018 -0800
@@ -781,7 +781,7 @@
                     m_lastMouseMovePosition = lParam;
                 }
                 // See RT-11305 regarding the GetCapture() check
-                if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) != 0 && ::GetCapture() == hwnd) {
+                if ((wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2)) != 0 && ::GetCapture() == hwnd) {
                     type = com_sun_glass_events_MouseEvent_DRAG;
                 } else {
                     type = com_sun_glass_events_MouseEvent_MOVE;
@@ -795,6 +795,10 @@
                     button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
                 } else if (wParam & MK_MBUTTON) {
                     button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
+                } else if (wParam & MK_XBUTTON1) {
+                    button = com_sun_glass_events_MouseEvent_BUTTON_BACK;
+                } else if (wParam & MK_XBUTTON2) {
+                    button = com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
                 }
                 break;
             case WM_LBUTTONDOWN:
@@ -821,6 +825,16 @@
                 type = com_sun_glass_events_MouseEvent_UP;
                 button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
                 break;
+            case WM_XBUTTONDOWN:
+                type = com_sun_glass_events_MouseEvent_DOWN;
+                button = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? com_sun_glass_events_MouseEvent_BUTTON_BACK :
+                            com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
+                break;
+            case WM_XBUTTONUP:
+                type = com_sun_glass_events_MouseEvent_UP;
+                button = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? com_sun_glass_events_MouseEvent_BUTTON_BACK :
+                            com_sun_glass_events_MouseEvent_BUTTON_FORWARD;
+                break;
             case WM_MOUSEWHEEL:
             case WM_MOUSEHWHEEL:
                 {
--- a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseDragEventTest.java	Thu Dec 27 07:51:20 2018 -0800
@@ -79,6 +79,8 @@
         assertFalse(e.isPrimaryButtonDown());
         assertTrue(e.isMiddleButtonDown());
         assertFalse(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
         assertTrue(e.isSynthesized());
         assertFalse(e.isPopupTrigger());
         assertSame(gsrc, e.getGestureSource());
@@ -160,6 +162,8 @@
         assertFalse(e.isPrimaryButtonDown());
         assertTrue(e.isMiddleButtonDown());
         assertFalse(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
         assertTrue(e.isSynthesized());
         assertFalse(e.isPopupTrigger());
         assertSame(gsrc, e.getGestureSource());
@@ -187,7 +191,6 @@
         assertTrue(e.isPopupTrigger());
     }
 
-
     @Test public void testLongConstructorWithoutPickResult() {
         Rectangle n1 = new Rectangle(10, 10);
         Rectangle n2 = new Rectangle(10, 10);
@@ -212,6 +215,135 @@
         assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20);
     }
 
+    @Test public void testFullConstructor() {
+        Rectangle n1 = new Rectangle(10, 10);
+        Rectangle n2 = new Rectangle(10, 10);
+        Rectangle node = new Rectangle();
+        node.setTranslateX(3);
+        node.setTranslateY(2);
+        node.setTranslateZ(50);
+        Rectangle gsrc = new Rectangle();
+
+        PickResult pickRes = new PickResult(node, new Point3D(15, 25, 100), 33);
+
+        MouseDragEvent e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER,
+                10, 20, 30, 40, MouseButton.MIDDLE, 3,
+                true, false, false, true,
+                false, true, false, true, true,
+                true, false, pickRes, gsrc);
+
+        assertSame(MouseDragEvent.MOUSE_DRAG_OVER, e.getEventType());
+        assertEquals(18, e.getX(), 10e-20);
+        assertEquals(27, e.getY(), 10e-20);
+        assertEquals(150, e.getZ(), 10e-20);
+        assertEquals(10, e.getSceneX(), 10e-20);
+        assertEquals(20, e.getSceneY(), 10e-20);
+        assertEquals(30, e.getScreenX(), 10e-20);
+        assertEquals(40, e.getScreenY(), 10e-20);
+        assertSame(MouseButton.MIDDLE, e.getButton());
+        assertEquals(3, e.getClickCount());
+        assertTrue(e.isShiftDown());
+        assertFalse(e.isControlDown());
+        assertFalse(e.isAltDown());
+        assertTrue(e.isMetaDown());
+        assertFalse(e.isPrimaryButtonDown());
+        assertTrue(e.isMiddleButtonDown());
+        assertFalse(e.isSecondaryButtonDown());
+        assertTrue(e.isBackButtonDown());
+        assertTrue(e.isForwardButtonDown());
+        assertTrue(e.isSynthesized());
+        assertFalse(e.isPopupTrigger());
+        assertSame(gsrc, e.getGestureSource());
+        assertFalse(e.isConsumed());
+        assertSame(pickRes, e.getPickResult());
+        assertSame(n1, e.getSource());
+        assertSame(n2, e.getTarget());
+
+        e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER,
+                10, 20, 30, 40, MouseButton.MIDDLE, 3,
+                false, true, true, false,
+                true, false, true, true, false,
+                false, true, pickRes, gsrc);
+
+        assertSame(n1, e.getSource());
+        assertSame(n2, e.getTarget());
+        assertFalse(e.isShiftDown());
+        assertTrue(e.isControlDown());
+        assertTrue(e.isAltDown());
+        assertFalse(e.isMetaDown());
+        assertTrue(e.isPrimaryButtonDown());
+        assertFalse(e.isMiddleButtonDown());
+        assertTrue(e.isSecondaryButtonDown());
+        assertTrue(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
+        assertFalse(e.isSynthesized());
+        assertTrue(e.isPopupTrigger());
+
+        e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER,
+                10, 20, 30, 40, MouseButton.MIDDLE, 3,
+                false, true, true, false,
+                true, false, true, false, true,
+                false, true, pickRes, gsrc);
+
+        assertSame(n1, e.getSource());
+        assertSame(n2, e.getTarget());
+        assertFalse(e.isShiftDown());
+        assertTrue(e.isControlDown());
+        assertTrue(e.isAltDown());
+        assertFalse(e.isMetaDown());
+        assertTrue(e.isPrimaryButtonDown());
+        assertFalse(e.isMiddleButtonDown());
+        assertTrue(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertTrue(e.isForwardButtonDown());
+        assertFalse(e.isSynthesized());
+        assertTrue(e.isPopupTrigger());
+
+        e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER,
+                10, 20, 30, 40, MouseButton.MIDDLE, 3,
+                false, true, true, false,
+                true, false, true, false, false,
+                false, true, pickRes, gsrc);
+
+        assertSame(n1, e.getSource());
+        assertSame(n2, e.getTarget());
+        assertFalse(e.isShiftDown());
+        assertTrue(e.isControlDown());
+        assertTrue(e.isAltDown());
+        assertFalse(e.isMetaDown());
+        assertTrue(e.isPrimaryButtonDown());
+        assertFalse(e.isMiddleButtonDown());
+        assertTrue(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
+        assertFalse(e.isSynthesized());
+        assertTrue(e.isPopupTrigger());
+    }
+
+    @Test public void testFullConstructorWithoutPickResult() {
+        Rectangle n1 = new Rectangle(10, 10);
+        Rectangle n2 = new Rectangle(10, 10);
+        MouseDragEvent e = new MouseDragEvent(n1, n2, MouseDragEvent.MOUSE_DRAG_OVER,
+                10, 20, 30, 40, MouseButton.MIDDLE, 3,
+                true, false, false, true,
+                false, true, false, true, true,
+                true, false, null, new Rectangle());
+        assertSame(n1, e.getSource());
+        assertSame(n2, e.getTarget());
+        assertEquals(10, e.getX(), 10e-20);
+        assertEquals(20, e.getY(), 10e-20);
+        assertEquals(0, e.getZ(), 10e-20);
+        assertEquals(10, e.getSceneX(), 10e-20);
+        assertEquals(20, e.getSceneY(), 10e-20);
+        assertEquals(30, e.getScreenX(), 10e-20);
+        assertEquals(40, e.getScreenY(), 10e-20);
+        assertNotNull(e.getPickResult());
+        assertNotNull(e.getPickResult().getIntersectedPoint());
+        assertEquals(10, e.getPickResult().getIntersectedPoint().getX(), 10e-20);
+        assertEquals(20, e.getPickResult().getIntersectedPoint().getY(), 10e-20);
+        assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20);
+    }
+
     @Test
     public void fullPDRShouldNotStartAutomatically() {
         World w = new World(false, false);
--- a/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/input/MouseEventTest.java	Thu Dec 27 07:51:20 2018 -0800
@@ -81,6 +81,8 @@
         assertFalse(e.isPrimaryButtonDown());
         assertTrue(e.isMiddleButtonDown());
         assertFalse(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
         assertTrue(e.isSynthesized());
         assertFalse(e.isPopupTrigger());
         assertTrue(e.isStillSincePress());
@@ -102,6 +104,8 @@
         assertTrue(e.isPrimaryButtonDown());
         assertFalse(e.isMiddleButtonDown());
         assertTrue(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
         assertFalse(e.isSynthesized());
         assertTrue(e.isPopupTrigger());
         assertFalse(e.isStillSincePress());
@@ -164,6 +168,8 @@
         assertFalse(e.isPrimaryButtonDown());
         assertTrue(e.isMiddleButtonDown());
         assertFalse(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
         assertTrue(e.isSynthesized());
         assertFalse(e.isPopupTrigger());
         assertTrue(e.isStillSincePress());
@@ -185,12 +191,13 @@
         assertTrue(e.isPrimaryButtonDown());
         assertFalse(e.isMiddleButtonDown());
         assertTrue(e.isSecondaryButtonDown());
+        assertFalse(e.isBackButtonDown());
+        assertFalse(e.isForwardButtonDown());
         assertFalse(e.isSynthesized());
         assertTrue(e.isPopupTrigger());
         assertFalse(e.isStillSincePress());
     }
 
-
     @Test public void testLongConstructorWithoutPickResult() {
         Rectangle n1 = new Rectangle(10, 10);
         Rectangle n2 = new Rectangle(10, 10);
@@ -215,6 +222,31 @@
         assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20);
     }
 
+    @Test public void testFullConstructorWithoutPickResult() {
+        Rectangle n1 = new Rectangle(10, 10);
+        Rectangle n2 = new Rectangle(10, 10);
+        MouseEvent e = new MouseEvent(n1, n2, MouseEvent.MOUSE_DRAGGED,
+                10, 20, 30, 40, MouseButton.MIDDLE, 3,
+                true, false, false, true,
+                false, true, false,
+                false, false, true,
+                false, true, null);
+        assertSame(n1, e.getSource());
+        assertSame(n2, e.getTarget());
+        assertEquals(10, e.getX(), 10e-20);
+        assertEquals(20, e.getY(), 10e-20);
+        assertEquals(0, e.getZ(), 10e-20);
+        assertEquals(10, e.getSceneX(), 10e-20);
+        assertEquals(20, e.getSceneY(), 10e-20);
+        assertEquals(30, e.getScreenX(), 10e-20);
+        assertEquals(40, e.getScreenY(), 10e-20);
+        assertNotNull(e.getPickResult());
+        assertNotNull(e.getPickResult().getIntersectedPoint());
+        assertEquals(10, e.getPickResult().getIntersectedPoint().getX(), 10e-20);
+        assertEquals(20, e.getPickResult().getIntersectedPoint().getY(), 10e-20);
+        assertEquals(0, e.getPickResult().getIntersectedPoint().getZ(), 10e-20);
+    }
+
     @Test
     public void shouldCreateDoubleClickMouseEvent() {
         /* constructor called during initialization */
@@ -234,6 +266,8 @@
         assertFalse(doubleclick.isPrimaryButtonDown());
         assertTrue(doubleclick.isMiddleButtonDown());
         assertFalse(doubleclick.isSecondaryButtonDown());
+        assertFalse(doubleclick.isBackButtonDown());
+        assertFalse(doubleclick.isForwardButtonDown());
         assertSame(MouseEvent.MOUSE_CLICKED, doubleclick.getEventType());
         assertSame(MouseEvent.NULL_SOURCE_TARGET, doubleclick.getSource());
     }
@@ -259,6 +293,8 @@
         assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown());
         assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown());
         assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown());
+        assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown());
+        assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown());
         assertSame(doubleclick.getEventType(), copy.getEventType());
         assertSame(node1, copy.getSource());
         assertSame(node2, copy.getTarget());
@@ -286,6 +322,8 @@
         assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown());
         assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown());
         assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown());
+        assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown());
+        assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown());
         assertSame(MouseEvent.MOUSE_ENTERED, copy.getEventType());
         assertSame(node1, copy.getSource());
         assertSame(node2, copy.getTarget());
@@ -313,6 +351,8 @@
         assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown());
         assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown());
         assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown());
+        assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown());
+        assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown());
         assertSame(doubleclick.getEventType(), copy.getEventType());
         assertSame(node2, copy.getSource());
         assertSame(node1, copy.getTarget());
@@ -339,6 +379,8 @@
         assertEquals(doubleclick.isPrimaryButtonDown(), copy.isPrimaryButtonDown());
         assertEquals(doubleclick.isMiddleButtonDown(), copy.isMiddleButtonDown());
         assertEquals(doubleclick.isSecondaryButtonDown(), copy.isSecondaryButtonDown());
+        assertEquals(doubleclick.isBackButtonDown(), copy.isBackButtonDown());
+        assertEquals(doubleclick.isForwardButtonDown(), copy.isForwardButtonDown());
         assertSame(doubleclick.getEventType(), copy.getEventType());
         assertSame(node1, copy.getSource());
         assertSame(node2, copy.getTarget());
--- a/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.swing/src/main/java/com/sun/javafx/embed/swing/SwingEvents.java	Thu Dec 27 07:51:20 2018 -0800
@@ -71,6 +71,12 @@
             case MouseEvent.BUTTON3:
                 abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON;
                 break;
+            case 4:
+                abstractButton = AbstractEvents.MOUSEEVENT_BACK_BUTTON;
+                break;
+            case 5:
+                abstractButton = AbstractEvents.MOUSEEVENT_FORWARD_BUTTON;
+                break;
             default:
                 break;
         }
@@ -81,6 +87,10 @@
             abstractButton = AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON;
         } else if ((extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
             abstractButton = AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON;
+        } else if ((extModifiers & MouseEvent.getMaskForButton(4)) != 0) {
+            abstractButton = AbstractEvents.MOUSEEVENT_BACK_BUTTON;
+        } else if ((extModifiers & MouseEvent.getMaskForButton(5)) != 0) {
+            abstractButton = AbstractEvents.MOUSEEVENT_FORWARD_BUTTON;
         }
         return abstractButton;
     }
@@ -180,6 +190,12 @@
         if (event.isMiddleButtonDown()) {
             mods |= MouseEvent.BUTTON2_DOWN_MASK;
         }
+        if (event.isBackButtonDown()) {
+            mods |= MouseEvent.getMaskForButton(4);
+        }
+        if (event.isForwardButtonDown()) {
+            mods |= MouseEvent.getMaskForButton(5);
+        }
         return mods;
     }
 
@@ -191,6 +207,10 @@
                 return MouseEvent.BUTTON3;
             case MIDDLE:
                 return MouseEvent.BUTTON2;
+            case BACK:
+                return 4;
+            case FORWARD:
+                return 5;
         }
         return 0;
     }
--- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java	Thu Dec 27 07:51:20 2018 -0800
@@ -363,12 +363,12 @@
             return;
         }
 
-        // FX only supports 3 buttons so don't send the event for other buttons
+        // FX only supports 5 buttons so don't send the event for other buttons
         switch (e.getID()) {
             case MouseEvent.MOUSE_DRAGGED:
             case MouseEvent.MOUSE_PRESSED:
             case MouseEvent.MOUSE_RELEASED:
-                if (e.getButton() > 3)  return;
+                if (e.getButton() > 5)  return;
                 break;
         }
 
@@ -378,6 +378,9 @@
         boolean primaryBtnDown = (extModifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0;
         boolean middleBtnDown = (extModifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0;
         boolean secondaryBtnDown = (extModifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0;
+        boolean backBtnDown = (extModifiers & MouseEvent.getMaskForButton(4)) != 0;
+        boolean forwardBtnDown = (extModifiers & MouseEvent.getMaskForButton(5)) != 0;
+
         // Fix for RT-16558: if a PRESSED event is consumed, e.g. by a Swing Popup,
         // subsequent DRAGGED and RELEASED events should not be sent to FX as well
         if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
@@ -390,7 +393,7 @@
             if (!isCapturingMouse) {
                 return;
             }
-            isCapturingMouse = primaryBtnDown || middleBtnDown || secondaryBtnDown;
+            isCapturingMouse = primaryBtnDown || middleBtnDown || secondaryBtnDown || backBtnDown || forwardBtnDown;
         } else if (e.getID() == MouseEvent.MOUSE_CLICKED) {
             // Don't send click events to FX, as they are generated in Scene
             return;
@@ -417,6 +420,7 @@
                     SwingEvents.mouseIDToEmbedMouseType(e.getID()),
                     SwingEvents.mouseButtonToEmbedMouseButton(e.getButton(), extModifiers),
                     primaryBtnDown, middleBtnDown, secondaryBtnDown,
+                    backBtnDown, forwardBtnDown,
                     e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(),
                     (extModifiers & MouseEvent.SHIFT_DOWN_MASK) != 0,
                     (extModifiers & MouseEvent.CTRL_DOWN_MASK) != 0,
--- a/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.swt/src/main/java/javafx/embed/swt/FXCanvas.java	Thu Dec 27 07:51:20 2018 -0800
@@ -488,22 +488,22 @@
             }
             @Override
             public void mouseDown(MouseEvent me) {
-                // FX only supports 3 buttons so don't send the event for other buttons
-                if (me.button > 3) return;
+                // FX only supports 5 buttons so don't send the event for other buttons
+                if (me.button > 5) return;
                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_PRESSED);
             }
             @Override
             public void mouseUp(MouseEvent me) {
-                // FX only supports 3 buttons so don't send the event for other buttons
-                if (me.button > 3) return;
+                // FX only supports 5 buttons so don't send the event for other buttons
+                if (me.button > 5) return;
                 FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_RELEASED);
             }
         });
 
         addMouseMoveListener(me -> {
             if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
-                // FX only supports 3 buttons so don't send the event for other buttons
-                if ((me.stateMask & (SWT.BUTTON1 | SWT.BUTTON2 | SWT.BUTTON3)) != 0) {
+                // FX only supports 5 buttons so don't send the event for other buttons
+                if ((me.stateMask & (SWT.BUTTON1 | SWT.BUTTON2 | SWT.BUTTON3 | SWT.BUTTON4 | SWT.BUTTON5)) != 0) {
                     FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_DRAGGED);
                 } else {
                     FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
@@ -689,6 +689,8 @@
         boolean primaryBtnDown = (me.stateMask & SWT.BUTTON1) != 0;
         boolean middleBtnDown = (me.stateMask & SWT.BUTTON2) != 0;
         boolean secondaryBtnDown = (me.stateMask & SWT.BUTTON3) != 0;
+        boolean backBtnDown = (me.stateMask & SWT.BUTTON4) != 0;
+        boolean forwardBtnDown = (me.stateMask & SWT.BUTTON5) != 0;
         boolean shift = (me.stateMask & SWT.SHIFT) != 0;
         boolean control = (me.stateMask & SWT.CONTROL) != 0;
         boolean alt = (me.stateMask & SWT.ALT) != 0;
@@ -699,11 +701,15 @@
                 primaryBtnDown |= me.button == 1;
                 middleBtnDown |= me.button == 2;
                 secondaryBtnDown |= me.button == 3;
+                backBtnDown |= me.button == 4;
+                forwardBtnDown |= me.button == 5;
                 break;
             case AbstractEvents.MOUSEEVENT_RELEASED:
                 primaryBtnDown &= me.button != 1;
                 middleBtnDown &= me.button != 2;
                 secondaryBtnDown &= me.button != 3;
+                backBtnDown &= me.button == 4;
+                forwardBtnDown &= me.button == 5;
                 break;
             case AbstractEvents.MOUSEEVENT_CLICKED:
                 // Don't send click events to FX, as they are generated in Scene
@@ -723,6 +729,10 @@
                         button = 2;
                     } else if ((me.stateMask & SWT.BUTTON3) != 0) {
                         button = 3;
+                    } else if ((me.stateMask & SWT.BUTTON4) != 0) {
+                        button = 4;
+                    } else if ((me.stateMask & SWT.BUTTON5) != 0) {
+                        button = 5;
                     }
                 }
                 break;
@@ -735,6 +745,7 @@
                 embedMouseType,
                 SWTEvents.mouseButtonToEmbedMouseButton(button, me.stateMask),
                 primaryBtnDown, middleBtnDown, secondaryBtnDown,
+                backBtnDown, forwardBtnDown,
                 me.x, me.y,
                 los.x, los.y,
                 shift, control, alt, meta,
--- a/modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/modules/javafx.swt/src/main/java/javafx/embed/swt/SWTEvents.java	Thu Dec 27 07:51:20 2018 -0800
@@ -69,6 +69,8 @@
             case 1: return AbstractEvents.MOUSEEVENT_PRIMARY_BUTTON;
             case 2: return AbstractEvents.MOUSEEVENT_MIDDLE_BUTTON;
             case 3: return AbstractEvents.MOUSEEVENT_SECONDARY_BUTTON;
+            case 4: return AbstractEvents.MOUSEEVENT_BACK_BUTTON;
+            case 5: return AbstractEvents.MOUSEEVENT_FORWARD_BUTTON;
         }
         return AbstractEvents.MOUSEEVENT_NONE_BUTTON;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/manual/swt/FXCanvasMouseButtonEventsTest.java	Thu Dec 27 07:51:20 2018 -0800
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javafx.embed.swt.FXCanvas;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextArea;
+import javafx.scene.layout.VBox;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+public class FXCanvasMouseButtonEventsTest {
+
+    static final String instructions =
+            "This tests that SWT mouse button events (press, release, and click) are properly transferred to SWT. " +
+                    "It passes if all mouse button events for each of the 5 buttons are recognized properly.";
+
+    private static TextArea createInfo(String msg) {
+        TextArea t = new TextArea(msg);
+        t.setWrapText(true);
+        t.setEditable(false);
+        t.setMaxWidth(400);
+        t.setMaxHeight(100);
+        return t;
+    }
+
+    public static void main(String[] args) {
+        final Display display = new Display();
+        final Shell shell = new Shell(display);
+        shell.setText("FXCanvasMouseButtonEventsTest");
+        shell.setSize(400, 400);
+        shell.setLayout(new FillLayout());
+        final FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
+        shell.open();
+
+        // create and hook scene
+        Group root = new Group();
+
+        TextArea info = createInfo(instructions);
+        Label clickOutput = new Label("No click events yet...");
+        Label pressOutput = new Label("No press events yet...");
+        Label releaseOutput = new Label("No release events yet...");
+
+        VBox vbox = new VBox();
+        vbox.getChildren().addAll(info, clickOutput, pressOutput, releaseOutput);
+        root.getChildren().add(vbox);
+
+        final Scene scene = new Scene(root, 400, 400);
+
+        final int[] clickEventCount = {0};
+        root.setOnMouseClicked(clickEvent -> {
+            clickOutput.setText("Mouse CLICK #" + clickEventCount[0]++ + ": button: " + clickEvent.getButton());
+        });
+        final int[] pressEventCount = {0};
+        root.setOnMousePressed(pressEvent -> {
+            pressOutput.setText("Mouse PRESS #" + pressEventCount[0]++ + ": button: " + pressEvent.getButton());
+        });
+        final int[] releaseEventCount = {0};
+        root.setOnMouseReleased(releaseEvent -> {
+            releaseOutput.setText("Mouse RELEASE #" + releaseEventCount[0]++ + ": button: " + releaseEvent.getButton());
+        });
+
+        canvas.setScene(scene);
+
+        while (!shell.isDisposed()) {
+            // run SWT event loop
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+        display.dispose();
+    }
+}
--- a/tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/tests/system/src/test/java/test/robot/com/sun/glass/ui/monocle/RobotTest.java	Thu Dec 27 07:51:20 2018 -0800
@@ -79,6 +79,26 @@
             assertEquals(400, (int) robot.getMouseY());
         });
         TestLogShim.waitForLog("Clicked at 300, 400");
+
+        Platform.runLater(() -> {
+            Robot robot = new Robot();
+            robot.mouseMove(new Point2D(300, 400));
+            robot.mouseClick(MouseButton.BACK);
+            assertEquals(new Point2D(300, 400), robot.getMousePosition());
+            assertEquals(300, (int) robot.getMouseX());
+            assertEquals(400, (int) robot.getMouseY());
+        });
+        TestLogShim.waitForLog("Clicked at 300, 400");
+
+        Platform.runLater(() -> {
+            Robot robot = new Robot();
+            robot.mouseMove(new Point2D(300, 400));
+            robot.mouseClick(MouseButton.FORWARD);
+            assertEquals(new Point2D(300, 400), robot.getMousePosition());
+            assertEquals(300, (int) robot.getMouseX());
+            assertEquals(400, (int) robot.getMouseY());
+        });
+        TestLogShim.waitForLog("Clicked at 300, 400");
     }
 
     @Test
--- a/tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java	Mon Dec 24 13:23:52 2018 +0530
+++ b/tests/system/src/test/java/test/robot/javafx/scene/RobotTest.java	Thu Dec 27 07:51:20 2018 -0800
@@ -24,6 +24,8 @@
  */
 package test.robot.javafx.scene;
 
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -37,6 +39,7 @@
 import javafx.geometry.Point2D;
 import javafx.scene.Scene;
 import javafx.scene.control.Button;
+import javafx.scene.control.Label;
 import javafx.scene.control.TextField;
 import javafx.scene.image.WritableImage;
 import javafx.scene.input.KeyCode;
@@ -54,10 +57,12 @@
 import javafx.stage.StageStyle;
 import javafx.stage.WindowEvent;
 
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import junit.framework.AssertionFailedError;
 import test.util.Util;
@@ -73,6 +78,8 @@
     static volatile Stage stage;
     static volatile Scene scene;
     static Robot robot;
+    static Set<MouseButton> pressedButtons = new HashSet<>();
+
     // A tolerance is needed because on macOS the pixel colors are affected by the configured "color profile"
     // of the display.
     private static final double TOLERANCE = 0.07;
@@ -95,15 +102,24 @@
         test.testMousePressPrimary();
         test.testMousePressSecondary();
         test.testMousePressMiddle();
+        test.testMousePressBack();
+        test.testMousePressForward();
         test.testMouseClickPrimary();
         test.testMouseClickSecondary();
         test.testMouseClickMiddle();
+        test.testMouseClickForward();
+        test.testMouseClickBack();
         test.testMousePressThrowsISEOnWrongThread();
         test.testMousePressThrowsNPEForNullArgument();
         test.testMouseReleaseThrowsISEOnWrongThread();
         test.testMouseReleaseThrowsNPEForNullArgument();
         test.testMouseClickThrowsISEOnWrongThread();
         test.testMouseClickThrowsNPEForNullArgument();
+        test.testMouseDragPrimary();
+        test.testMouseDragSecondary();
+        test.testMouseDragMiddle();
+        test.testMouseDragForward();
+        test.testMouseDragBack();
         test.testMouseWheelPositiveAmount();
         test.testMouseWheelNegativeAmount();
         test.testMouseWheelThrowsISEOnWrongThread();
@@ -176,9 +192,6 @@
                 capFirst(keyAction.name()) + "().");
         Assert.assertEquals("letter 'a' should be " + keyAction.name().toLowerCase() +
                 " by Robot", "a", textField.getText());
-        if (keyAction == KeyAction.PRESSED) {
-            Util.runAndWait(() -> robot.keyRelease(KeyCode.A));
-        }
     }
 
     @Test
@@ -298,6 +311,16 @@
     }
 
     @Test
+    public void testMousePressBack() {
+        testMouseAction(MouseAction.PRESSED, MouseButton.BACK);
+    }
+
+    @Test
+    public void testMousePressForward() {
+        testMouseAction(MouseAction.PRESSED, MouseButton.FORWARD);
+    }
+
+    @Test
     public void testMouseClickPrimary() {
         testMouseAction(MouseAction.CLICKED, MouseButton.PRIMARY);
     }
@@ -312,6 +335,16 @@
         testMouseAction(MouseAction.CLICKED, MouseButton.MIDDLE);
     }
 
+    @Test
+    public void testMouseClickForward() {
+        testMouseAction(MouseAction.CLICKED, MouseButton.FORWARD);
+    }
+
+    @Test
+    public void testMouseClickBack() {
+        testMouseAction(MouseAction.CLICKED, MouseButton.BACK);
+    }
+
     private enum MouseAction {
         PRESSED,
         CLICKED
@@ -377,6 +410,7 @@
             switch (mouseAction) {
                 case PRESSED:
                     robot.mousePress(mouseButton);
+                    pressedButtons.add(mouseButton);
                     break;
                 case CLICKED:
                     robot.mousePress(mouseButton);
@@ -388,9 +422,6 @@
                 capFirst(mouseAction.name()) + "().");
         Assert.assertEquals(mouseButton + " mouse button should be " + mouseAction.name().toLowerCase() + " by Robot",
                 expectedText, button.getText());
-        if (mouseAction == MouseAction.PRESSED) {
-            Util.runAndWait(() -> robot.mouseRelease(MouseButton.PRIMARY, MouseButton.SECONDARY, MouseButton.MIDDLE));
-        }
     }
 
     @Test
@@ -460,6 +491,73 @@
     }
 
     @Test
+    @Ignore("Flaky - see JDK-8215376")
+    public void testMouseDragPrimary() {
+        testMouseDrag(MouseButton.PRIMARY);
+    }
+
+    @Test
+    @Ignore("Flaky - see JDK-8215376")
+    public void testMouseDragSecondary() {
+        testMouseDrag(MouseButton.SECONDARY);
+    }
+
+    @Test
+    @Ignore("Flaky - see JDK-8215376")
+    public void testMouseDragMiddle() {
+        Assume.assumeTrue(!PlatformUtil.isMac() ); // See JDK-8215376
+        testMouseDrag(MouseButton.MIDDLE);
+    }
+
+    @Test
+    @Ignore("Flaky - see JDK-8215376")
+    public void testMouseDragForward() {
+        Assume.assumeTrue(!PlatformUtil.isMac()); // See JDK-8215376
+        testMouseDrag(MouseButton.FORWARD);
+    }
+
+    @Test
+    @Ignore("Flaky - see JDK-8215376")
+    public void testMouseDragBack() {
+        Assume.assumeTrue(!PlatformUtil.isMac()); // See JDK-8215376
+        testMouseDrag(MouseButton.BACK);
+    }
+
+    public void testMouseDrag(MouseButton mouseButton) {
+        CountDownLatch mouseDragLatch = new CountDownLatch(1);
+        CountDownLatch setSceneLatch = new CountDownLatch(1);
+        Label label = new Label("Source");
+        InvalidationListener invalidationListener = observable -> setSceneLatch.countDown();
+        Util.runAndWait(() -> {
+            label.setOnMouseDragged(event -> {
+                if (event.getButton() == mouseButton) {
+                    mouseDragLatch.countDown();
+                }
+            });
+            scene = new Scene(new HBox(label));
+            stage.sceneProperty().addListener(observable -> {
+                setSceneLatch.countDown();
+                stage.sceneProperty().removeListener(invalidationListener);
+            });
+            stage.setScene(scene);
+        });
+        waitForLatch(setSceneLatch, 5, "Timeout while waiting for scene to be set on stage.");
+        Util.runAndWait(() -> {
+            int mouseX = (int) (scene.getWindow().getX() + scene.getX() +
+                    label.getLayoutX() + label.getLayoutBounds().getWidth() / 2);
+            int mouseY = (int) (scene.getWindow().getY() + scene.getY() +
+                    label.getLayoutY() + label.getLayoutBounds().getHeight() / 2);
+            robot.mouseMove(mouseX, mouseY);
+            robot.mousePress(mouseButton);
+            for (int i = 1; i <= 50; i++) {
+                robot.mouseMove(mouseX + i, mouseY);
+            }
+            robot.mouseRelease(mouseButton);
+        });
+        waitForLatch(mouseDragLatch, 5, "Timeout while waiting for button.onMouseDragged().");
+    }
+
+    @Test
     public void testMouseWheelPositiveAmount() {
         testMouseWheel(5);
     }
@@ -714,6 +812,17 @@
         Platform.exit();
     }
 
+    @After
+    public void cleanup() {
+        Util.runAndWait(() -> {
+            if (!pressedButtons.isEmpty()) {
+                robot.mouseRelease(pressedButtons.toArray(new MouseButton[]{}));
+                pressedButtons.clear();
+            }
+            robot.keyRelease(KeyCode.A);
+        });
+    }
+
     private static void waitForLatch(CountDownLatch latch, int seconds, String msg) {
         try {
             if (!latch.await(seconds, TimeUnit.SECONDS)) {