1 /* 2 * Copyright (c) 2003, 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt.X11; 27 28 import java.io.IOException; 29 30 import java.util.HashMap; 31 32 import sun.util.logging.PlatformLogger; 33 34 /** 35 * An abstract class for drop protocols on X11 systems. 36 * Contains protocol-independent drop target code. 37 * 38 * @since 1.5 39 */ 40 abstract class XDropTargetProtocol { 41 private static final PlatformLogger logger = 42 PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDropTargetProtocol"); 43 44 private final XDropTargetProtocolListener listener; 45 46 public static final int EMBEDDER_ALREADY_REGISTERED = 0; 47 48 public static final int UNKNOWN_MESSAGE = 0; 49 public static final int ENTER_MESSAGE = 1; 50 public static final int MOTION_MESSAGE = 2; 51 public static final int LEAVE_MESSAGE = 3; 52 public static final int DROP_MESSAGE = 4; 53 XDropTargetProtocol(XDropTargetProtocolListener listener)54 protected XDropTargetProtocol(XDropTargetProtocolListener listener) { 55 if (listener == null) { 56 throw new NullPointerException("Null XDropTargetProtocolListener"); 57 } 58 this.listener = listener; 59 } 60 getProtocolListener()61 protected final XDropTargetProtocolListener getProtocolListener() { 62 return listener; 63 } 64 65 /** 66 * Returns the protocol name. The protocol name cannot be null. 67 */ getProtocolName()68 public abstract String getProtocolName(); 69 70 /* The caller must hold AWT_LOCK. */ registerDropTarget(long window)71 public abstract void registerDropTarget(long window); 72 73 /* The caller must hold AWT_LOCK. */ unregisterDropTarget(long window)74 public abstract void unregisterDropTarget(long window); 75 76 /* The caller must hold AWT_LOCK. */ registerEmbedderDropSite(long window)77 public abstract void registerEmbedderDropSite(long window); 78 79 /* The caller must hold AWT_LOCK. */ unregisterEmbedderDropSite(long window)80 public abstract void unregisterEmbedderDropSite(long window); 81 82 /* The caller must hold AWT_LOCK. */ registerEmbeddedDropSite(long embedded)83 public abstract void registerEmbeddedDropSite(long embedded); 84 85 /* The caller must hold AWT_LOCK. */ unregisterEmbeddedDropSite(long embedded)86 public final void unregisterEmbeddedDropSite(long embedded) { 87 removeEmbedderRegistryEntry(embedded); 88 } 89 90 91 /* The caller must hold AWT_LOCK. */ isProtocolSupported(long window)92 public abstract boolean isProtocolSupported(long window); 93 getMessageType(XClientMessageEvent xclient)94 public abstract int getMessageType(XClientMessageEvent xclient); 95 96 /* The caller must hold AWT_LOCK. */ processClientMessage(XClientMessageEvent xclient)97 public final boolean processClientMessage(XClientMessageEvent xclient) { 98 int type = getMessageType(xclient); 99 boolean processed = processClientMessageImpl(xclient); 100 101 postProcessClientMessage(xclient, processed, type); 102 103 return processed; 104 } 105 106 /* The caller must hold AWT_LOCK. */ processClientMessageImpl(XClientMessageEvent xclient)107 protected abstract boolean processClientMessageImpl(XClientMessageEvent xclient); 108 109 /* 110 * Forwards a drag notification to the embedding toplevel modifying the event 111 * to match the protocol version supported by the toplevel. 112 * The caller must hold AWT_LOCK. 113 * Returns True if the event is sent, False otherwise. 114 */ forwardClientMessageToToplevel(long toplevel, XClientMessageEvent xclient)115 protected final boolean forwardClientMessageToToplevel(long toplevel, 116 XClientMessageEvent xclient) { 117 EmbedderRegistryEntry entry = getEmbedderRegistryEntry(toplevel); 118 119 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 120 logger.finest(" entry={0}", entry); 121 } 122 // Window not registered as an embedder for this protocol. 123 if (entry == null) { 124 return false; 125 } 126 127 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 128 logger.finest(" entry.isOverriden()={0}", entry.isOverriden()); 129 } 130 // Window didn't have an associated drop site, so there is no need 131 // to forward the message. 132 if (!entry.isOverriden()) { 133 return false; 134 } 135 136 adjustEventForForwarding(xclient, entry); 137 138 long proxy = entry.getProxy(); 139 140 if (logger.isLoggable(PlatformLogger.Level.FINEST)) { 141 logger.finest(" proxy={0} toplevel={1}", proxy, toplevel); 142 } 143 if (proxy == 0) { 144 proxy = toplevel; 145 } 146 147 xclient.set_window(toplevel); 148 149 XToolkit.awtLock(); 150 try { 151 XlibWrapper.XSendEvent(XToolkit.getDisplay(), proxy, false, 152 XConstants.NoEventMask, xclient.pData); 153 } finally { 154 XToolkit.awtUnlock(); 155 } 156 157 return true; 158 } 159 160 161 /* True iff the previous notification was MotionEvent and it was 162 forwarded to the browser. */ 163 private boolean motionPassedAlong = false; 164 sendEnterMessageToToplevel(long toplevel, XClientMessageEvent xclient)165 protected abstract void sendEnterMessageToToplevel(long toplevel, 166 XClientMessageEvent xclient); 167 sendLeaveMessageToToplevel(long toplevel, XClientMessageEvent xclient)168 protected abstract void sendLeaveMessageToToplevel(long toplevel, 169 XClientMessageEvent xclient); 170 postProcessClientMessage(XClientMessageEvent xclient, boolean processed, int type)171 private void postProcessClientMessage(XClientMessageEvent xclient, 172 boolean processed, 173 int type) { 174 long toplevel = xclient.get_window(); 175 176 if (getEmbedderRegistryEntry(toplevel) != null) { 177 /* 178 * This code forwards drag notifications to the browser according to the 179 * following rules: 180 * - the messages that we failed to process are always forwarded to the 181 * browser; 182 * - MotionEvents and DropEvents are forwarded if and only if the drag 183 * is not over a plugin window; 184 * - XDnD: EnterEvents and LeaveEvents are never forwarded, instead, we 185 * send synthesized EnterEvents or LeaveEvents when the drag 186 * respectively exits or enters plugin windows; 187 * - Motif DnD: EnterEvents and LeaveEvents are always forwarded. 188 * Synthetic EnterEvents and LeaveEvents are needed, because the XDnD drop 189 * site implemented Netscape 6.2 has a nice feature: when it receives 190 * the first XdndPosition it continuously sends XdndStatus messages to 191 * the source (every 100ms) until the drag terminates or leaves the drop 192 * site. When the mouse is dragged over plugin window embedded in the 193 * browser frame, these XdndStatus messages are mixed with the XdndStatus 194 * messages sent from the plugin. 195 * For Motif DnD, synthetic events cause Motif warnings being displayed, 196 * so these events are always forwarded. However, Motif DnD drop site in 197 * Netscape 6.2 is implemented in the same way, so there could be similar 198 * problems if the drag source choose Motif DnD for communication. 199 */ 200 if (!processed) { 201 forwardClientMessageToToplevel(toplevel, xclient); 202 } else { 203 boolean motifProtocol = 204 xclient.get_message_type() == 205 MotifDnDConstants.XA_MOTIF_DRAG_AND_DROP_MESSAGE.getAtom(); 206 207 switch (type) { 208 case XDropTargetProtocol.MOTION_MESSAGE: 209 if (!isDragOverComponent()) { 210 if (!motionPassedAlong && !motifProtocol) { 211 sendEnterMessageToToplevel(toplevel, xclient); 212 } 213 forwardClientMessageToToplevel(toplevel, xclient); 214 motionPassedAlong = true; 215 } else { 216 if (motionPassedAlong && !motifProtocol) { 217 sendLeaveMessageToToplevel(toplevel, xclient); 218 } 219 motionPassedAlong = false; 220 } 221 break; 222 case XDropTargetProtocol.DROP_MESSAGE: 223 if (!isDragOverComponent()) { 224 forwardClientMessageToToplevel(toplevel, xclient); 225 } 226 motionPassedAlong = false; 227 break; 228 case XDropTargetProtocol.ENTER_MESSAGE: 229 case XDropTargetProtocol.LEAVE_MESSAGE: 230 if (motifProtocol) { 231 forwardClientMessageToToplevel(toplevel, xclient); 232 } 233 motionPassedAlong = false; 234 break; 235 } 236 } 237 } 238 } 239 sendResponse(long ctxt, int eventID, int action)240 public abstract boolean sendResponse(long ctxt, int eventID, int action); 241 242 /* 243 * Retrieves the data from the drag source in the specified format. 244 * 245 * @param ctxt a pointer to the XClientMessageEvent structure for this 246 * protocol's drop message. 247 * @param format the format in which the data should be retrieved. 248 * 249 * @throws IllegalArgumentException if ctxt doesn't point to the 250 * XClientMessageEvent structure for this protocol's drop message. 251 * @throws IOException if data retrieval failed. 252 */ getData(long ctxt, long format)253 public abstract Object getData(long ctxt, long format) 254 throws IllegalArgumentException, IOException; 255 sendDropDone(long ctxt, boolean success, int dropAction)256 public abstract boolean sendDropDone(long ctxt, boolean success, 257 int dropAction); 258 getSourceWindow()259 public abstract long getSourceWindow(); 260 cleanup()261 public abstract void cleanup(); 262 isDragOverComponent()263 public abstract boolean isDragOverComponent(); 264 adjustEventForForwarding(XClientMessageEvent xclient, EmbedderRegistryEntry entry)265 public void adjustEventForForwarding(XClientMessageEvent xclient, 266 EmbedderRegistryEntry entry) {} 267 forwardEventToEmbedded(long embedded, long ctxt, int eventID)268 public abstract boolean forwardEventToEmbedded(long embedded, long ctxt, 269 int eventID); 270 271 /* 272 * Returns true if the XEmbed protocol prescribes that an XEmbed server must 273 * support this DnD protocol for drop sites associated with XEmbed clients. 274 */ isXEmbedSupported()275 public abstract boolean isXEmbedSupported(); 276 277 protected static final class EmbedderRegistryEntry { 278 private final boolean overriden; 279 private final int version; 280 private final long proxy; EmbedderRegistryEntry(boolean overriden, int version, long proxy)281 EmbedderRegistryEntry(boolean overriden, int version, long proxy) { 282 this.overriden = overriden; 283 this.version = version; 284 this.proxy = proxy; 285 } isOverriden()286 public boolean isOverriden() { 287 return overriden; 288 } getVersion()289 public int getVersion() { 290 return version; 291 } getProxy()292 public long getProxy() { 293 return proxy; 294 } 295 } 296 297 /* Access to HashMap is synchronized on this XDropTargetProtocol instance. */ 298 private final HashMap<Long, EmbedderRegistryEntry> embedderRegistry = 299 new HashMap<>(); 300 putEmbedderRegistryEntry(long embedder, boolean overriden, int version, long proxy)301 protected final void putEmbedderRegistryEntry(long embedder, 302 boolean overriden, 303 int version, 304 long proxy) { 305 synchronized (this) { 306 embedderRegistry.put(Long.valueOf(embedder), 307 new EmbedderRegistryEntry(overriden, version, 308 proxy)); 309 } 310 } 311 getEmbedderRegistryEntry(long embedder)312 protected final EmbedderRegistryEntry getEmbedderRegistryEntry(long embedder) { 313 synchronized (this) { 314 return embedderRegistry.get(Long.valueOf(embedder)); 315 } 316 } 317 removeEmbedderRegistryEntry(long embedder)318 protected final void removeEmbedderRegistryEntry(long embedder) { 319 synchronized (this) { 320 embedderRegistry.remove(Long.valueOf(embedder)); 321 } 322 } 323 } 324