From cc696566e4661d7b68fdb1243e595ed4d2b77475 Mon Sep 17 00:00:00 2001 From: Sergey Chernyshev Date: Wed, 4 Jun 2025 10:02:46 +0200 Subject: [PATCH 1/3] Backport 46251bc6e248a19e8d78173ff8d0502c68ee1acb --- .../swing/plaf/basic/BasicOptionPaneUI.java | 11 +++- .../TestOptionPaneStackOverflow.java | 59 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index b43e5d24c6c..e02e083826c 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -83,6 +83,7 @@ public class BasicOptionPaneUI extends OptionPaneUI { public static final int MinimumHeight = 90; private static String newline; + private static int recursionCount; /** * JOptionPane that the receiver is providing the @@ -396,7 +397,7 @@ protected void addMessageComponents(Container container, if (nl == 0) { JPanel breakPanel = new JPanel() { public Dimension getPreferredSize() { - Font f = getFont(); + Font f = getFont(); if (f != null) { return new Dimension(1, f.getSize() + 2); @@ -411,8 +412,14 @@ public Dimension getPreferredSize() { addMessageComponents(container, cons, s.substring(0, nl), maxll, false); } + // Prevent recursion of more than + // 200 successive newlines in a message + if (recursionCount++ > 200) { + recursionCount = 0; + return; + } addMessageComponents(container, cons, s.substring(nl + nll), maxll, - false); + false); } else if (len > maxll) { Container c = Box.createVerticalBox(); diff --git a/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java b/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java new file mode 100644 index 00000000000..f8650c28ac4 --- /dev/null +++ b/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022, 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. + * + * 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. + */ + +/* @test + @bug 8224267 + @key headful + @summary Verifies if StackOverflowError is not thrown for multiple newlines + @run main TestOptionPaneStackOverflow + */ + +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; + +public class TestOptionPaneStackOverflow +{ + static JFrame frame; + + public static void main(String[] argv) throws Exception + { + try { + String message = java.nio.CharBuffer.allocate(5000).toString(). + replace('\0','\n'); + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame(); + JOptionPane optionPane = new JOptionPane(); + optionPane.createDialog(frame, null); + optionPane.setMessage(message); + }); + } finally { + SwingUtilities.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + } +} From 3fc7e51dd4030a0f171a62f89b29b3109781810c Mon Sep 17 00:00:00 2001 From: Sergey Chernyshev Date: Sat, 7 Jun 2025 02:05:17 +0200 Subject: [PATCH 2/3] Backport 6e18883d8ffd9a7b7d495da05e9859dc1d1a2677 --- .../classes/javax/swing/plaf/basic/BasicOptionPaneUI.java | 4 ++++ .../javax/swing/JOptionPane/TestOptionPaneStackOverflow.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index e02e083826c..b4b6632b0f6 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -318,6 +318,7 @@ protected Container createMessageArea() { "OptionPane.messageAnchor", GridBagConstraints.CENTER); cons.insets = new Insets(0,0,3,0); + recursionCount = 0; addMessageComponents(body, cons, getMessage(), getMaxCharactersPerLineCount(), false); top.add(realBody, BorderLayout.CENTER); @@ -414,8 +415,11 @@ public Dimension getPreferredSize() { } // Prevent recursion of more than // 200 successive newlines in a message + // and indicate message is truncated via ellipsis if (recursionCount++ > 200) { recursionCount = 0; + addMessageComponents(container, cons, new String("..."), + maxll,false); return; } addMessageComponents(container, cons, s.substring(nl + nll), maxll, diff --git a/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java b/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java index f8650c28ac4..9cfaea13755 100644 --- a/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java +++ b/jdk/test/javax/swing/JOptionPane/TestOptionPaneStackOverflow.java @@ -22,7 +22,7 @@ */ /* @test - @bug 8224267 + @bug 8224267 8290162 @key headful @summary Verifies if StackOverflowError is not thrown for multiple newlines @run main TestOptionPaneStackOverflow From 4b63d5396152f35f53b537b63051862d0ac1b6a9 Mon Sep 17 00:00:00 2001 From: Sergey Chernyshev Date: Sat, 7 Jun 2025 02:08:13 +0200 Subject: [PATCH 3/3] Backport 91072ee3934616ab2edc4850a59c0a25fd0de3b4 --- .../swing/plaf/basic/BasicOptionPaneUI.java | 103 +++++++++++------- .../swing/JOptionPane/TestJOptionHTMLTag.java | 91 ++++++++++++++++ 2 files changed, 155 insertions(+), 39 deletions(-) create mode 100644 jdk/test/javax/swing/JOptionPane/TestJOptionHTMLTag.java diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java index b4b6632b0f6..f785e24d9ab 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicOptionPaneUI.java @@ -393,50 +393,75 @@ protected void addMessageComponents(Container container, } else if ((nl = s.indexOf('\n')) >= 0) { nll = 1; } - if (nl >= 0) { - // break up newlines - if (nl == 0) { - JPanel breakPanel = new JPanel() { - public Dimension getPreferredSize() { - Font f = getFont(); - - if (f != null) { - return new Dimension(1, f.getSize() + 2); - } - return new Dimension(0, 0); - } - }; - breakPanel.setName("OptionPane.break"); - addMessageComponents(container, cons, breakPanel, maxll, - true); - } else { - addMessageComponents(container, cons, s.substring(0, nl), - maxll, false); - } - // Prevent recursion of more than - // 200 successive newlines in a message - // and indicate message is truncated via ellipsis - if (recursionCount++ > 200) { - recursionCount = 0; - addMessageComponents(container, cons, new String("..."), - maxll,false); - return; + if (s.contains("")) { + /* line break in html text is done by
tag + * and not by /n so it's incorrect to address newline + * same as non-html text. + * Text between tags are extracted + * and rendered as JLabel text + */ + int index1 = s.indexOf(""); + int index2 = s.indexOf(""); + String str = ""; + if (index2 >= 0) { + str = s.substring(index2 + "".length()); + s = s.substring(index1, index2 + + "".length()); } - addMessageComponents(container, cons, s.substring(nl + nll), maxll, - false); - - } else if (len > maxll) { - Container c = Box.createVerticalBox(); - c.setName("OptionPane.verticalBox"); - burstStringInto(c, s, maxll); - addMessageComponents(container, cons, c, maxll, true ); - - } else { JLabel label; - label = new JLabel( s, JLabel.LEADING ); + label = new JLabel(s, JLabel.LEADING); label.setName("OptionPane.label"); configureMessageLabel(label); addMessageComponents(container, cons, label, maxll, true); + if (!str.isEmpty()) { + addMessageComponents(container, cons, str, maxll, false); + } + } else { + if (nl >= 0) { + // break up newlines + if (nl == 0) { + @SuppressWarnings("serial") // anonymous class + JPanel breakPanel = new JPanel() { + public Dimension getPreferredSize() { + Font f = getFont(); + + if (f != null) { + return new Dimension(1, f.getSize() + 2); + } + return new Dimension(0, 0); + } + }; + breakPanel.setName("OptionPane.break"); + addMessageComponents(container, cons, breakPanel, maxll, + true); + } else { + addMessageComponents(container, cons, s.substring(0, nl), + maxll, false); + } + // Prevent recursion of more than + // 200 successive newlines in a message + // and indicate message is truncated via ellipsis + if (recursionCount++ > 200) { + recursionCount = 0; + addMessageComponents(container, cons, new String("..."), + maxll, false); + return; + } + addMessageComponents(container, cons, s.substring(nl + nll), maxll, + false); + + } else if (len > maxll) { + Container c = Box.createVerticalBox(); + c.setName("OptionPane.verticalBox"); + burstStringInto(c, s, maxll); + addMessageComponents(container, cons, c, maxll, true); + + } else { + JLabel label; + label = new JLabel(s, JLabel.LEADING); + label.setName("OptionPane.label"); + configureMessageLabel(label); + addMessageComponents(container, cons, label, maxll, true); + } } } } diff --git a/jdk/test/javax/swing/JOptionPane/TestJOptionHTMLTag.java b/jdk/test/javax/swing/JOptionPane/TestJOptionHTMLTag.java new file mode 100644 index 00000000000..df85ab33353 --- /dev/null +++ b/jdk/test/javax/swing/JOptionPane/TestJOptionHTMLTag.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2022, 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. + * + * 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. + */ +/* @test + * @bug 5074006 + * @key headful + * @library /java/awt/regtesthelpers + * @library /test/lib + * @summary Swing JOptionPane shows tag as a string after newline + * @run main/manual TestJOptionHTMLTag +*/ + +import java.awt.BorderLayout; +import static java.awt.Dialog.ModalityType; + +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; + +import jtreg.SkippedException; + +public class TestJOptionHTMLTag { + static String instructions + = "INSTRUCTIONS:\n" + + " Read the text in the above panel.\n" + + " If it does not contain string, press Pass else press Fail.\n"; + + private static boolean passed = false; + private static boolean skipped = false; + private static Thread mainThread = Thread.currentThread(); + public static void main(String[] args) throws Exception { + + try { + SwingUtilities.invokeLater(() -> { + String message = "" + "This is a test\n" + "\n\n\n\n"; + JOptionPane optionPane = new JOptionPane(); + optionPane.setMessage(message); + optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE); + optionPane.setOptions(new Object[]{"Pass", "Fail"}); + JDialog dialog = optionPane.createDialog("Test"); + dialog.setModalityType(ModalityType.TOOLKIT_MODAL); + dialog.setContentPane(optionPane); + + JTextArea textArea = new JTextArea(instructions); + textArea.setEditable(false); + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(textArea, BorderLayout.CENTER); + dialog.add(mainPanel); + dialog.pack(); + dialog.show(); + Object value = optionPane.getValue(); + if (value == null) { + skipped = true; + } else if (value.equals("Pass")) { + passed = true; + } + mainThread.interrupt(); + }); + Thread.sleep(10000); + } catch (InterruptedException e) { + if (passed) { + return; + } else if (!skipped) { + throw new RuntimeException("Test failed."); + } + } + throw new SkippedException("Test skipped."); + } +} +