/* * Copyright (c) 2023, 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. */ import java.awt.AWTException; import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; import java.awt.ScrollPane; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.atomic.AtomicReference; import static java.awt.EventQueue.invokeAndWait; /* * @test * @bug 8297923 * @key headful * @requires os.family=="windows" * @summary Verifies no GDI objects are leaked after scrolling continuously * @run main/othervm -Dsun.java2d.d3d=false ScrollPaneLeakTest */ public class ScrollPaneLeakTest { /** * The number of times the test repeats scrolling cycles. */ private static final int REPEATS = 1; /** * The number of times the robot moves the scroll bar thumb down and up * per one cycle. */ private static final int UP_DOWN_CYCLES = 20; private static final Color CANVAS_FOREGROUND = new Color(200, 240, 200); private static final Color CANVAS_BACKGROUND = new Color(240, 200, 240); private static final Color SCROLL_PANE_BACKGROUND = new Color(240, 240, 200); private static final Dimension CANVAS_SIZE = new Dimension(400, 600); private static final Dimension FRAME_SIZE = new Dimension(CANVAS_SIZE.width * 2, 3 * CANVAS_SIZE.height / 4); private static final Dimension SCROLL_PANE_SIZE = new Dimension(CANVAS_SIZE.width, CANVAS_SIZE.height / 2); private static Frame frame; private static ScrollPane scroll; private static final AtomicReference frameBounds = new AtomicReference<>(); private static final AtomicReference scrollBounds = new AtomicReference<>(); private static final AtomicReference vertBarWidth = new AtomicReference<>(); private static final AtomicReference horzBarHeight = new AtomicReference<>(); public static void main(String[] args) throws InterruptedException, InvocationTargetException, AWTException { try { invokeAndWait(ScrollPaneLeakTest::createUI); final Robot robot = new Robot(); robot.waitForIdle(); invokeAndWait(() -> frame.setExtendedState(frame.getExtendedState() | Frame.MAXIMIZED_BOTH)); robot.waitForIdle(); invokeAndWait(() -> { scrollBounds.set(new Rectangle(scroll.getLocationOnScreen(), scroll.getSize())); vertBarWidth.set(scroll.getVScrollbarWidth()); horzBarHeight.set(scroll.getHScrollbarHeight()); }); robot.waitForIdle(); invokeAndWait(() -> scroll.setScrollPosition(0, 0)); robot.waitForIdle(); robot.delay(1000); final Rectangle sb = scrollBounds.get(); final int vbar = vertBarWidth.get(); final int hbar = horzBarHeight.get() * 2; final Point pos = new Point(); for (int no = 0; no < REPEATS; no++) { pos.x = sb.x + sb.width - vbar / 3; pos.y = sb.y + hbar; robot.mouseMove(pos.x, pos.y); robot.mousePress(MouseEvent.BUTTON1_DOWN_MASK); for (int i = 0; i < UP_DOWN_CYCLES; i++) { while (++pos.y < sb.y + sb.height - hbar) { robot.mouseMove(pos.x, pos.y); robot.delay(5); } while (--pos.y > sb.y + hbar) { robot.mouseMove(pos.x, pos.y); robot.delay(5); } } robot.mouseRelease(MouseEvent.BUTTON1_DOWN_MASK); invokeAndWait(() -> frame.setExtendedState(frame.getExtendedState() | Frame.ICONIFIED)); robot.delay(500); invokeAndWait(() -> frame.setExtendedState(frame.getExtendedState() & ~Frame.ICONIFIED)); robot.delay(500); } invokeAndWait(() -> scroll.setScrollPosition(0, sb.height / 2)); invokeAndWait(() -> { Rectangle bounds = frame.getBounds(); frameBounds.set(bounds); }); // Throws OutOfMemoryError when the test fails robot.createScreenCapture(frameBounds.get()); System.out.println("Robot created a screenshot: test passed"); } finally { invokeAndWait(frame::dispose); } } private static void createUI() { frame = new Frame("Scroll Pane Leak Test"); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.setLayout(new FlowLayout(FlowLayout.CENTER)); frame.setLocation(0, 0); Canvas canvas = new Canvas() { @Override public void paint(Graphics g) { g.setColor(CANVAS_FOREGROUND); g.fillRect(0, 0, getWidth(), getHeight()); } }; canvas.setBackground(CANVAS_BACKGROUND); canvas.setSize(CANVAS_SIZE); scroll = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); scroll.add(canvas); scroll.setSize(SCROLL_PANE_SIZE); scroll.setBackground(SCROLL_PANE_BACKGROUND); frame.add(scroll); frame.setSize(FRAME_SIZE); frame.setVisible(true); } }