From 01403e0371a4be0e5583e327edabfe43280dcb2a Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 10 Nov 2025 10:23:34 +0000 Subject: [PATCH 01/10] 8255248: NullPointerException in JFXPanel due to race condition in HostContainer --- .../sun/javafx/tk/quantum/EmbeddedScene.java | 4 +- .../java/javafx/embed/swing/JFXPanel.java | 104 +++++++++++---- .../javafx/embed/swing/JFXPanelNPETest.java | 126 ++++++++++++++++++ 3 files changed, 208 insertions(+), 26 deletions(-) create mode 100644 tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java index a84d6b736e2..8455b059f4b 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java @@ -155,7 +155,9 @@ public void setPixelScaleFactors(float scalex, float scaley) { entireSceneNeedsRepaint(); Platform.runLater(() -> { QuantumToolkit.runWithRenderLock(() -> { - updateSceneState(); + if (getSceneState() != null) { + updateSceneState(); + } return null; }); }); diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 8b6e2d70c85..4a62949bd85 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -205,6 +205,21 @@ public class JFXPanel extends JComponent { @SuppressWarnings("doclint:missing") private JFXPanelInteropN jfxPanelIOP; + private static final boolean debugPrint; + private static final String debugPrefix = "JFXPanel:>> "; + private static final String JFXPANEL_DEBUG = "jfxpanel.debug"; + static { + String debugStr = System.getProperty(JFXPANEL_DEBUG); + + debugPrint = "true".equalsIgnoreCase(debugStr); + } + + protected static void debug_println(String str) { + if (debugPrint) { + System.out.println(debugPrefix + " " + str); + } + } + private synchronized void registerFinishListener() { if (instanceCount.getAndIncrement() > 0) { // Already registered @@ -274,6 +289,7 @@ private synchronized static void initFx() { public JFXPanel() { super(); + debug_println("JFXPanel Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); jfxPanelIOP = new JFXPanelInteropN(); initFx(); @@ -299,6 +315,7 @@ public JFXPanel() { * @return the {@code Scene} attached to this {@code JFXPanel} */ public Scene getScene() { + debug_println("getScene Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); return scene; } @@ -313,6 +330,7 @@ public Scene getScene() { * @see javafx.application.Platform#isFxApplicationThread() */ public void setScene(final Scene newScene) { + debug_println("setScene Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if (Toolkit.getToolkit().isFxUserThread()) { setSceneImpl(newScene); } else { @@ -360,6 +378,7 @@ private void setSceneImpl(Scene newScene) { */ @Override public final void setOpaque(boolean opaque) { + debug_println("setOpaque Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); // Don't let user control opacity if (!opaque) { super.setOpaque(opaque); @@ -376,6 +395,7 @@ public final void setOpaque(boolean opaque) { */ @Override public final boolean isOpaque() { + debug_println("isOpaque Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); return false; } @@ -418,7 +438,8 @@ private Point2D convertSwingToFxPixel(GraphicsConfiguration g, double wx, double } private void sendMouseEventToFX(MouseEvent e) { - if (scenePeer == null || !isFxEnabled()) { + var hScenePeer = scenePeer; + if (hScenePeer == null || !isFxEnabled()) { return; } @@ -467,7 +488,7 @@ private void sendMouseEventToFX(MouseEvent e) { int fxYOnScreen = (int)Math.round(onScreen.getY()); if(e.getID() == MouseEvent.MOUSE_WHEEL) { - scenePeer.scrollEvent(AbstractEvents.MOUSEEVENT_VERTICAL_WHEEL, + hScenePeer.scrollEvent(AbstractEvents.MOUSEEVENT_VERTICAL_WHEEL, 0, -SwingEvents.getWheelRotation(e), 0, 0, // total scroll 40, 40, // multiplier @@ -478,7 +499,7 @@ private void sendMouseEventToFX(MouseEvent e) { (extModifiers & MouseEvent.ALT_DOWN_MASK) != 0, (extModifiers & MouseEvent.META_DOWN_MASK) != 0, false); } else { - scenePeer.mouseEvent( + hScenePeer.mouseEvent( SwingEvents.mouseIDToEmbedMouseType(e.getID()), SwingEvents.mouseButtonToEmbedMouseButton(e.getButton(), extModifiers), primaryBtnDown, middleBtnDown, secondaryBtnDown, @@ -492,7 +513,7 @@ private void sendMouseEventToFX(MouseEvent e) { popupTrigger); } if (e.isPopupTrigger()) { - scenePeer.menuEvent(e.getX(), e.getY(), fxXOnScreen, fxYOnScreen, false); + hScenePeer.menuEvent(e.getX(), e.getY(), fxXOnScreen, fxYOnScreen, false); } } @@ -505,6 +526,7 @@ private void sendMouseEventToFX(MouseEvent e) { */ @Override protected void processMouseEvent(MouseEvent e) { + debug_println("processMouseEvent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if ((e.getID() == MouseEvent.MOUSE_PRESSED) && (e.getButton() == MouseEvent.BUTTON1)) { if (isFocusable() && !hasFocus()) { @@ -532,6 +554,7 @@ protected void processMouseEvent(MouseEvent e) { */ @Override protected void processMouseMotionEvent(MouseEvent e) { + debug_println("processMouseMotionEvent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); sendMouseEventToFX(e); super.processMouseMotionEvent(e); } @@ -546,12 +569,14 @@ protected void processMouseMotionEvent(MouseEvent e) { */ @Override protected void processMouseWheelEvent(MouseWheelEvent e) { + debug_println("processMouseWheelEvent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); sendMouseEventToFX(e); super.processMouseWheelEvent(e); } private void sendKeyEventToFX(final KeyEvent e) { - if (scenePeer == null || !isFxEnabled()) { + var hScenePeer = scenePeer; + if (hScenePeer == null || !isFxEnabled()) { return; } @@ -559,7 +584,7 @@ private void sendKeyEventToFX(final KeyEvent e) { ? new char[] {} : new char[] { SwingEvents.keyCharToEmbedKeyChar(e.getKeyChar()) }; - scenePeer.keyEvent( + hScenePeer.keyEvent( SwingEvents.keyIDToEmbedKeyType(e.getID()), e.getKeyCode(), chars, SwingEvents.keyModifiersToEmbedKeyModifiers(e.getModifiersEx())); @@ -574,11 +599,13 @@ private void sendKeyEventToFX(final KeyEvent e) { */ @Override protected void processKeyEvent(KeyEvent e) { + debug_println("processKeyEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); sendKeyEventToFX(e); super.processKeyEvent(e); } private void sendResizeEventToFX() { + debug_println("sendResizeEventToFX Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if (stagePeer != null) { stagePeer.setSize(pWidth, pHeight); } @@ -598,6 +625,7 @@ private void sendResizeEventToFX() { */ @Override protected void processComponentEvent(ComponentEvent e) { + debug_println("processComponentEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); switch (e.getID()) { case ComponentEvent.COMPONENT_RESIZED: { updateComponentSize(); @@ -627,6 +655,7 @@ private AffineTransform getCurrentTransform() { // called on EDT only private void updateComponentSize() { + debug_println("updateComponentSize Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); int oldWidth = pWidth; int oldHeight = pHeight; if (getBorder() != null) { @@ -649,8 +678,9 @@ private void updateComponentSize() { newScaleFactorX != scaleFactorX || newScaleFactorY != scaleFactorY) { createResizePixelBuffer(newScaleFactorX, newScaleFactorY); - if (scenePeer != null) { - scenePeer.setPixelScaleFactors((float) newScaleFactorX, + var hScenePeer = scenePeer; + if (hScenePeer != null) { + hScenePeer.setPixelScaleFactors((float) newScaleFactorX, (float) newScaleFactorY); } scaleFactorX = newScaleFactorX; @@ -692,6 +722,7 @@ private void sendMoveEventToFX() { */ @Override protected void processHierarchyBoundsEvent(HierarchyEvent e) { + debug_println("processHierarchyBoundsEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if (e.getID() == HierarchyEvent.ANCESTOR_MOVED) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -702,6 +733,7 @@ protected void processHierarchyBoundsEvent(HierarchyEvent e) { @Override protected void processHierarchyEvent(HierarchyEvent e) { + debug_println("processHierarchyEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -739,13 +771,15 @@ private void sendFocusEventToFX(final FocusEvent e) { */ @Override protected void processFocusEvent(FocusEvent e) { + debug_println("processFocusEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); sendFocusEventToFX(e); super.processFocusEvent(e); } // called on EDT only private void createResizePixelBuffer(double newScaleFactorX, double newScaleFactorY) { - if (scenePeer == null || pWidth <= 0 || pHeight <= 0) { + var hScenePeer = scenePeer; + if (hScenePeer == null || pWidth <= 0 || pHeight <= 0) { pixelsIm = null; } else { BufferedImage oldIm = pixelsIm; @@ -753,7 +787,7 @@ private void createResizePixelBuffer(double newScaleFactorX, double newScaleFact int newPixelH = (int) Math.ceil(pHeight * newScaleFactorY); pixelsIm = new BufferedImage(newPixelW, newPixelH, SwingFXUtils.getBestBufferedImageType( - scenePeer.getPixelFormat(), null, false)); + hScenePeer.getPixelFormat(), null, false)); if (oldIm != null) { double ratioX = newScaleFactorX / scaleFactorX; double ratioY = newScaleFactorY / scaleFactorY; @@ -773,6 +807,7 @@ private void createResizePixelBuffer(double newScaleFactorX, double newScaleFact @Override protected void processInputMethodEvent(InputMethodEvent e) { + debug_println("processInputMethodEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if (e.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) { sendInputMethodEventToFX(e); } @@ -803,8 +838,11 @@ private void sendInputMethodEventToFX(InputMethodEvent e) { * @see #isOpaque() */ @Override - protected void paintComponent(Graphics g) { - if (scenePeer == null) { + protected synchronized void paintComponent(Graphics g) { + debug_println("paintComponent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + var hScenePeer = scenePeer; + var hStage = stage; + if (hScenePeer == null || hStage == null) { return; } if (pixelsIm == null) { @@ -816,7 +854,7 @@ protected void paintComponent(Graphics g) { DataBufferInt dataBuf = (DataBufferInt)pixelsIm.getRaster().getDataBuffer(); int[] pixelsData = dataBuf.getData(); IntBuffer buf = IntBuffer.wrap(pixelsData); - if (!scenePeer.getPixels(buf, pWidth, pHeight)) { + if (!hScenePeer.getPixels(buf, pWidth, pHeight)) { // In this case we just render what we have so far in the buffer. } @@ -824,7 +862,7 @@ protected void paintComponent(Graphics g) { try { ComponentOrientation cor = this.getComponentOrientation(); boolean rtl = ComponentOrientation.RIGHT_TO_LEFT.equals(cor); - stage.setNodeOrientation(rtl ? NodeOrientation.RIGHT_TO_LEFT : + hStage.setNodeOrientation(rtl ? NodeOrientation.RIGHT_TO_LEFT : NodeOrientation.LEFT_TO_RIGHT); gg = g.create(); @@ -845,7 +883,7 @@ protected void paintComponent(Graphics g) { if (scaleFactorX != newScaleFactorX || scaleFactorY != newScaleFactorY) { createResizePixelBuffer(newScaleFactorX, newScaleFactorY); // The scene will request repaint. - scenePeer.setPixelScaleFactors((float) newScaleFactorX, + hScenePeer.setPixelScaleFactors((float) newScaleFactorX, (float) newScaleFactorY); scaleFactorX = newScaleFactorX; scaleFactorY = newScaleFactorY; @@ -869,6 +907,7 @@ protected void paintComponent(Graphics g) { */ @Override public Dimension getPreferredSize() { + debug_println("getPreferredSize Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if (isPreferredSizeSet() || scenePeer == null) { return super.getPreferredSize(); } @@ -943,6 +982,7 @@ private void setFxEnabled(boolean enabled) { */ @Override public void addNotify() { + debug_println("addNotify Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); super.addNotify(); registerFinishListener(); @@ -960,6 +1000,7 @@ public void addNotify() { @Override public InputMethodRequests getInputMethodRequests() { + debug_println("getInputMethodRequests Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); EmbeddedSceneInterface scene = scenePeer; if (scene == null) { return new InputMethodSupport.InputMethodRequestsAdapter(null); @@ -973,6 +1014,7 @@ public InputMethodRequests getInputMethodRequests() { * chain of parent components are removed. */ @Override public void removeNotify() { + debug_println("removeNotify Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && stage.isShowing()) { stage.hide(); @@ -1006,16 +1048,18 @@ private class HostContainer implements HostInterface { @Override public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { + debug_println("HostContainer.setEmbeddedStage Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); stagePeer = embeddedStage; - if (stagePeer == null) { + var hStagePeer = stagePeer; + if (hStagePeer == null) { return; } if (pWidth > 0 && pHeight > 0) { - stagePeer.setSize(pWidth, pHeight); + hStagePeer.setSize(pWidth, pHeight); } invokeOnClientEDT(() -> { - if (stagePeer != null && JFXPanel.this.isFocusOwner()) { - stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED); + if (hStagePeer != null && JFXPanel.this.isFocusOwner()) { + hStagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED); } }); sendMoveEventToFX(); @@ -1023,11 +1067,13 @@ public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { @Override public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { + debug_println("HostContainer.setEmbeddedScene Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); if (scenePeer == embeddedScene) { return; } scenePeer = embeddedScene; - if (scenePeer == null) { + var hScenePeer = scenePeer; + if (hScenePeer == null) { invokeOnClientEDT(() -> { if (dnd != null) { dnd.removeNotify(); @@ -1037,26 +1083,28 @@ public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { return; } if (pWidth > 0 && pHeight > 0) { - scenePeer.setSize(pWidth, pHeight); + hScenePeer.setSize(pWidth, pHeight); } - scenePeer.setPixelScaleFactors((float) scaleFactorX, (float) scaleFactorY); + hScenePeer.setPixelScaleFactors((float) scaleFactorX, (float) scaleFactorY); invokeOnClientEDT(() -> { - dnd = new SwingDnD(JFXPanel.this, scenePeer); + dnd = new SwingDnD(JFXPanel.this, hScenePeer); dnd.addNotify(); - if (scenePeer != null) { - scenePeer.setDragStartListener(dnd.getDragStartListener()); + if (hScenePeer != null) { + hScenePeer.setDragStartListener(dnd.getDragStartListener()); } }); } @Override public boolean requestFocus() { + debug_println("HostContainer.requestFocus Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); return requestFocusInWindow(); } @Override public boolean traverseFocusOut(boolean forward) { + debug_println("HostContainer.traverseFocusOut Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); if (forward) { kfm.focusNextComponent(JFXPanel.this); @@ -1068,6 +1116,7 @@ public boolean traverseFocusOut(boolean forward) { @Override public void setPreferredSize(final int width, final int height) { + debug_println("HostContainer.setPreferredSize Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); invokeOnClientEDT(() -> { JFXPanel.this.pPreferredWidth = width; JFXPanel.this.pPreferredHeight = height; @@ -1077,6 +1126,7 @@ public void setPreferredSize(final int width, final int height) { @Override public void repaint() { + debug_println("HostContainer.repaint Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); invokeOnClientEDT(() -> { JFXPanel.this.repaint(); }); @@ -1084,11 +1134,13 @@ public void repaint() { @Override public void setEnabled(final boolean enabled) { + debug_println("HostContainer.setEnabled Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); JFXPanel.this.setFxEnabled(enabled); } @Override public void setCursor(CursorFrame cursorFrame) { + debug_println("HostContainer.setCursor Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); final Cursor cursor = getPlatformCursor(cursorFrame); invokeOnClientEDT(() -> { JFXPanel.this.setCursor(cursor); @@ -1113,6 +1165,7 @@ private Cursor getPlatformCursor(final CursorFrame cursorFrame) { @Override public boolean grabFocus() { + debug_println("HostContainer.grabFocus Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return true; @@ -1129,6 +1182,7 @@ public boolean grabFocus() { @Override public void ungrabFocus() { + debug_println("HostContainer.ungrabFocus Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return; diff --git a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java new file mode 100644 index 00000000000..e451a09deee --- /dev/null +++ b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025, 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.javafx.embed.swing; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.scene.Scene; +import javafx.scene.web.WebView; +import javafx.stage.Stage; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.CountDownLatch; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import test.util.Util; + +public class JFXPanelNPETest { + private static WebView webView; + private static JFrame jFrame; + private static JFXPanel contentPane; + private static AtomicBoolean failure; + // Used to launch the application before running any test + private static final CountDownLatch launchLatch = new CountDownLatch(1); + + // Application class. An instance is created and initialized before running + // the first test, and it lives through the execution of all tests. + public static class MyApp extends Application { + @Override + public void start(Stage primaryStage) throws Exception { + Platform.setImplicitExit(false); + Assertions.assertTrue(Platform.isFxApplicationThread()); + Assertions.assertNotNull(primaryStage); + + launchLatch.countDown(); + } + } + + @BeforeAll + public static void doSetupOnce() throws Exception { + Util.launch(launchLatch, MyApp.class); + Assertions.assertEquals(0, launchLatch.getCount()); + } + + @AfterAll + public static void doTeardownOnce() { + Util.shutdown(); + } + + @AfterEach + public void doCleanup() { + if (jFrame != null) { + SwingUtilities.invokeLater(() -> jFrame.dispose()); + } + } + + @Test + public void testSceneNPE() throws Exception { + failure = new AtomicBoolean(false); + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + e.printStackTrace(); + failure.set(true); + } + }); + SwingUtilities.invokeAndWait(JFXPanelNPETest::createUI); + for (int i = 0; i < 300; i++) { + SwingUtilities.invokeLater(contentPane::repaint); + Platform.runLater(() -> contentPane.setScene(null)); + Thread.sleep(100); + Platform.runLater(() -> contentPane.setScene(webView.getScene())); + Thread.sleep(100); + } + System.out.println("failure = " + failure.get()); + Assertions.assertFalse(failure.get()); + } + + protected static void createUI() { + jFrame = new JFrame(); + contentPane = new JFXPanel(); + Platform.runLater(() -> fx(contentPane)); + jFrame.setContentPane(contentPane); + jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + jFrame.setSize(400,400); + jFrame.setVisible(true); + } + + private static void fx(final JFXPanel contentPane) { + webView = new WebView(); + final var engine = webView.getEngine(); + engine.loadContent("hello!"); + contentPane.setScene(new Scene(webView)); + } +} + From 4cae04111079703a3b031601f52badd507f78ff4 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 11 Nov 2025 02:57:20 +0000 Subject: [PATCH 02/10] debug fix --- .../src/main/java/javafx/embed/swing/JFXPanel.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 4a62949bd85..a56fdea537b 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -205,17 +205,11 @@ public class JFXPanel extends JComponent { @SuppressWarnings("doclint:missing") private JFXPanelInteropN jfxPanelIOP; - private static final boolean debugPrint; + private static final boolean DEBUG = Boolean.getBoolean("jfxpanel.debug"); private static final String debugPrefix = "JFXPanel:>> "; - private static final String JFXPANEL_DEBUG = "jfxpanel.debug"; - static { - String debugStr = System.getProperty(JFXPANEL_DEBUG); - debugPrint = "true".equalsIgnoreCase(debugStr); - } - - protected static void debug_println(String str) { - if (debugPrint) { + private static void debug_println(String str) { + if (DEBUG) { System.out.println(debugPrefix + " " + str); } } From e29a2aff482edee56d4409b2825a9f58c1aa1d37 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 13 Nov 2025 03:24:14 +0000 Subject: [PATCH 03/10] debug log fix --- .../java/javafx/embed/swing/JFXPanel.java | 154 ++++++++++++++---- 1 file changed, 121 insertions(+), 33 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index a56fdea537b..d475cdee034 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -209,9 +209,7 @@ public class JFXPanel extends JComponent { private static final String debugPrefix = "JFXPanel:>> "; private static void debug_println(String str) { - if (DEBUG) { - System.out.println(debugPrefix + " " + str); - } + System.out.println(debugPrefix + " " + str); } private synchronized void registerFinishListener() { @@ -283,7 +281,10 @@ private synchronized static void initFx() { public JFXPanel() { super(); - debug_println("JFXPanel Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("JFXPanel Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } jfxPanelIOP = new JFXPanelInteropN(); initFx(); @@ -309,7 +310,10 @@ public JFXPanel() { * @return the {@code Scene} attached to this {@code JFXPanel} */ public Scene getScene() { - debug_println("getScene Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("getScene Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } return scene; } @@ -324,7 +328,10 @@ public Scene getScene() { * @see javafx.application.Platform#isFxApplicationThread() */ public void setScene(final Scene newScene) { - debug_println("setScene Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("setScene Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if (Toolkit.getToolkit().isFxUserThread()) { setSceneImpl(newScene); } else { @@ -372,7 +379,10 @@ private void setSceneImpl(Scene newScene) { */ @Override public final void setOpaque(boolean opaque) { - debug_println("setOpaque Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("setOpaque Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } // Don't let user control opacity if (!opaque) { super.setOpaque(opaque); @@ -389,7 +399,10 @@ public final void setOpaque(boolean opaque) { */ @Override public final boolean isOpaque() { - debug_println("isOpaque Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("isOpaque Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } return false; } @@ -520,7 +533,10 @@ private void sendMouseEventToFX(MouseEvent e) { */ @Override protected void processMouseEvent(MouseEvent e) { - debug_println("processMouseEvent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processMouseEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if ((e.getID() == MouseEvent.MOUSE_PRESSED) && (e.getButton() == MouseEvent.BUTTON1)) { if (isFocusable() && !hasFocus()) { @@ -548,7 +564,10 @@ protected void processMouseEvent(MouseEvent e) { */ @Override protected void processMouseMotionEvent(MouseEvent e) { - debug_println("processMouseMotionEvent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processMouseMotionEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } sendMouseEventToFX(e); super.processMouseMotionEvent(e); } @@ -563,7 +582,10 @@ protected void processMouseMotionEvent(MouseEvent e) { */ @Override protected void processMouseWheelEvent(MouseWheelEvent e) { - debug_println("processMouseWheelEvent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processMouseWheelEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } sendMouseEventToFX(e); super.processMouseWheelEvent(e); } @@ -593,13 +615,19 @@ private void sendKeyEventToFX(final KeyEvent e) { */ @Override protected void processKeyEvent(KeyEvent e) { - debug_println("processKeyEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processKeyEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } sendKeyEventToFX(e); super.processKeyEvent(e); } private void sendResizeEventToFX() { - debug_println("sendResizeEventToFX Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("sendResizeEventToFX Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if (stagePeer != null) { stagePeer.setSize(pWidth, pHeight); } @@ -619,7 +647,10 @@ private void sendResizeEventToFX() { */ @Override protected void processComponentEvent(ComponentEvent e) { - debug_println("processComponentEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processComponentEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } switch (e.getID()) { case ComponentEvent.COMPONENT_RESIZED: { updateComponentSize(); @@ -716,7 +747,10 @@ private void sendMoveEventToFX() { */ @Override protected void processHierarchyBoundsEvent(HierarchyEvent e) { - debug_println("processHierarchyBoundsEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processHierarchyBoundsEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if (e.getID() == HierarchyEvent.ANCESTOR_MOVED) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -727,7 +761,10 @@ protected void processHierarchyBoundsEvent(HierarchyEvent e) { @Override protected void processHierarchyEvent(HierarchyEvent e) { - debug_println("processHierarchyEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processHierarchyEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -765,7 +802,10 @@ private void sendFocusEventToFX(final FocusEvent e) { */ @Override protected void processFocusEvent(FocusEvent e) { - debug_println("processFocusEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processFocusEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } sendFocusEventToFX(e); super.processFocusEvent(e); } @@ -801,7 +841,10 @@ private void createResizePixelBuffer(double newScaleFactorX, double newScaleFact @Override protected void processInputMethodEvent(InputMethodEvent e) { - debug_println("processInputMethodEVent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("processInputMethodEvent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if (e.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) { sendInputMethodEventToFX(e); } @@ -833,7 +876,10 @@ private void sendInputMethodEventToFX(InputMethodEvent e) { */ @Override protected synchronized void paintComponent(Graphics g) { - debug_println("paintComponent Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("paintComponent Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } var hScenePeer = scenePeer; var hStage = stage; if (hScenePeer == null || hStage == null) { @@ -901,7 +947,10 @@ protected synchronized void paintComponent(Graphics g) { */ @Override public Dimension getPreferredSize() { - debug_println("getPreferredSize Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("getPreferredSize Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if (isPreferredSizeSet() || scenePeer == null) { return super.getPreferredSize(); } @@ -976,7 +1025,10 @@ private void setFxEnabled(boolean enabled) { */ @Override public void addNotify() { - debug_println("addNotify Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("addNotify Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } super.addNotify(); registerFinishListener(); @@ -994,7 +1046,10 @@ public void addNotify() { @Override public InputMethodRequests getInputMethodRequests() { - debug_println("getInputMethodRequests Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("getInputMethodRequests Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } EmbeddedSceneInterface scene = scenePeer; if (scene == null) { return new InputMethodSupport.InputMethodRequestsAdapter(null); @@ -1008,7 +1063,10 @@ public InputMethodRequests getInputMethodRequests() { * chain of parent components are removed. */ @Override public void removeNotify() { - debug_println("removeNotify Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("removeNotify Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && stage.isShowing()) { stage.hide(); @@ -1042,7 +1100,10 @@ private class HostContainer implements HostInterface { @Override public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { - debug_println("HostContainer.setEmbeddedStage Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.setEmbeddedStage Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } stagePeer = embeddedStage; var hStagePeer = stagePeer; if (hStagePeer == null) { @@ -1061,7 +1122,10 @@ public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { @Override public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { - debug_println("HostContainer.setEmbeddedScene Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.setEmbeddedScene Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } if (scenePeer == embeddedScene) { return; } @@ -1092,13 +1156,19 @@ public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { @Override public boolean requestFocus() { - debug_println("HostContainer.requestFocus Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.requestFocus Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } return requestFocusInWindow(); } @Override public boolean traverseFocusOut(boolean forward) { - debug_println("HostContainer.traverseFocusOut Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.traverseFocusOut Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); if (forward) { kfm.focusNextComponent(JFXPanel.this); @@ -1110,7 +1180,10 @@ public boolean traverseFocusOut(boolean forward) { @Override public void setPreferredSize(final int width, final int height) { - debug_println("HostContainer.setPreferredSize Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.setPreferredSize Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } invokeOnClientEDT(() -> { JFXPanel.this.pPreferredWidth = width; JFXPanel.this.pPreferredHeight = height; @@ -1120,7 +1193,10 @@ public void setPreferredSize(final int width, final int height) { @Override public void repaint() { - debug_println("HostContainer.repaint Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.repaint Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } invokeOnClientEDT(() -> { JFXPanel.this.repaint(); }); @@ -1128,13 +1204,19 @@ public void repaint() { @Override public void setEnabled(final boolean enabled) { - debug_println("HostContainer.setEnabled Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.setEnabled Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } JFXPanel.this.setFxEnabled(enabled); } @Override public void setCursor(CursorFrame cursorFrame) { - debug_println("HostContainer.setCursor Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.setCursor Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } final Cursor cursor = getPlatformCursor(cursorFrame); invokeOnClientEDT(() -> { JFXPanel.this.setCursor(cursor); @@ -1159,7 +1241,10 @@ private Cursor getPlatformCursor(final CursorFrame cursorFrame) { @Override public boolean grabFocus() { - debug_println("HostContainer.grabFocus Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.grabFocus Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return true; @@ -1176,7 +1261,10 @@ public boolean grabFocus() { @Override public void ungrabFocus() { - debug_println("HostContainer.ungrabFocus Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + if (DEBUG) { + debug_println("HostContainer.ungrabFocus Thread " + Thread.currentThread().getName() + + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); + } // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return; From 115001f6c92b587574c67624f0033cabaabe04d0 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 13 Nov 2025 03:25:46 +0000 Subject: [PATCH 04/10] debug log fix --- .../javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index d475cdee034..405058f5995 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -680,7 +680,6 @@ private AffineTransform getCurrentTransform() { // called on EDT only private void updateComponentSize() { - debug_println("updateComponentSize Thread " + Thread.currentThread().getName() + " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); int oldWidth = pWidth; int oldHeight = pHeight; if (getBorder() != null) { From 7bf8a91e364aeed228738d963688f2b0a094ecc6 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 17 Nov 2025 16:31:41 +0000 Subject: [PATCH 05/10] Remove debug, store transient var in temp var in EDT methods --- .../java/javafx/embed/swing/JFXPanel.java | 176 +++--------------- 1 file changed, 26 insertions(+), 150 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 405058f5995..491372c0f9e 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -205,13 +205,6 @@ public class JFXPanel extends JComponent { @SuppressWarnings("doclint:missing") private JFXPanelInteropN jfxPanelIOP; - private static final boolean DEBUG = Boolean.getBoolean("jfxpanel.debug"); - private static final String debugPrefix = "JFXPanel:>> "; - - private static void debug_println(String str) { - System.out.println(debugPrefix + " " + str); - } - private synchronized void registerFinishListener() { if (instanceCount.getAndIncrement() > 0) { // Already registered @@ -281,10 +274,6 @@ private synchronized static void initFx() { public JFXPanel() { super(); - if (DEBUG) { - debug_println("JFXPanel Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } jfxPanelIOP = new JFXPanelInteropN(); initFx(); @@ -310,10 +299,6 @@ public JFXPanel() { * @return the {@code Scene} attached to this {@code JFXPanel} */ public Scene getScene() { - if (DEBUG) { - debug_println("getScene Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } return scene; } @@ -328,10 +313,6 @@ public Scene getScene() { * @see javafx.application.Platform#isFxApplicationThread() */ public void setScene(final Scene newScene) { - if (DEBUG) { - debug_println("setScene Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } if (Toolkit.getToolkit().isFxUserThread()) { setSceneImpl(newScene); } else { @@ -379,10 +360,6 @@ private void setSceneImpl(Scene newScene) { */ @Override public final void setOpaque(boolean opaque) { - if (DEBUG) { - debug_println("setOpaque Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } // Don't let user control opacity if (!opaque) { super.setOpaque(opaque); @@ -399,10 +376,6 @@ public final void setOpaque(boolean opaque) { */ @Override public final boolean isOpaque() { - if (DEBUG) { - debug_println("isOpaque Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } return false; } @@ -533,10 +506,7 @@ private void sendMouseEventToFX(MouseEvent e) { */ @Override protected void processMouseEvent(MouseEvent e) { - if (DEBUG) { - debug_println("processMouseEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } + var hStagePeer = stagePeer; if ((e.getID() == MouseEvent.MOUSE_PRESSED) && (e.getButton() == MouseEvent.BUTTON1)) { if (isFocusable() && !hasFocus()) { @@ -544,9 +514,9 @@ protected void processMouseEvent(MouseEvent e) { // The extra simulated mouse pressed event is removed by making the JavaFX scene focused. // It is safe, because in JavaFX only the method "setFocused(true)" is called, // which doesn't have any side-effects when called multiple times. - if (stagePeer != null) { + if (hStagePeer != null) { int focusCause = AbstractEvents.FOCUSEVENT_ACTIVATED; - stagePeer.setFocused(true, focusCause); + hStagePeer.setFocused(true, focusCause); } } } @@ -564,10 +534,6 @@ protected void processMouseEvent(MouseEvent e) { */ @Override protected void processMouseMotionEvent(MouseEvent e) { - if (DEBUG) { - debug_println("processMouseMotionEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } sendMouseEventToFX(e); super.processMouseMotionEvent(e); } @@ -582,10 +548,6 @@ protected void processMouseMotionEvent(MouseEvent e) { */ @Override protected void processMouseWheelEvent(MouseWheelEvent e) { - if (DEBUG) { - debug_println("processMouseWheelEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } sendMouseEventToFX(e); super.processMouseWheelEvent(e); } @@ -615,24 +577,18 @@ private void sendKeyEventToFX(final KeyEvent e) { */ @Override protected void processKeyEvent(KeyEvent e) { - if (DEBUG) { - debug_println("processKeyEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } sendKeyEventToFX(e); super.processKeyEvent(e); } private void sendResizeEventToFX() { - if (DEBUG) { - debug_println("sendResizeEventToFX Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } - if (stagePeer != null) { - stagePeer.setSize(pWidth, pHeight); + var hStagePeer = stagePeer; + var hScenePeer = scenePeer; + if (hStagePeer != null) { + hStagePeer.setSize(pWidth, pHeight); } - if (scenePeer != null) { - scenePeer.setSize(pWidth, pHeight); + if (hScenePeer != null) { + hScenePeer.setSize(pWidth, pHeight); } } @@ -647,10 +603,6 @@ private void sendResizeEventToFX() { */ @Override protected void processComponentEvent(ComponentEvent e) { - if (DEBUG) { - debug_println("processComponentEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } switch (e.getID()) { case ComponentEvent.COMPONENT_RESIZED: { updateComponentSize(); @@ -728,11 +680,9 @@ private boolean updateScreenLocation() { } private void sendMoveEventToFX() { - if (stagePeer == null) { - return; + if (stagePeer != null) { + stagePeer.setLocation(screenX, screenY); } - - stagePeer.setLocation(screenX, screenY); } /** @@ -746,10 +696,6 @@ private void sendMoveEventToFX() { */ @Override protected void processHierarchyBoundsEvent(HierarchyEvent e) { - if (DEBUG) { - debug_println("processHierarchyBoundsEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } if (e.getID() == HierarchyEvent.ANCESTOR_MOVED) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -760,10 +706,6 @@ protected void processHierarchyBoundsEvent(HierarchyEvent e) { @Override protected void processHierarchyEvent(HierarchyEvent e) { - if (DEBUG) { - debug_println("processHierarchyEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -773,7 +715,8 @@ protected void processHierarchyEvent(HierarchyEvent e) { } private void sendFocusEventToFX(final FocusEvent e) { - if ((stage == null) || (stagePeer == null) || !isFxEnabled()) { + var hStagePeer = stagePeer; + if ((stage == null) || (hStagePeer == null) || !isFxEnabled()) { return; } @@ -788,7 +731,7 @@ private void sendFocusEventToFX(final FocusEvent e) { focusCause = AbstractEvents.FOCUSEVENT_TRAVERSED_BACKWARD; } } - stagePeer.setFocused(focused, focusCause); + hStagePeer.setFocused(focused, focusCause); } /** @@ -801,10 +744,6 @@ private void sendFocusEventToFX(final FocusEvent e) { */ @Override protected void processFocusEvent(FocusEvent e) { - if (DEBUG) { - debug_println("processFocusEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } sendFocusEventToFX(e); super.processFocusEvent(e); } @@ -840,10 +779,6 @@ private void createResizePixelBuffer(double newScaleFactorX, double newScaleFact @Override protected void processInputMethodEvent(InputMethodEvent e) { - if (DEBUG) { - debug_println("processInputMethodEvent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } if (e.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) { sendInputMethodEventToFX(e); } @@ -857,11 +792,13 @@ private void sendInputMethodEventToFX(InputMethodEvent e) { if (e.getCaret() != null) { insertionIndex = e.getCaret().getInsertionIndex(); } - scenePeer.inputMethodEvent( - javafx.scene.input.InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, - InputMethodSupport.inputMethodEventComposed(t, e.getCommittedCharacterCount()), - t.substring(0, e.getCommittedCharacterCount()), - insertionIndex); + if (scenePeer != null) { + scenePeer.inputMethodEvent( + javafx.scene.input.InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, + InputMethodSupport.inputMethodEventComposed(t, e.getCommittedCharacterCount()), + t.substring(0, e.getCommittedCharacterCount()), + insertionIndex); + } } /** @@ -874,14 +811,9 @@ private void sendInputMethodEventToFX(InputMethodEvent e) { * @see #isOpaque() */ @Override - protected synchronized void paintComponent(Graphics g) { - if (DEBUG) { - debug_println("paintComponent Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } + protected void paintComponent(Graphics g) { var hScenePeer = scenePeer; - var hStage = stage; - if (hScenePeer == null || hStage == null) { + if (hScenePeer == null || stage == null) { return; } if (pixelsIm == null) { @@ -901,7 +833,7 @@ protected synchronized void paintComponent(Graphics g) { try { ComponentOrientation cor = this.getComponentOrientation(); boolean rtl = ComponentOrientation.RIGHT_TO_LEFT.equals(cor); - hStage.setNodeOrientation(rtl ? NodeOrientation.RIGHT_TO_LEFT : + stage.setNodeOrientation(rtl ? NodeOrientation.RIGHT_TO_LEFT : NodeOrientation.LEFT_TO_RIGHT); gg = g.create(); @@ -946,10 +878,6 @@ protected synchronized void paintComponent(Graphics g) { */ @Override public Dimension getPreferredSize() { - if (DEBUG) { - debug_println("getPreferredSize Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } if (isPreferredSizeSet() || scenePeer == null) { return super.getPreferredSize(); } @@ -1024,10 +952,6 @@ private void setFxEnabled(boolean enabled) { */ @Override public void addNotify() { - if (DEBUG) { - debug_println("addNotify Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } super.addNotify(); registerFinishListener(); @@ -1038,17 +962,13 @@ public void addNotify() { SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && !stage.isShowing()) { stage.show(); - sendMoveEventToFX(); } }); + sendMoveEventToFX(); } @Override public InputMethodRequests getInputMethodRequests() { - if (DEBUG) { - debug_println("getInputMethodRequests Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } EmbeddedSceneInterface scene = scenePeer; if (scene == null) { return new InputMethodSupport.InputMethodRequestsAdapter(null); @@ -1062,10 +982,6 @@ public InputMethodRequests getInputMethodRequests() { * chain of parent components are removed. */ @Override public void removeNotify() { - if (DEBUG) { - debug_println("removeNotify Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && stage.isShowing()) { stage.hide(); @@ -1099,10 +1015,6 @@ private class HostContainer implements HostInterface { @Override public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { - if (DEBUG) { - debug_println("HostContainer.setEmbeddedStage Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } stagePeer = embeddedStage; var hStagePeer = stagePeer; if (hStagePeer == null) { @@ -1115,16 +1027,12 @@ public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { if (hStagePeer != null && JFXPanel.this.isFocusOwner()) { hStagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED); } + sendMoveEventToFX(); }); - sendMoveEventToFX(); } @Override public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { - if (DEBUG) { - debug_println("HostContainer.setEmbeddedScene Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } if (scenePeer == embeddedScene) { return; } @@ -1155,19 +1063,11 @@ public void setEmbeddedScene(EmbeddedSceneInterface embeddedScene) { @Override public boolean requestFocus() { - if (DEBUG) { - debug_println("HostContainer.requestFocus Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } return requestFocusInWindow(); } @Override public boolean traverseFocusOut(boolean forward) { - if (DEBUG) { - debug_println("HostContainer.traverseFocusOut Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); if (forward) { kfm.focusNextComponent(JFXPanel.this); @@ -1179,10 +1079,6 @@ public boolean traverseFocusOut(boolean forward) { @Override public void setPreferredSize(final int width, final int height) { - if (DEBUG) { - debug_println("HostContainer.setPreferredSize Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } invokeOnClientEDT(() -> { JFXPanel.this.pPreferredWidth = width; JFXPanel.this.pPreferredHeight = height; @@ -1192,10 +1088,6 @@ public void setPreferredSize(final int width, final int height) { @Override public void repaint() { - if (DEBUG) { - debug_println("HostContainer.repaint Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } invokeOnClientEDT(() -> { JFXPanel.this.repaint(); }); @@ -1203,19 +1095,11 @@ public void repaint() { @Override public void setEnabled(final boolean enabled) { - if (DEBUG) { - debug_println("HostContainer.setEnabled Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } JFXPanel.this.setFxEnabled(enabled); } @Override public void setCursor(CursorFrame cursorFrame) { - if (DEBUG) { - debug_println("HostContainer.setCursor Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } final Cursor cursor = getPlatformCursor(cursorFrame); invokeOnClientEDT(() -> { JFXPanel.this.setCursor(cursor); @@ -1240,10 +1124,6 @@ private Cursor getPlatformCursor(final CursorFrame cursorFrame) { @Override public boolean grabFocus() { - if (DEBUG) { - debug_println("HostContainer.grabFocus Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return true; @@ -1260,10 +1140,6 @@ public boolean grabFocus() { @Override public void ungrabFocus() { - if (DEBUG) { - debug_println("HostContainer.ungrabFocus Thread " + Thread.currentThread().getName() + - " isFXUserThread " + Toolkit.getToolkit().isFxUserThread()); - } // On X11 grab is limited to a single XDisplay connection, // so we can't delegate it to another GUI toolkit. if (PlatformUtil.isLinux()) return; From fc830a7effba76949bda37104d5893b60da9bb2b Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 18 Nov 2025 13:20:04 +0000 Subject: [PATCH 06/10] temp-var handling in few other methods --- .../java/javafx/embed/swing/JFXPanel.java | 82 ++++++++++++++++--- 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 491372c0f9e..6b78e8b973d 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -150,12 +150,15 @@ public class JFXPanel extends JComponent { private transient HostContainer hostContainer; + // Set in FX thread and accessed in EDT/FX threads private transient volatile EmbeddedWindow stage; + private transient volatile Scene scene; // Accessed on EDT only private transient SwingDnD dnd; + // Set in FX thread and accessed in EDT/FX threads private transient EmbeddedStageInterface stagePeer; private transient EmbeddedSceneInterface scenePeer; @@ -417,6 +420,7 @@ private Point2D convertSwingToFxPixel(GraphicsConfiguration g, double wx, double return new Point2D(newx, newy); } + // Called on EDT thread private void sendMouseEventToFX(MouseEvent e) { var hScenePeer = scenePeer; if (hScenePeer == null || !isFxEnabled()) { @@ -506,6 +510,9 @@ private void sendMouseEventToFX(MouseEvent e) { */ @Override protected void processMouseEvent(MouseEvent e) { + + // Called on EDT thread. + var hStagePeer = stagePeer; if ((e.getID() == MouseEvent.MOUSE_PRESSED) && (e.getButton() == MouseEvent.BUTTON1)) { @@ -534,6 +541,9 @@ protected void processMouseEvent(MouseEvent e) { */ @Override protected void processMouseMotionEvent(MouseEvent e) { + + // Called on EDT thread. + sendMouseEventToFX(e); super.processMouseMotionEvent(e); } @@ -548,11 +558,17 @@ protected void processMouseMotionEvent(MouseEvent e) { */ @Override protected void processMouseWheelEvent(MouseWheelEvent e) { + + // Called on EDT thread. + sendMouseEventToFX(e); super.processMouseWheelEvent(e); } private void sendKeyEventToFX(final KeyEvent e) { + + // Called on EDT thread. + var hScenePeer = scenePeer; if (hScenePeer == null || !isFxEnabled()) { return; @@ -577,11 +593,17 @@ private void sendKeyEventToFX(final KeyEvent e) { */ @Override protected void processKeyEvent(KeyEvent e) { + + // Called on EDT thread. + sendKeyEventToFX(e); super.processKeyEvent(e); } private void sendResizeEventToFX() { + + // Called on EDT thread. + var hStagePeer = stagePeer; var hScenePeer = scenePeer; if (hStagePeer != null) { @@ -603,6 +625,9 @@ private void sendResizeEventToFX() { */ @Override protected void processComponentEvent(ComponentEvent e) { + + // Called on EDT thread. + switch (e.getID()) { case ComponentEvent.COMPONENT_RESIZED: { updateComponentSize(); @@ -657,7 +682,7 @@ private void updateComponentSize() { var hScenePeer = scenePeer; if (hScenePeer != null) { hScenePeer.setPixelScaleFactors((float) newScaleFactorX, - (float) newScaleFactorY); + (float) newScaleFactorY); } scaleFactorX = newScaleFactorX; scaleFactorY = newScaleFactorY; @@ -680,8 +705,12 @@ private boolean updateScreenLocation() { } private void sendMoveEventToFX() { - if (stagePeer != null) { - stagePeer.setLocation(screenX, screenY); + + // Called on EDT thread. + + var hStagePeer = stagePeer; + if (hStagePeer != null) { + hStagePeer.setLocation(screenX, screenY); } } @@ -696,6 +725,9 @@ private void sendMoveEventToFX() { */ @Override protected void processHierarchyBoundsEvent(HierarchyEvent e) { + + // Called on EDT thread. + if (e.getID() == HierarchyEvent.ANCESTOR_MOVED) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -706,6 +738,9 @@ protected void processHierarchyBoundsEvent(HierarchyEvent e) { @Override protected void processHierarchyEvent(HierarchyEvent e) { + + // Called on EDT thread. + if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) { if (updateScreenLocation()) { sendMoveEventToFX(); @@ -715,6 +750,9 @@ protected void processHierarchyEvent(HierarchyEvent e) { } private void sendFocusEventToFX(final FocusEvent e) { + + // Called on EDT thread. + var hStagePeer = stagePeer; if ((stage == null) || (hStagePeer == null) || !isFxEnabled()) { return; @@ -744,6 +782,9 @@ private void sendFocusEventToFX(final FocusEvent e) { */ @Override protected void processFocusEvent(FocusEvent e) { + + // Called on EDT thread. + sendFocusEventToFX(e); super.processFocusEvent(e); } @@ -779,6 +820,9 @@ private void createResizePixelBuffer(double newScaleFactorX, double newScaleFact @Override protected void processInputMethodEvent(InputMethodEvent e) { + + // Called on EDT thread. + if (e.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) { sendInputMethodEventToFX(e); } @@ -786,14 +830,18 @@ protected void processInputMethodEvent(InputMethodEvent e) { } private void sendInputMethodEventToFX(InputMethodEvent e) { + + // Called on EDT thread. + String t = InputMethodSupport.getTextForEvent(e); int insertionIndex = 0; if (e.getCaret() != null) { insertionIndex = e.getCaret().getInsertionIndex(); } - if (scenePeer != null) { - scenePeer.inputMethodEvent( + var hScenePeer = scenePeer; + if (hScenePeer != null) { + hScenePeer.inputMethodEvent( javafx.scene.input.InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, InputMethodSupport.inputMethodEventComposed(t, e.getCommittedCharacterCount()), t.substring(0, e.getCommittedCharacterCount()), @@ -812,8 +860,14 @@ private void sendInputMethodEventToFX(InputMethodEvent e) { */ @Override protected void paintComponent(Graphics g) { + + // Called on EDT thread. + + // scenePeer, stage set in FX thread and being accessed in EDT var hScenePeer = scenePeer; - if (hScenePeer == null || stage == null) { + var hStage = stage; + + if (hScenePeer == null || hStage == null) { return; } if (pixelsIm == null) { @@ -833,7 +887,7 @@ protected void paintComponent(Graphics g) { try { ComponentOrientation cor = this.getComponentOrientation(); boolean rtl = ComponentOrientation.RIGHT_TO_LEFT.equals(cor); - stage.setNodeOrientation(rtl ? NodeOrientation.RIGHT_TO_LEFT : + hStage.setNodeOrientation(rtl ? NodeOrientation.RIGHT_TO_LEFT : NodeOrientation.LEFT_TO_RIGHT); gg = g.create(); @@ -855,7 +909,7 @@ protected void paintComponent(Graphics g) { createResizePixelBuffer(newScaleFactorX, newScaleFactorY); // The scene will request repaint. hScenePeer.setPixelScaleFactors((float) newScaleFactorX, - (float) newScaleFactorY); + (float) newScaleFactorY); scaleFactorX = newScaleFactorX; scaleFactorY = newScaleFactorY; } @@ -909,13 +963,15 @@ private void setFxEnabled(boolean enabled) { } private transient AWTEventListener ungrabListener = event -> { + var hStagePeer = JFXPanel.this.stagePeer; + if (jfxPanelIOP.isUngrabEvent(event)) { SwingNodeHelper.runOnFxThread(() -> { - if (JFXPanel.this.stagePeer != null && + if (hStagePeer != null && getScene() != null && getScene().getFocusOwner() != null && getScene().getFocusOwner().isFocused()) { - JFXPanel.this.stagePeer.focusUngrab(); + hStagePeer.focusUngrab(); } }); } @@ -929,7 +985,7 @@ private void setFxEnabled(boolean enabled) { if (jfxPanelWindow == eventWindow) { SwingNodeHelper.runOnFxThread(() -> { - if (JFXPanel.this.stagePeer != null) { + if (hStagePeer != null) { // No need to check if grab is active or not. // NoAutoHide popups don't request the grab and // ignore the Ungrab event anyway. @@ -937,7 +993,7 @@ private void setFxEnabled(boolean enabled) { // user clicks some non-FX content, even if for // some reason they didn't install the grab when // they were shown. - JFXPanel.this.stagePeer.focusUngrab(); + hStagePeer.focusUngrab(); } }); } @@ -1013,6 +1069,8 @@ final BufferedImage test_getPixelsIm() { private class HostContainer implements HostInterface { + // These methods are invoked from FX thread + @Override public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) { stagePeer = embeddedStage; From 290ed2de0ad76ca2bbaf787c7992f730280e01f6 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 19 Nov 2025 12:32:34 +0000 Subject: [PATCH 07/10] Review comment --- .../src/main/java/javafx/embed/swing/JFXPanel.java | 12 ++++++------ .../test/javafx/embed/swing/JFXPanelNPETest.java | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 6b78e8b973d..3a61b4e0817 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -153,6 +153,7 @@ public class JFXPanel extends JComponent { // Set in FX thread and accessed in EDT/FX threads private transient volatile EmbeddedWindow stage; + // Set and accessed in FX thread only private transient volatile Scene scene; // Accessed on EDT only @@ -963,15 +964,14 @@ private void setFxEnabled(boolean enabled) { } private transient AWTEventListener ungrabListener = event -> { - var hStagePeer = JFXPanel.this.stagePeer; if (jfxPanelIOP.isUngrabEvent(event)) { SwingNodeHelper.runOnFxThread(() -> { - if (hStagePeer != null && + if (JFXPanel.this.stagePeer != null && getScene() != null && getScene().getFocusOwner() != null && getScene().getFocusOwner().isFocused()) { - hStagePeer.focusUngrab(); + JFXPanel.this.stagePeer.focusUngrab(); } }); } @@ -985,7 +985,7 @@ private void setFxEnabled(boolean enabled) { if (jfxPanelWindow == eventWindow) { SwingNodeHelper.runOnFxThread(() -> { - if (hStagePeer != null) { + if (JFXPanel.this.stagePeer != null) { // No need to check if grab is active or not. // NoAutoHide popups don't request the grab and // ignore the Ungrab event anyway. @@ -993,7 +993,7 @@ private void setFxEnabled(boolean enabled) { // user clicks some non-FX content, even if for // some reason they didn't install the grab when // they were shown. - hStagePeer.focusUngrab(); + JFXPanel.this.stagePeer.focusUngrab(); } }); } @@ -1019,8 +1019,8 @@ public void addNotify() { if ((stage != null) && !stage.isShowing()) { stage.show(); } + SwingNodeHelper.runOnEDT(() -> sendMoveEventToFX()); }); - sendMoveEventToFX(); } @Override diff --git a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java index e451a09deee..ccb67cefafe 100644 --- a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java +++ b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java @@ -102,7 +102,6 @@ public void uncaughtException(Thread t, Throwable e) { Platform.runLater(() -> contentPane.setScene(webView.getScene())); Thread.sleep(100); } - System.out.println("failure = " + failure.get()); Assertions.assertFalse(failure.get()); } From 4049ad67ae86b81b37874a43a717b778a57a2cec Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 19 Nov 2025 12:50:16 +0000 Subject: [PATCH 08/10] Review comment --- .../javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java index 3a61b4e0817..5aada2bfcb3 100644 --- a/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java +++ b/modules/javafx.swing/src/main/java/javafx/embed/swing/JFXPanel.java @@ -1018,8 +1018,8 @@ public void addNotify() { SwingNodeHelper.runOnFxThread(() -> { if ((stage != null) && !stage.isShowing()) { stage.show(); + SwingNodeHelper.runOnEDT(() -> sendMoveEventToFX()); } - SwingNodeHelper.runOnEDT(() -> sendMoveEventToFX()); }); } From f08b74171ae9dad8b732fb0c247b980f18c67a55 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 19 Nov 2025 16:23:50 +0000 Subject: [PATCH 09/10] Review comment --- .../main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java | 4 +--- .../src/main/java/com/sun/javafx/tk/quantum/GlassScene.java | 4 +++- .../test/java/test/javafx/embed/swing/JFXPanelNPETest.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java index 8455b059f4b..a84d6b736e2 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/EmbeddedScene.java @@ -155,9 +155,7 @@ public void setPixelScaleFactors(float scalex, float scaley) { entireSceneNeedsRepaint(); Platform.runLater(() -> { QuantumToolkit.runWithRenderLock(() -> { - if (getSceneState() != null) { - updateSceneState(); - } + updateSceneState(); return null; }); }); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java index e623060b306..12dc090e80f 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java @@ -250,7 +250,9 @@ final SceneState getSceneState() { final void updateSceneState() { // should only be called on the event thread - sceneState.update(); + if (getSceneState() != null) { + sceneState.update(); + } } protected View getPlatformView() { diff --git a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java index ccb67cefafe..220033e80a8 100644 --- a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java +++ b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java @@ -98,9 +98,9 @@ public void uncaughtException(Thread t, Throwable e) { for (int i = 0; i < 300; i++) { SwingUtilities.invokeLater(contentPane::repaint); Platform.runLater(() -> contentPane.setScene(null)); - Thread.sleep(100); + Thread.sleep(1); Platform.runLater(() -> contentPane.setScene(webView.getScene())); - Thread.sleep(100); + Thread.sleep(1); } Assertions.assertFalse(failure.get()); } From f66fff5c5914a800bb2ecd52e5704281b3a23922 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 19 Nov 2025 17:09:47 +0000 Subject: [PATCH 10/10] Review comment + OutputRedirect --- .../com/sun/javafx/tk/quantum/GlassScene.java | 2 +- .../javafx/embed/swing/JFXPanelNPETest.java | 28 ++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java index 12dc090e80f..e888730b2fc 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/GlassScene.java @@ -250,7 +250,7 @@ final SceneState getSceneState() { final void updateSceneState() { // should only be called on the event thread - if (getSceneState() != null) { + if (sceneState != null) { sceneState.update(); } } diff --git a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java index 220033e80a8..4a58dd2eb28 100644 --- a/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java +++ b/tests/system/src/test/java/test/javafx/embed/swing/JFXPanelNPETest.java @@ -43,6 +43,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import test.javafx.util.OutputRedirect; import test.util.Util; public class JFXPanelNPETest { @@ -86,23 +87,19 @@ public void doCleanup() { @Test public void testSceneNPE() throws Exception { - failure = new AtomicBoolean(false); - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - e.printStackTrace(); - failure.set(true); + OutputRedirect.suppressStderr(); + try { + SwingUtilities.invokeAndWait(JFXPanelNPETest::createUI); + for (int i = 0; i < 300; i++) { + SwingUtilities.invokeLater(contentPane::repaint); + Platform.runLater(() -> contentPane.setScene(null)); + Thread.sleep(1); + Platform.runLater(() -> contentPane.setScene(webView.getScene())); + Thread.sleep(1); } - }); - SwingUtilities.invokeAndWait(JFXPanelNPETest::createUI); - for (int i = 0; i < 300; i++) { - SwingUtilities.invokeLater(contentPane::repaint); - Platform.runLater(() -> contentPane.setScene(null)); - Thread.sleep(1); - Platform.runLater(() -> contentPane.setScene(webView.getScene())); - Thread.sleep(1); + } finally { + OutputRedirect.checkAndRestoreStderr(); } - Assertions.assertFalse(failure.get()); } protected static void createUI() { @@ -122,4 +119,3 @@ private static void fx(final JFXPanel contentPane) { contentPane.setScene(new Scene(webView)); } } -