changeset 10744:a43d6cc29dea

8183520: [linux] NPE when switching Scene Reviewed-by: kcr, aghaisas
author arapte
date Tue, 12 Dec 2017 23:15:08 +0530
parents 674513271a88
children 90751bb9e2c4
files modules/javafx.controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp tests/system/src/test/java/test/robot/javafx/scene/SceneChangeEventsTest.java
diffstat 4 files changed, 163 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java	Tue Dec 12 12:08:34 2017 +0530
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ContextMenuSkin.java	Tue Dec 12 23:15:08 2017 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, 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
@@ -219,6 +219,7 @@
         if (ownerNode == null) return;
 
         final Bounds ownerBounds = ownerNode.localToScreen(ownerNode.getLayoutBounds());
+        if (ownerBounds == null) return;
 
         // shifting vertically
         final double rootPrefHeight = root.prefHeight(-1);
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java	Tue Dec 12 12:08:34 2017 +0530
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/MenuBarSkin.java	Tue Dec 12 23:15:08 2017 +0530
@@ -51,6 +51,7 @@
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.event.WeakEventHandler;
+import javafx.geometry.Bounds;
 import javafx.geometry.NodeOrientation;
 import javafx.geometry.Pos;
 import javafx.scene.AccessibleAttribute;
@@ -318,7 +319,8 @@
 
         // When we click else where in the scene - menu selection should be cleared.
         mouseEventHandler = t -> {
-            if (!container.localToScreen(container.getLayoutBounds()).contains(t.getScreenX(), t.getScreenY())) {
+            Bounds containerScreenBounds = container.localToScreen(container.getLayoutBounds());
+            if (containerScreenBounds == null || !containerScreenBounds.contains(t.getScreenX(), t.getScreenY())) {
                 unSelectMenus();
             }
         };
--- a/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp	Tue Dec 12 12:08:34 2017 +0530
+++ b/modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp	Tue Dec 12 23:15:08 2017 +0530
@@ -568,6 +568,14 @@
 bool WindowContextBase::set_view(jobject view) {
 
     if (jview) {
+        mainEnv->CallVoidMethod(jview, jViewNotifyMouse,
+                com_sun_glass_events_MouseEvent_EXIT,
+                com_sun_glass_events_MouseEvent_BUTTON_NONE,
+                0, 0,
+                0, 0,
+                0,
+                JNI_FALSE,
+                JNI_FALSE);
         mainEnv->DeleteGlobalRef(jview);
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/robot/javafx/scene/SceneChangeEventsTest.java	Tue Dec 12 23:15:08 2017 +0530
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package test.robot.javafx.scene;
+
+import com.sun.glass.ui.Robot;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.layout.HBox;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.stage.WindowEvent;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+/*
+ * Test to verify the events when scene of stage is changed.
+ * Steps of Test:
+ * 1. Create a stage with a scene with a button.
+ * 2. Add MOUSE_EXITED listener & WindowProperty Listener to the same scene.
+ * 3. In onAction of the button change stage's scene to a new scene.
+ * 4. Verify that MOUSE_EXITED & WindowPropery change listener are called in
+ *    sequence.
+ */
+
+public class SceneChangeEventsTest {
+    static CountDownLatch startupLatch;
+    static Robot robot;
+    static volatile Stage stage;
+    boolean mouseExited = false;
+    boolean mouseWindowEventOrder = false;
+    boolean windowChanged = false;
+
+    public static void main(String[] args) {
+        SceneChangeEventsTest test = new SceneChangeEventsTest();
+        test.testSceneChange();
+        exit();
+    }
+
+    @Test
+    public void testSceneChange() {
+
+        Button button = new Button("onAction");
+        CountDownLatch onActionLatch = new CountDownLatch(1);
+        button.setOnAction(event -> {
+            stage.setScene(new Scene(new HBox()));
+            onActionLatch.countDown();
+        });
+        HBox root = new HBox();
+        root.getChildren().add(button);
+        Scene scene = new Scene(root);
+        CountDownLatch setSceneLatch = new CountDownLatch(1);
+        stage.sceneProperty().addListener(observable -> setSceneLatch.countDown());
+        Platform.runLater(() -> {
+            stage.setScene(scene);
+        });
+        waitForLatch(setSceneLatch, 5, "Timeout while waiting for scene to be set on stage.");
+
+        scene.setOnMouseExited(event -> {
+            mouseExited = true;
+        });
+        scene.windowProperty().addListener(observable -> {
+            mouseWindowEventOrder = mouseExited;
+            windowChanged = true;
+        });
+        Platform.runLater(() -> {
+            robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + button.getLayoutX() + button.getLayoutBounds().getWidth() / 2),
+                    (int)(scene.getWindow().getY() + scene.getY() +  button.getLayoutY() + button.getLayoutBounds().getHeight() / 2));
+            robot.mousePress(Robot.MOUSE_LEFT_BTN);
+            robot.mouseRelease(Robot.MOUSE_LEFT_BTN);
+        });
+        waitForLatch(onActionLatch, 5, "Timeout while waiting for button.onAction().");
+
+        Assert.assertTrue("MOUSE_EXITED should be received when scene is " +
+            " changed.", mouseExited);
+        Assert.assertTrue("scene.windowProperty() listener should be received" +
+            "on scene change.", windowChanged);
+        Assert.assertTrue("MOUSE_EXITED should have been received before " +
+            "scene.windowProperty().", mouseWindowEventOrder);
+    }
+
+    public static class TestApp extends Application {
+        @Override
+        public void start(Stage primaryStage) {
+            robot = com.sun.glass.ui.Application.GetApplication().createRobot();
+            stage = primaryStage;
+            stage.initStyle(StageStyle.UNDECORATED);
+            stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e ->
+                    Platform.runLater(startupLatch::countDown));
+            stage.setAlwaysOnTop(true);
+            stage.show();
+        }
+    }
+
+    @BeforeClass
+    public static void initFX() {
+        startupLatch = new CountDownLatch(1);
+        new Thread(() -> Application.launch(TestApp.class, (String[])null)).start();
+        waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start");
+    }
+
+    @AfterClass
+    public static void exit() {
+        Platform.runLater(() -> {
+            stage.hide();
+        });
+        Platform.exit();
+    }
+
+    public static void waitForLatch(CountDownLatch latch, int seconds, String msg) {
+        try {
+            if (!latch.await(seconds, TimeUnit.SECONDS)) {
+                fail(msg);
+            }
+        } catch (Exception ex) {
+            fail("Unexpected exception: " + ex);
+        }
+    }
+}