--- old/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDropTargetContextPeer.java 2015-10-08 10:44:06.000000000 +0400 +++ new/src/java.desktop/macosx/classes/sun/lwawt/macosx/CDropTargetContextPeer.java 2015-10-08 10:44:06.000000000 +0400 @@ -166,6 +166,14 @@ eventID, SunDropTargetContextPeer.DISPATCH_SYNC); } + @Override + protected void handleDropMessage(Component component, int x, int y, + int dropAction, int actions, long[] formats, + long nativeCtxt) { + super.handleDropMessage(component, x, y, dropAction, actions, formats, nativeCtxt); + flushEvents(component); + } + // Signal drop complete: protected void doDropDone(boolean success, int dropAction, boolean isLocal) { long nativeDropTarget = this.getNativeDragContext(); --- old/src/java.desktop/macosx/native/libawt_lwawt/awt/CDropTarget.m 2015-10-08 10:44:07.000000000 +0400 +++ new/src/java.desktop/macosx/native/libawt_lwawt/awt/CDropTarget.m 2015-10-08 10:44:06.000000000 +0400 @@ -663,13 +663,6 @@ if (sDraggingError == FALSE) { JNFCallVoidMethod(env, fDropTargetContextPeer, handleDropMessageMethod, fComponent, (jint) javaLocation.x, (jint) javaLocation.y, dropAction, actions, formats, ptr_to_jlong(self)); // AWT_THREADING Safe (event) } - - if (sDraggingError == FALSE) { - JNF_MEMBER_CACHE(flushEventsMethod, jc_CDropTargetContextPeer, "flushEvents", "(Ljava/awt/Component;)V"); - if (sDraggingError == FALSE) { - JNFCallVoidMethod(env, fDropTargetContextPeer, flushEventsMethod, fComponent); // AWT_THREADING Safe (AWTRunLoopMode) - } - } } else { // 8-19-03 Note: [Radar 3368754] // draggingExited: is not called after a drop - we must do that here ... but only in case --- old/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java 2015-10-08 10:44:07.000000000 +0400 +++ new/src/java.desktop/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java 2015-10-08 10:44:07.000000000 +0400 @@ -498,7 +498,7 @@ * upcall to handle the Drop message */ - private void handleDropMessage(final Component component, + protected void handleDropMessage(final Component component, final int x, final int y, final int dropAction, final int actions, final long[] formats, --- /dev/null 2015-10-08 10:44:08.000000000 +0400 +++ new/test/java/awt/dnd/RemoveDropTargetCrashTest/RemoveDropTargetCrashTest.java 2015-10-08 10:44:08.000000000 +0400 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2015, 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.*; +import java.awt.datatransfer.*; +import java.awt.dnd.*; +import java.awt.event.*; +import java.io.*; +import java.util.concurrent.TimeUnit; + +/** + * @test + * @bug 4357905 8136999 + * @summary tests that removal of the drop target during drop processing doesn't + * cause crash + * @run main RemoveDropTargetCrashTest RUN_PROCESS + */ +public class RemoveDropTargetCrashTest { + + private static final int FRAME_ACTIVATION_TIMEOUT = 1000; + private static final String RUN_PROCESS = "RUN_PROCESS"; + private static final String RUN_TEST = "RUN_TEST"; + private static boolean exception = false; + + public static void main(String[] args) throws Exception { + String command = args.length < 1 ? RUN_TEST : args[0]; + + switch (command) { + case RUN_PROCESS: + runProcess(); + break; + case RUN_TEST: + runTest(); + break; + default: + throw new RuntimeException("Unknown command: " + command); + } + } + + private static void runTest() throws Exception { + + Frame frame = null; + try { + DragSourceButton dragSourceButton = new DragSourceButton(); + DropTargetPanel dropTargetPanel = new DropTargetPanel(); + + frame = new Frame(); + frame.setTitle("Test frame"); + frame.setLocation(200, 200); + frame.setLayout(new GridLayout(2, 1)); + frame.add(dragSourceButton); + frame.add(dropTargetPanel); + + frame.pack(); + frame.setVisible(true); + + Thread.sleep(FRAME_ACTIVATION_TIMEOUT); + + Point dragPoint = dragSourceButton.getLocationOnScreen(); + Dimension size = dragSourceButton.getSize(); + dragPoint.translate(size.width / 2, size.height / 2); + + Point dropPoint = dropTargetPanel.getLocationOnScreen(); + size = dropTargetPanel.getSize(); + dropPoint.translate(size.width / 2, size.height / 2); + + Robot robot = new Robot(); + robot.mouseMove(dragPoint.x, dragPoint.y); + robot.keyPress(KeyEvent.VK_CONTROL); + robot.mousePress(InputEvent.BUTTON1_MASK); + for (; !dragPoint.equals(dropPoint); + dragPoint.translate(sign(dropPoint.x - dragPoint.x), + sign(dropPoint.y - dragPoint.y))) { + robot.mouseMove(dragPoint.x, dragPoint.y); + Thread.sleep(10); + } + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.keyRelease(KeyEvent.VK_CONTROL); + } finally { + if (frame != null) { + frame.dispose(); + } + } + } + + public static int sign(int n) { + return n < 0 ? -1 : n == 0 ? 0 : 1; + } + + static class DragSourceButton extends Button implements Serializable, + Transferable, + DragGestureListener, + DragSourceListener { + + private static DataFlavor dataflavor; + + static { + try { + dataflavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType); + dataflavor.setHumanPresentableName("Local Object Flavor"); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public DragSourceButton() { + this("DragSourceButton"); + } + + public DragSourceButton(String str) { + super(str); + + DragSource ds = DragSource.getDefaultDragSource(); + ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, + this); + } + + public void dragGestureRecognized(DragGestureEvent dge) { + dge.startDrag(null, this, this); + } + + public void dragEnter(DragSourceDragEvent dsde) { + } + + public void dragExit(DragSourceEvent dse) { + } + + public void dragOver(DragSourceDragEvent dsde) { + } + + public void dragDropEnd(DragSourceDropEvent dsde) { + } + + public void dropActionChanged(DragSourceDragEvent dsde) { + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + + if (!isDataFlavorSupported(flavor)) { + throw new UnsupportedFlavorException(flavor); + } + + return this; + } + + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[]{dataflavor}; + } + + public boolean isDataFlavorSupported(DataFlavor dflavor) { + return dataflavor.equals(dflavor); + } + } + + static class DropTargetPanel extends Panel implements DropTargetListener { + + final Dimension preferredSize = new Dimension(100, 100); + + public DropTargetPanel() { + setDropTarget(new DropTarget(this, this)); + } + + public Dimension getPreferredSize() { + return preferredSize; + } + + public void dragEnter(DropTargetDragEvent dtde) { + } + + public void dragExit(DropTargetEvent dte) { + } + + public void dragOver(DropTargetDragEvent dtde) { + } + + public void dropActionChanged(DropTargetDragEvent dtde) { + } + + public void drop(DropTargetDropEvent dtde) { + + setDropTarget(null); + + DropTargetContext dtc = dtde.getDropTargetContext(); + + if ((dtde.getSourceActions() & DnDConstants.ACTION_COPY) != 0) { + dtde.acceptDrop(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrop(); + } + + DataFlavor[] dfs = dtde.getCurrentDataFlavors(); + Component comp = null; + + if (dfs != null && dfs.length >= 1) { + Transferable transfer = dtde.getTransferable(); + + try { + comp = (Component) transfer.getTransferData(dfs[0]); + } catch (Throwable e) { + dtc.dropComplete(false); + throw new RuntimeException(e); + } + } + dtc.dropComplete(true); + + add(comp); + validate(); + } + } + + private static void runProcess() throws Exception { + String javaPath = System.getProperty("java.home", ""); + String command = javaPath + File.separator + "bin" + File.separator + "java" + + " " + RemoveDropTargetCrashTest.class.getName() + " " + RUN_TEST; + + Process process = Runtime.getRuntime().exec(command); + boolean processExit = process.waitFor(20, TimeUnit.SECONDS); + + StringBuilder inStream = new StringBuilder(); + StringBuilder errStream = new StringBuilder(); + checkErrors(process.getErrorStream(), errStream); + checkErrors(process.getInputStream(), inStream); + + if (exception) { + System.out.println(inStream); + System.err.println(errStream); + throw new RuntimeException("Exception in the output!"); + } + + if (!processExit) { + process.destroy(); + throw new RuntimeException("" + + "The sub process has not exited!"); + } + } + + private static void checkErrors(InputStream in, StringBuilder stream) throws IOException { + try (BufferedReader bufferedReader + = new BufferedReader(new InputStreamReader(in))) { + + String line = null; + while ((line = bufferedReader.readLine()) != null) { + if (!exception) { + exception = line.contains("Exception") || line.contains("Error"); + } + stream.append(line).append("\n"); + } + } + } +}