-
Bug
-
Resolution: Fixed
-
P4
-
jfx14
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8289483 | jfx17.0.4 | Johan Vos | P4 | Resolved | Fixed |
Bubbled up on SO https://stackoverflow.com/a/61539677/203657 - copied the example here for convenience.
To reproduce, run open popup and click on menuItem
- expected: new root showing without errors
- actual: new root showing, NPE in MenuButtonSkin
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:222)
at javafx.controls/javafx.scene.control.skin.MenuButtonSkinBase.lambda$new$7(MenuButtonSkinBase.java:198)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:832)
The example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
MenuButton btn = new MenuButton("Switch...");
MenuItem item = new MenuItem("To 'Hello, World!'");
item.setOnAction(
e -> {
StackPane root = new StackPane(new Label("Hello, World!"));
primaryStage.getScene().setRoot(root);
});
btn.getItems().add(item);
primaryStage.setScene(new Scene(new StackPane(btn), 500, 300));
primaryStage.show();
}
}
Converted into a unit test:
@Test
public void testMenuButtonSkinNPEOnReplaceSceneOpenPopup() {
MenuButton menuButton = new MenuButton("switch");
MenuItem menuItem = new MenuItem("Press '_a'");
menuButton.getItems().add(menuItem);
Scene scene = new Scene(menuButton);
Stage stage = new Stage();
menuItem.setOnAction(e -> {
StackPane root = new StackPane(new Label("Hello, World!"));
scene.setRoot(root);
});
stage.setScene(scene);
stage.show();
menuButton.requestFocus();
menuButton.show();
assertTrue("sanity:", menuButton.isShowing());
assertSame(scene.getFocusOwner(), menuButton);
// trying to see the NPE
// working options (the exact sequence of events is slightly different)
// keyboard on menuItem and press accelerator
KeyEventFirer keyboard = new KeyEventFirer(menuItem, scene);
keyboard.doKeyPress(KeyCode.A, KeyModifier.ALT);
}
The error happens in the showingListener installed in skin:
if (popup.isShowing()) {
boolean showMnemonics = NodeHelper.isShowMnemonics(getSkinnable());
Utils.addMnemonics(popup, getSkinnable().getScene(), showMnemonics, mnemonics);
} else {
// we wrap this in a runLater so that mnemonics are not removed
// before all key events are fired (because KEY_PRESSED might have
// been used to hide the menu, but KEY_TYPED and KEY_RELEASED
// events are still to be fired, and shouldn't miss out on going
// through the mnemonics code (especially in case they should be
// consumed to prevent them being used elsewhere).
// SeeJDK-8090026 for more detail.
>>>> the scene here might be null
Scene scene = getSkinnable().getScene();
List<Mnemonic> mnemonicsToRemove = new ArrayList<>(mnemonics);
mnemonics.clear();
>>>> that's why we get the npe here
Platform.runLater(() -> mnemonicsToRemove.forEach(scene::removeMnemonic));
Note that this is a different root than the other NPEs (JDK-8244081, JDK-8244110) around MenuButtonSkin, it's uneffected by cleaning up in the sceneListener and/or in dispose. There seem to be several paths that remove the accelerators from the scene, probably need to find them all and guard against null (or funnel into a single cleanup, don't know if that's possible)
To reproduce, run open popup and click on menuItem
- expected: new root showing without errors
- actual: new root showing, NPE in MenuButtonSkin
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:222)
at javafx.controls/javafx.scene.control.skin.MenuButtonSkinBase.lambda$new$7(MenuButtonSkinBase.java:198)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:832)
The example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
MenuButton btn = new MenuButton("Switch...");
MenuItem item = new MenuItem("To 'Hello, World!'");
item.setOnAction(
e -> {
StackPane root = new StackPane(new Label("Hello, World!"));
primaryStage.getScene().setRoot(root);
});
btn.getItems().add(item);
primaryStage.setScene(new Scene(new StackPane(btn), 500, 300));
primaryStage.show();
}
}
Converted into a unit test:
@Test
public void testMenuButtonSkinNPEOnReplaceSceneOpenPopup() {
MenuButton menuButton = new MenuButton("switch");
MenuItem menuItem = new MenuItem("Press '_a'");
menuButton.getItems().add(menuItem);
Scene scene = new Scene(menuButton);
Stage stage = new Stage();
menuItem.setOnAction(e -> {
StackPane root = new StackPane(new Label("Hello, World!"));
scene.setRoot(root);
});
stage.setScene(scene);
stage.show();
menuButton.requestFocus();
menuButton.show();
assertTrue("sanity:", menuButton.isShowing());
assertSame(scene.getFocusOwner(), menuButton);
// trying to see the NPE
// working options (the exact sequence of events is slightly different)
// keyboard on menuItem and press accelerator
KeyEventFirer keyboard = new KeyEventFirer(menuItem, scene);
keyboard.doKeyPress(KeyCode.A, KeyModifier.ALT);
}
The error happens in the showingListener installed in skin:
if (popup.isShowing()) {
boolean showMnemonics = NodeHelper.isShowMnemonics(getSkinnable());
Utils.addMnemonics(popup, getSkinnable().getScene(), showMnemonics, mnemonics);
} else {
// we wrap this in a runLater so that mnemonics are not removed
// before all key events are fired (because KEY_PRESSED might have
// been used to hide the menu, but KEY_TYPED and KEY_RELEASED
// events are still to be fired, and shouldn't miss out on going
// through the mnemonics code (especially in case they should be
// consumed to prevent them being used elsewhere).
// See
>>>> the scene here might be null
Scene scene = getSkinnable().getScene();
List<Mnemonic> mnemonicsToRemove = new ArrayList<>(mnemonics);
mnemonics.clear();
>>>> that's why we get the npe here
Platform.runLater(() -> mnemonicsToRemove.forEach(scene::removeMnemonic));
Note that this is a different root than the other NPEs (
- backported by
-
JDK-8289483 MenuButton: NPE on removing from scene with open popup
- Resolved
- duplicates
-
JDK-8252630 NPE in MenuButtonSkinBase | OpenJDK 14.0.2
- Closed
- links to
-
Commit openjdk/jfx17u/f84dac29
-
Commit openjdk/jfx/6e215263
-
Review openjdk/jfx17u/69
-
Review openjdk/jfx/713
(1 links to)