1 /*
   2  * Copyright (c) 2003, 2014, 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.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.HashMap;
  31 import java.util.HashSet;
  32 import java.util.Iterator;
  33 import java.util.List;
  34 import sun.util.logging.PlatformLogger;
  35 
  36 import java.awt.Point;
  37 
  38 
  39 /**
  40  * The class responsible for registration/deregistration of drop sites.
  41  *
  42  * @since 1.5
  43  */
  44 final class XDropTargetRegistry {
  45     private static final PlatformLogger logger =
  46         PlatformLogger.getLogger("sun.awt.X11.xembed.xdnd.XDropTargetRegistry");
  47 
  48     private static final long DELAYED_REGISTRATION_PERIOD = 200;
  49 
  50     private static final XDropTargetRegistry theInstance =
  51         new XDropTargetRegistry();
  52 
  53     private final HashMap<Long, Runnable> delayedRegistrationMap =
  54         new HashMap<Long, Runnable>();
  55 
  56     private XDropTargetRegistry() {}
  57 
  58     static XDropTargetRegistry getRegistry() {
  59         return theInstance;
  60     }
  61 
  62     /**
  63      * Returns the XID of the topmost window with WM_STATE set in the ancestor
  64      * hierarchy of the specified window or 0 if none found.
  65      */
  66     private long getToplevelWindow(long window) {
  67         XBaseWindow candWindow = XToolkit.windowToXWindow(window);
  68         if (candWindow != null) {
  69             XWindowPeer toplevel = candWindow.getToplevelXWindow();
  70             if (toplevel != null && !(toplevel instanceof XEmbeddedFramePeer)) {
  71                 return toplevel.getWindow();
  72             }
  73         }
  74 
  75         /* Traverse the ancestor tree from window up to the root and find
  76            the top-level client window nearest to the root. */
  77         do {
  78             if (XlibUtil.isTrueToplevelWindow(window)) {
  79                 return window;
  80             }
  81 
  82             window = XlibUtil.getParentWindow(window);
  83 
  84         } while (window != 0);
  85 
  86         return window;
  87     }
  88 
  89     static final long getDnDProxyWindow() {
  90         return XWindow.getXAWTRootWindow().getWindow();
  91     }
  92 
  93     private static final class EmbeddedDropSiteEntry {
  94         private final long root;
  95         private final long event_mask;
  96         private List<XDropTargetProtocol> supportedProtocols;
  97         private final HashSet<Long> nonXEmbedClientSites = new HashSet<Long>();
  98         private final List<Long> sites = new ArrayList<Long>();
  99 
 100         public EmbeddedDropSiteEntry(long root, long event_mask,
 101                                      List<XDropTargetProtocol> supportedProtocols) {
 102             if (supportedProtocols == null) {
 103                 throw new NullPointerException("Null supportedProtocols");
 104             }
 105             this.root = root;
 106             this.event_mask = event_mask;
 107             this.supportedProtocols = supportedProtocols;
 108         }
 109 
 110         public long getRoot() {
 111             return root;
 112         }
 113         public long getEventMask() {
 114             return event_mask;
 115         }
 116         public boolean hasNonXEmbedClientSites() {
 117             return !nonXEmbedClientSites.isEmpty();
 118         }
 119         public synchronized void addSite(long window, boolean isXEmbedClient) {
 120             Long lWindow = Long.valueOf(window);
 121             if (!sites.contains(lWindow)) {
 122                 sites.add(lWindow);
 123             }
 124             if (!isXEmbedClient) {
 125                 nonXEmbedClientSites.add(lWindow);
 126             }
 127         }
 128         public synchronized void removeSite(long window) {
 129             Long lWindow = Long.valueOf(window);
 130             sites.remove(lWindow);
 131             nonXEmbedClientSites.remove(lWindow);
 132         }
 133         public void setSupportedProtocols(List<XDropTargetProtocol> list) {
 134             supportedProtocols = list;
 135         }
 136         public List<XDropTargetProtocol> getSupportedProtocols() {
 137             return supportedProtocols;
 138         }
 139         public boolean hasSites() {
 140             return !sites.isEmpty();
 141         }
 142         public long[] getSites() {
 143             long[] ret = new long[sites.size()];
 144             Iterator<Long> iter = sites.iterator();
 145             int index = 0;
 146             while (iter.hasNext()) {
 147                 Long l = iter.next();
 148                 ret[index++] = l.longValue();
 149             }
 150             return ret;
 151         }
 152         public long getSite(int x, int y) {
 153             assert XToolkit.isAWTLockHeldByCurrentThread();
 154 
 155             Iterator<Long> iter = sites.iterator();
 156             while (iter.hasNext()) {
 157                 Long l = iter.next();
 158                 long window = l.longValue();
 159 
 160                 Point p = XBaseWindow.toOtherWindow(getRoot(), window, x, y);
 161 
 162                 if (p == null) {
 163                     continue;
 164                 }
 165 
 166                 int dest_x = p.x;
 167                 int dest_y = p.y;
 168                 if (dest_x >= 0 && dest_y >= 0) {
 169                     XWindowAttributes wattr = new XWindowAttributes();
 170                     try {
 171                         XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 172                         int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 173                                                                       window, wattr.pData);
 174                         XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 175 
 176                         if ((status == 0) ||
 177                             ((XErrorHandlerUtil.saved_error != null) &&
 178                             (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
 179                             continue;
 180                         }
 181 
 182                         if (wattr.get_map_state() != XConstants.IsUnmapped
 183                             && dest_x < wattr.get_width()
 184                             && dest_y < wattr.get_height()) {
 185                             return window;
 186                         }
 187                     } finally {
 188                         wattr.dispose();
 189                     }
 190                 }
 191             }
 192             return 0;
 193         }
 194     }
 195 
 196     private final HashMap<Long, EmbeddedDropSiteEntry> embeddedDropSiteRegistry =
 197         new HashMap<Long, EmbeddedDropSiteEntry>();
 198 
 199     private EmbeddedDropSiteEntry registerEmbedderDropSite(long embedder) {
 200         assert XToolkit.isAWTLockHeldByCurrentThread();
 201 
 202         Iterator<XDropTargetProtocol> dropTargetProtocols =
 203             XDragAndDropProtocols.getDropTargetProtocols();
 204         // The list of protocols supported by the embedder.
 205         List<XDropTargetProtocol> embedderProtocols = new ArrayList<>();
 206 
 207         while (dropTargetProtocols.hasNext()) {
 208             XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 209             if (dropTargetProtocol.isProtocolSupported(embedder)) {
 210                 embedderProtocols.add(dropTargetProtocol);
 211             }
 212         }
 213 
 214         embedderProtocols = Collections.unmodifiableList(embedderProtocols);
 215 
 216         /* Grab server, since we are working with the window that belongs to
 217            another client. */
 218         XlibWrapper.XGrabServer(XToolkit.getDisplay());
 219         try {
 220             long root = 0;
 221             long event_mask = 0;
 222             XWindowAttributes wattr = new XWindowAttributes();
 223             try {
 224                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 225                 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
 226                                                               embedder, wattr.pData);
 227                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 228 
 229                 if ((status == 0) ||
 230                     ((XErrorHandlerUtil.saved_error != null) &&
 231                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success))) {
 232                     throw new XException("XGetWindowAttributes failed");
 233                 }
 234 
 235                 event_mask = wattr.get_your_event_mask();
 236                 root = wattr.get_root();
 237             } finally {
 238                 wattr.dispose();
 239             }
 240 
 241             if ((event_mask & XConstants.PropertyChangeMask) == 0) {
 242                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 243                 XlibWrapper.XSelectInput(XToolkit.getDisplay(), embedder,
 244                                          event_mask | XConstants.PropertyChangeMask);
 245                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 246 
 247                 if ((XErrorHandlerUtil.saved_error != null) &&
 248                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 249                     throw new XException("XSelectInput failed");
 250                 }
 251             }
 252 
 253             return new EmbeddedDropSiteEntry(root, event_mask, embedderProtocols);
 254         } finally {
 255             XlibWrapper.XUngrabServer(XToolkit.getDisplay());
 256         }
 257     }
 258 
 259     private static final boolean XEMBED_PROTOCOLS = true;
 260     private static final boolean NON_XEMBED_PROTOCOLS = false;
 261 
 262     private void registerProtocols(long embedder, boolean protocols,
 263                                    List<XDropTargetProtocol> supportedProtocols) {
 264         Iterator<XDropTargetProtocol> dropTargetProtocols = null;
 265 
 266         /*
 267          * By default, we register a drop site that supports all dnd
 268          * protocols. This approach is not appropriate in plugin
 269          * scenario if the browser supports Motif DnD and doesn't support
 270          * XDnD. If we forcibly set XdndAware on the browser toplevel, any drag
 271          * source that supports both protocols and prefers XDnD will be unable
 272          * to drop anything on the browser.
 273          * The solution for this problem is not to register XDnD drop site
 274          * if the browser supports only Motif DnD.
 275          * In general, if the browser already supports some protocols, we
 276          * register the embedded drop site only for those protocols. Otherwise
 277          * we register the embedded drop site for all protocols.
 278          */
 279         if (!supportedProtocols.isEmpty()) {
 280             dropTargetProtocols = supportedProtocols.iterator();
 281         } else {
 282             dropTargetProtocols =
 283                 XDragAndDropProtocols.getDropTargetProtocols();
 284         }
 285 
 286         /* Grab server, since we are working with the window that belongs to
 287            another client. */
 288         XlibWrapper.XGrabServer(XToolkit.getDisplay());
 289         try {
 290             while (dropTargetProtocols.hasNext()) {
 291                 XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 292                 if ((protocols == XEMBED_PROTOCOLS) ==
 293                     dropTargetProtocol.isXEmbedSupported()) {
 294                     dropTargetProtocol.registerEmbedderDropSite(embedder);
 295                 }
 296             }
 297         } finally {
 298             XlibWrapper.XUngrabServer(XToolkit.getDisplay());
 299         }
 300     }
 301 
 302     public void updateEmbedderDropSite(long embedder) {
 303         XBaseWindow xbaseWindow = XToolkit.windowToXWindow(embedder);
 304         // No need to update our own drop sites.
 305         if (xbaseWindow != null) {
 306             return;
 307         }
 308 
 309         assert XToolkit.isAWTLockHeldByCurrentThread();
 310 
 311         Iterator<XDropTargetProtocol> dropTargetProtocols =
 312             XDragAndDropProtocols.getDropTargetProtocols();
 313         // The list of protocols supported by the embedder.
 314         List<XDropTargetProtocol> embedderProtocols = new ArrayList<>();
 315 
 316         while (dropTargetProtocols.hasNext()) {
 317             XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 318             if (dropTargetProtocol.isProtocolSupported(embedder)) {
 319                 embedderProtocols.add(dropTargetProtocol);
 320             }
 321         }
 322 
 323         embedderProtocols = Collections.unmodifiableList(embedderProtocols);
 324 
 325         Long lToplevel = Long.valueOf(embedder);
 326         boolean isXEmbedServer = false;
 327         synchronized (this) {
 328             EmbeddedDropSiteEntry entry = embeddedDropSiteRegistry.get(lToplevel);
 329             if (entry == null) {
 330                 return;
 331             }
 332             entry.setSupportedProtocols(embedderProtocols);
 333             isXEmbedServer = !entry.hasNonXEmbedClientSites();
 334         }
 335 
 336         /*
 337          * By default, we register a drop site that supports all dnd
 338          * protocols. This approach is not appropriate in plugin
 339          * scenario if the browser supports Motif DnD and doesn't support
 340          * XDnD. If we forcibly set XdndAware on the browser toplevel, any drag
 341          * source that supports both protocols and prefers XDnD will be unable
 342          * to drop anything on the browser.
 343          * The solution for this problem is not to register XDnD drop site
 344          * if the browser supports only Motif DnD.
 345          * In general, if the browser already supports some protocols, we
 346          * register the embedded drop site only for those protocols. Otherwise
 347          * we register the embedded drop site for all protocols.
 348          */
 349         if (!embedderProtocols.isEmpty()) {
 350             dropTargetProtocols = embedderProtocols.iterator();
 351         } else {
 352             dropTargetProtocols =
 353                 XDragAndDropProtocols.getDropTargetProtocols();
 354         }
 355 
 356         /* Grab server, since we are working with the window that belongs to
 357            another client. */
 358         XlibWrapper.XGrabServer(XToolkit.getDisplay());
 359         try {
 360             while (dropTargetProtocols.hasNext()) {
 361                 XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 362                 if (!isXEmbedServer || !dropTargetProtocol.isXEmbedSupported()) {
 363                     dropTargetProtocol.registerEmbedderDropSite(embedder);
 364                 }
 365             }
 366         } finally {
 367             XlibWrapper.XUngrabServer(XToolkit.getDisplay());
 368         }
 369     }
 370 
 371     private void unregisterEmbedderDropSite(long embedder,
 372                                             EmbeddedDropSiteEntry entry) {
 373         assert XToolkit.isAWTLockHeldByCurrentThread();
 374 
 375         Iterator<XDropTargetProtocol> dropTargetProtocols =
 376             XDragAndDropProtocols.getDropTargetProtocols();
 377 
 378         /* Grab server, since we are working with the window that belongs to
 379            another client. */
 380         XlibWrapper.XGrabServer(XToolkit.getDisplay());
 381         try {
 382             while (dropTargetProtocols.hasNext()) {
 383                 XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 384                 dropTargetProtocol.unregisterEmbedderDropSite(embedder);
 385             }
 386 
 387             long event_mask = entry.getEventMask();
 388 
 389             /* Restore the original event mask for the embedder. */
 390             if ((event_mask & XConstants.PropertyChangeMask) == 0) {
 391                 XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.IgnoreBadWindowHandler.getInstance());
 392                 XlibWrapper.XSelectInput(XToolkit.getDisplay(), embedder,
 393                                          event_mask);
 394                 XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
 395 
 396                 if ((XErrorHandlerUtil.saved_error != null) &&
 397                     (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
 398                     throw new XException("XSelectInput failed");
 399                 }
 400             }
 401         } finally {
 402             XlibWrapper.XUngrabServer(XToolkit.getDisplay());
 403         }
 404     }
 405 
 406     private void registerEmbeddedDropSite(long toplevel, long window) {
 407         XBaseWindow xBaseWindow = XToolkit.windowToXWindow(window);
 408         boolean isXEmbedClient =
 409             (xBaseWindow instanceof XEmbeddedFramePeer) &&
 410             ((XEmbeddedFramePeer)xBaseWindow).isXEmbedActive();
 411 
 412         XEmbedCanvasPeer peer = null;
 413         {
 414             XBaseWindow xbaseWindow = XToolkit.windowToXWindow(toplevel);
 415             if (xbaseWindow != null) {
 416                 if (xbaseWindow instanceof XEmbedCanvasPeer) {
 417                     peer = (XEmbedCanvasPeer)xbaseWindow;
 418                 } else {
 419                     throw new UnsupportedOperationException();
 420                 }
 421             }
 422         }
 423 
 424         Long lToplevel = Long.valueOf(toplevel);
 425         EmbeddedDropSiteEntry entry = null;
 426         synchronized (this) {
 427             entry = embeddedDropSiteRegistry.get(lToplevel);
 428             if (entry == null) {
 429                 if (peer != null) {
 430                     // Toplevel is an XEmbed server within this VM.
 431                     // Register an XEmbed drop site.
 432                     peer.setXEmbedDropTarget();
 433                     // Create a dummy entry to register the embedded site.
 434                     entry = new EmbeddedDropSiteEntry(0, 0,
 435                                                       Collections.<XDropTargetProtocol>emptyList());
 436                 } else {
 437                     // Foreign toplevel.
 438                     // Select for PropertyNotify events on the toplevel, so that
 439                     // we can track changes of the properties relevant to DnD
 440                     // protocols.
 441                     entry = registerEmbedderDropSite(toplevel);
 442                     // Register the toplevel with all DnD protocols that are not
 443                     // supported by XEmbed - actually setup a proxy, so that
 444                     // all DnD notifications sent to the toplevel are first
 445                     // routed to us.
 446                     registerProtocols(toplevel, NON_XEMBED_PROTOCOLS,
 447                                       entry.getSupportedProtocols());
 448                 }
 449                 embeddedDropSiteRegistry.put(lToplevel, entry);
 450             }
 451         }
 452 
 453         assert entry != null;
 454 
 455         synchronized (entry) {
 456             // For a foreign toplevel.
 457             if (peer == null) {
 458                 if (!isXEmbedClient) {
 459                     // Since this is not an XEmbed client we can no longer rely
 460                     // on XEmbed to route DnD notifications even for DnD
 461                     // protocols that are supported by XEmbed.
 462                     // We rollback to the XEmbed-unfriendly solution - setup
 463                     // a proxy, so that all DnD notifications sent to the
 464                     // toplevel are first routed to us.
 465                     registerProtocols(toplevel, XEMBED_PROTOCOLS,
 466                                       entry.getSupportedProtocols());
 467                 } else {
 468                     Iterator<XDropTargetProtocol> dropTargetProtocols =
 469                         XDragAndDropProtocols.getDropTargetProtocols();
 470 
 471                     // Register the embedded window as a plain drop site with
 472                     // all DnD protocols that are supported by XEmbed.
 473                     while (dropTargetProtocols.hasNext()) {
 474                         XDropTargetProtocol dropTargetProtocol =
 475                             dropTargetProtocols.next();
 476                         if (dropTargetProtocol.isXEmbedSupported()) {
 477                             dropTargetProtocol.registerEmbedderDropSite(window);
 478                         }
 479                     }
 480                 }
 481             }
 482 
 483             entry.addSite(window, isXEmbedClient);
 484         }
 485     }
 486 
 487     private void unregisterEmbeddedDropSite(long toplevel, long window) {
 488         Long lToplevel = Long.valueOf(toplevel);
 489         EmbeddedDropSiteEntry entry = null;
 490         synchronized (this) {
 491             entry = embeddedDropSiteRegistry.get(lToplevel);
 492             if (entry == null) {
 493                 return;
 494             }
 495             entry.removeSite(window);
 496             if (!entry.hasSites()) {
 497                 embeddedDropSiteRegistry.remove(lToplevel);
 498 
 499                 XBaseWindow xbaseWindow = XToolkit.windowToXWindow(toplevel);
 500                 if (xbaseWindow != null) {
 501                     if (xbaseWindow instanceof XEmbedCanvasPeer) {
 502                         XEmbedCanvasPeer peer = (XEmbedCanvasPeer)xbaseWindow;
 503                         // Unregister an XEmbed drop site.
 504                         peer.removeXEmbedDropTarget();
 505                     } else {
 506                         throw new UnsupportedOperationException();
 507                     }
 508                 } else {
 509                     unregisterEmbedderDropSite(toplevel, entry);
 510                 }
 511             }
 512         }
 513     }
 514 
 515     /*
 516      * Returns a drop site that is embedded in the specified embedder window and
 517      * contains the point with the specified root coordinates.
 518      */
 519     public long getEmbeddedDropSite(long embedder, int x, int y) {
 520         Long lToplevel = Long.valueOf(embedder);
 521         EmbeddedDropSiteEntry entry = embeddedDropSiteRegistry.get(lToplevel);
 522         if (entry == null) {
 523             return 0;
 524         }
 525         return entry.getSite(x, y);
 526     }
 527 
 528     /*
 529      * Note: this method should be called under AWT lock.
 530      */
 531     public void registerDropSite(long window) {
 532         assert XToolkit.isAWTLockHeldByCurrentThread();
 533 
 534         if (window == 0) {
 535             throw new IllegalArgumentException();
 536         }
 537 
 538         XDropTargetEventProcessor.activate();
 539 
 540         long toplevel = getToplevelWindow(window);
 541 
 542         /*
 543          * No window with WM_STATE property is found.
 544          * Since the window can be a plugin window reparented to the browser
 545          * toplevel, we cannot determine which window will eventually have
 546          * WM_STATE property set. So we schedule a timer callback that will
 547          * periodically attempt to find an ancestor with WM_STATE and
 548          * register the drop site appropriately.
 549          */
 550         if (toplevel == 0) {
 551             addDelayedRegistrationEntry(window);
 552             return;
 553         }
 554 
 555         if (toplevel == window) {
 556             Iterator<XDropTargetProtocol> dropTargetProtocols =
 557                 XDragAndDropProtocols.getDropTargetProtocols();
 558 
 559             while (dropTargetProtocols.hasNext()) {
 560                 XDropTargetProtocol dropTargetProtocol =
 561                     dropTargetProtocols.next();
 562                 dropTargetProtocol.registerDropTarget(toplevel);
 563             }
 564         } else {
 565             registerEmbeddedDropSite(toplevel, window);
 566         }
 567     }
 568 
 569     /*
 570      * Note: this method should be called under AWT lock.
 571      */
 572     public void unregisterDropSite(long window) {
 573         assert XToolkit.isAWTLockHeldByCurrentThread();
 574 
 575         if (window == 0) {
 576             throw new IllegalArgumentException();
 577         }
 578 
 579         long toplevel = getToplevelWindow(window);
 580 
 581         if (toplevel == window) {
 582             Iterator<XDropTargetProtocol> dropProtocols =
 583                 XDragAndDropProtocols.getDropTargetProtocols();
 584 
 585             removeDelayedRegistrationEntry(window);
 586 
 587             while (dropProtocols.hasNext()) {
 588                 XDropTargetProtocol dropProtocol = dropProtocols.next();
 589                 dropProtocol.unregisterDropTarget(window);
 590             }
 591         } else {
 592             unregisterEmbeddedDropSite(toplevel, window);
 593         }
 594     }
 595 
 596     public void registerXEmbedClient(long canvasWindow, long clientWindow) {
 597         // If the client has an associated XDnD drop site, add a drop target
 598         // to the XEmbedCanvasPeer's target to route drag notifications to the
 599         // client.
 600 
 601         XDragSourceProtocol xdndDragProtocol =
 602             XDragAndDropProtocols.getDragSourceProtocol(XDragAndDropProtocols.XDnD);
 603         XDragSourceProtocol.TargetWindowInfo info =
 604             xdndDragProtocol.getTargetWindowInfo(clientWindow);
 605         if (info != null &&
 606             info.getProtocolVersion() >= XDnDConstants.XDND_MIN_PROTOCOL_VERSION) {
 607 
 608             if (logger.isLoggable(PlatformLogger.Level.FINE)) {
 609                 logger.fine("        XEmbed drop site will be registered for " + Long.toHexString(clientWindow));
 610             }
 611             registerEmbeddedDropSite(canvasWindow, clientWindow);
 612 
 613             Iterator<XDropTargetProtocol> dropTargetProtocols =
 614                 XDragAndDropProtocols.getDropTargetProtocols();
 615 
 616             while (dropTargetProtocols.hasNext()) {
 617                 XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 618                 dropTargetProtocol.registerEmbeddedDropSite(clientWindow);
 619             }
 620 
 621             if (logger.isLoggable(PlatformLogger.Level.FINE)) {
 622                 logger.fine("        XEmbed drop site has been registered for " + Long.toHexString(clientWindow));
 623             }
 624         }
 625     }
 626 
 627     public void unregisterXEmbedClient(long canvasWindow, long clientWindow) {
 628         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
 629             logger.fine("        XEmbed drop site will be unregistered for " + Long.toHexString(clientWindow));
 630         }
 631         Iterator<XDropTargetProtocol> dropTargetProtocols =
 632             XDragAndDropProtocols.getDropTargetProtocols();
 633 
 634         while (dropTargetProtocols.hasNext()) {
 635             XDropTargetProtocol dropTargetProtocol = dropTargetProtocols.next();
 636             dropTargetProtocol.unregisterEmbeddedDropSite(clientWindow);
 637         }
 638 
 639         unregisterEmbeddedDropSite(canvasWindow, clientWindow);
 640 
 641         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
 642             logger.fine("        XEmbed drop site has beed unregistered for " + Long.toHexString(clientWindow));
 643         }
 644     }
 645 
 646     /**************** Delayed drop site registration *******************************/
 647 
 648     private void addDelayedRegistrationEntry(final long window) {
 649         Long lWindow = Long.valueOf(window);
 650         Runnable runnable = new Runnable() {
 651                 public void run() {
 652                     removeDelayedRegistrationEntry(window);
 653                     registerDropSite(window);
 654                 }
 655             };
 656 
 657         XToolkit.awtLock();
 658         try {
 659             removeDelayedRegistrationEntry(window);
 660             delayedRegistrationMap.put(lWindow, runnable);
 661             XToolkit.schedule(runnable, DELAYED_REGISTRATION_PERIOD);
 662         } finally {
 663             XToolkit.awtUnlock();
 664         }
 665     }
 666 
 667     private void removeDelayedRegistrationEntry(long window) {
 668         Long lWindow = Long.valueOf(window);
 669 
 670         XToolkit.awtLock();
 671         try {
 672             Runnable runnable = delayedRegistrationMap.remove(lWindow);
 673             if (runnable != null) {
 674                 XToolkit.remove(runnable);
 675             }
 676         } finally {
 677             XToolkit.awtUnlock();
 678         }
 679     }
 680     /*******************************************************************************/
 681 }