1 /* 2 * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.awt.Button; 25 import java.awt.Component; 26 import java.awt.Dimension; 27 import java.awt.Frame; 28 import java.awt.GridLayout; 29 import java.awt.Panel; 30 import java.awt.Point; 31 import java.awt.Robot; 32 import java.awt.datatransfer.DataFlavor; 33 import java.awt.datatransfer.Transferable; 34 import java.awt.datatransfer.UnsupportedFlavorException; 35 import java.awt.dnd.DnDConstants; 36 import java.awt.dnd.DragGestureEvent; 37 import java.awt.dnd.DragGestureListener; 38 import java.awt.dnd.DragSource; 39 import java.awt.dnd.DragSourceDragEvent; 40 import java.awt.dnd.DragSourceDropEvent; 41 import java.awt.dnd.DragSourceEvent; 42 import java.awt.dnd.DragSourceListener; 43 import java.awt.dnd.DropTarget; 44 import java.awt.dnd.DropTargetContext; 45 import java.awt.dnd.DropTargetDragEvent; 46 import java.awt.dnd.DropTargetDropEvent; 47 import java.awt.dnd.DropTargetEvent; 48 import java.awt.dnd.DropTargetListener; 49 import java.awt.event.InputEvent; 50 import java.awt.event.KeyEvent; 51 import java.io.BufferedReader; 52 import java.io.File; 53 import java.io.IOException; 54 import java.io.InputStream; 55 import java.io.InputStreamReader; 56 import java.io.Serializable; 57 import java.util.concurrent.CountDownLatch; 58 import java.util.concurrent.TimeUnit; 59 60 /** 61 * @test 62 * @key headful 63 * @bug 4393148 8136999 8186263 64 * @summary tests that removal of the drop target or disposal of frame during 65 * drop processing doesn't cause crash 66 * @run main RemoveDropTargetCrashTest RUN_PROCESS 67 */ 68 public class RemoveDropTargetCrashTest { 69 70 private static final String RUN_PROCESS = "RUN_PROCESS"; 71 private static final String RUN_TEST = "RUN_TEST"; 72 private static boolean exception; 73 private static volatile CountDownLatch go; 74 75 public static void main(String[] args) throws Exception { 76 String command = args.length < 1 ? RUN_TEST : args[0]; 77 78 switch (command) { 79 case RUN_PROCESS: 80 runProcess(); 81 break; 82 case RUN_TEST: 83 for (int i = 0; i < 10; ++i) { 84 runTest(i * 10); 85 runTest(-1); 86 } 87 break; 88 default: 89 throw new RuntimeException("Unknown command: " + command); 90 } 91 } 92 93 private static void runTest(int delay) throws Exception { 94 95 Robot robot = new Robot(); 96 robot.setAutoDelay(10); 97 Frame frame = null; 98 try { 99 DragSourceButton dragSourceButton = new DragSourceButton(); 100 dragSourceButton.addActionListener(e -> go.countDown()); 101 102 DropTargetPanel dropTargetPanel = new DropTargetPanel(); 103 104 frame = new Frame(); 105 frame.setTitle("Test frame"); 106 frame.setLocation(200, 200); 107 frame.setLayout(new GridLayout(2, 1)); 108 frame.add(dragSourceButton); 109 frame.add(dropTargetPanel); 110 111 frame.pack(); 112 frame.setVisible(true); 113 114 robot.waitForIdle(); 115 robot.delay(200); 116 117 Point dragPoint = dragSourceButton.getLocationOnScreen(); 118 Dimension size = dragSourceButton.getSize(); 119 dragPoint.translate(size.width / 2, size.height / 2); 120 121 pressOnButton(robot, dragPoint); 122 123 Point dropPoint = dropTargetPanel.getLocationOnScreen(); 124 size = dropTargetPanel.getSize(); 125 dropPoint.translate(size.width / 2, size.height / 2); 126 127 robot.mouseMove(dragPoint.x, dragPoint.y); 128 robot.keyPress(KeyEvent.VK_CONTROL); 129 robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); 130 131 Point tmpPoint = new Point(dragPoint); 132 for (; !tmpPoint.equals(dropPoint); 133 tmpPoint.translate(sign(dropPoint.x - tmpPoint.x), 134 sign(dropPoint.y - tmpPoint.y))) { 135 robot.mouseMove(tmpPoint.x, tmpPoint.y); 136 } 137 robot.keyRelease(KeyEvent.VK_CONTROL); 138 robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); 139 // This delay() is important part of the test, because we need to 140 // test a different delays before frame.dispose(). 141 if (delay >= 0) { 142 robot.delay(delay); 143 } else { 144 robot.waitForIdle(); 145 robot.delay(1000); 146 147 Point clickPoint = dragSourceButton.getLocationOnScreen(); 148 size = dragSourceButton.getSize(); 149 clickPoint.translate(size.width / 2, size.height / 2); 150 151 if (clickPoint.equals(dragPoint)) { 152 throw new RuntimeException("Button was not moved"); 153 } 154 pressOnButton(robot, clickPoint); 155 } 156 } finally { 157 if (frame != null) { 158 frame.dispose(); 159 } 160 } 161 } 162 163 private static void pressOnButton(Robot robot, Point clickPoint) 164 throws InterruptedException { 165 go = new CountDownLatch(1); 166 robot.mouseMove(clickPoint.x, clickPoint.y); 167 robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); 168 robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); 169 if (!go.await(10, TimeUnit.SECONDS)) { 170 throw new RuntimeException("Button was not pressed"); 171 } 172 } 173 174 public static int sign(int n) { 175 return n < 0 ? -1 : n == 0 ? 0 : 1; 176 } 177 178 static class DragSourceButton extends Button implements Serializable, 179 Transferable, 180 DragGestureListener, 181 DragSourceListener { 182 183 private static DataFlavor dataflavor; 184 185 static { 186 try { 187 dataflavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); 188 dataflavor.setHumanPresentableName("Local Object Flavor"); 189 } catch (ClassNotFoundException e) { 190 throw new RuntimeException(e); 191 } 192 } 193 194 public DragSourceButton() { 195 this("DragSourceButton"); 196 } 197 198 public DragSourceButton(String str) { 199 super(str); 200 201 DragSource ds = DragSource.getDefaultDragSource(); 202 ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, 203 this); 204 } 205 206 public void dragGestureRecognized(DragGestureEvent dge) { 207 dge.startDrag(null, this, this); 208 } 209 210 public void dragEnter(DragSourceDragEvent dsde) { 211 } 212 213 public void dragExit(DragSourceEvent dse) { 214 } 215 216 public void dragOver(DragSourceDragEvent dsde) { 217 } 218 219 public void dragDropEnd(DragSourceDropEvent dsde) { 220 } 221 222 public void dropActionChanged(DragSourceDragEvent dsde) { 223 } 224 225 public Object getTransferData(DataFlavor flavor) 226 throws UnsupportedFlavorException, IOException { 227 228 if (!isDataFlavorSupported(flavor)) { 229 throw new UnsupportedFlavorException(flavor); 230 } 231 232 return this; 233 } 234 235 public DataFlavor[] getTransferDataFlavors() { 236 return new DataFlavor[]{dataflavor}; 237 } 238 239 public boolean isDataFlavorSupported(DataFlavor dflavor) { 240 return dataflavor.equals(dflavor); 241 } 242 } 243 244 static class DropTargetPanel extends Panel implements DropTargetListener { 245 246 final Dimension preferredSize = new Dimension(100, 100); 247 248 public DropTargetPanel() { 249 setDropTarget(new DropTarget(this, this)); 250 } 251 252 public Dimension getPreferredSize() { 253 return preferredSize; 254 } 255 256 public void dragEnter(DropTargetDragEvent dtde) { 257 } 258 259 public void dragExit(DropTargetEvent dte) { 260 } 261 262 public void dragOver(DropTargetDragEvent dtde) { 263 } 264 265 public void dropActionChanged(DropTargetDragEvent dtde) { 266 } 267 268 public void drop(DropTargetDropEvent dtde) { 269 270 setDropTarget(null); 271 272 DropTargetContext dtc = dtde.getDropTargetContext(); 273 274 if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { 275 dtde.acceptDrop(DnDConstants.ACTION_COPY); 276 } else { 277 dtde.rejectDrop(); 278 } 279 280 DataFlavor[] dfs = dtde.getCurrentDataFlavors(); 281 282 if (dfs != null && dfs.length >= 1) { 283 Transferable transfer = dtde.getTransferable(); 284 Component comp; 285 try { 286 comp = (Component) transfer.getTransferData(dfs[0]); 287 } catch (Throwable e) { 288 dtc.dropComplete(false); 289 throw new RuntimeException(e); 290 } 291 add(comp); 292 validate(); 293 } 294 dtc.dropComplete(true); 295 } 296 } 297 298 private static void runProcess() throws Exception { 299 String javaPath = System.getProperty("java.home", ""); 300 String command = javaPath + File.separator + "bin" + File.separator + "java" 301 + " " + RemoveDropTargetCrashTest.class.getName() + " " + RUN_TEST; 302 303 Process process = Runtime.getRuntime().exec(command); 304 boolean processExit = process.waitFor(100, TimeUnit.SECONDS); 305 306 StringBuilder inStream = new StringBuilder(); 307 StringBuilder errStream = new StringBuilder(); 308 checkErrors(process.getErrorStream(), errStream); 309 checkErrors(process.getInputStream(), inStream); 310 311 System.out.println(inStream); 312 System.err.println(errStream); 313 314 if (exception) { 315 throw new RuntimeException("Exception in the output!"); 316 } 317 318 if (!processExit) { 319 process.destroy(); 320 throw new RuntimeException("" 321 + "The sub process has not exited!"); 322 } 323 } 324 325 private static void checkErrors(InputStream in, StringBuilder stream) throws IOException { 326 try (BufferedReader bufferedReader 327 = new BufferedReader(new InputStreamReader(in))) { 328 329 String line = null; 330 while ((line = bufferedReader.readLine()) != null) { 331 if (!exception) { 332 exception = line.contains("Exception") || line.contains("Error"); 333 } 334 stream.append(line).append("\n"); 335 } 336 } 337 } 338 } 339