Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
6
7 #include <X11/Xatom.h>
8
9 #include "base/event_types.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_pump_dispatcher.h"
13 #include "ui/aura/client/drag_drop_client.h"
14 #include "ui/aura/client/drag_drop_delegate.h"
15 #include "ui/aura/root_window.h"
16 #include "ui/aura/window.h"
17 #include "ui/base/dragdrop/drag_drop_types.h"
18 #include "ui/base/dragdrop/drop_target_event.h"
19 #include "ui/base/dragdrop/os_exchange_data.h"
20 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
21 #include "ui/base/x/selection_utils.h"
22 #include "ui/base/x/x11_util.h"
23 #include "ui/events/event.h"
24 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
25
26 using aura::client::DragDropDelegate;
27 using ui::OSExchangeData;
28
29 namespace {
30
31 const int kMinXdndVersion = 5;
32
33 const int kWillAcceptDrop = 1;
34 const int kWantFurtherPosEvents = 2;
35
36 const char kXdndActionCopy[] = "XdndActionCopy";
37 const char kXdndActionMove[] = "XdndActionMove";
38 const char kXdndActionLink[] = "XdndActionLink";
39
40 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
41 const char kXdndSelection[] = "XdndSelection";
42
43 const char* kAtomsToCache[] = {
44   kChromiumDragReciever,
45   "XdndActionAsk",
46   kXdndActionCopy,
47   kXdndActionLink,
48   "XdndActionList",
49   kXdndActionMove,
50   "XdndActionPrivate",
51   "XdndAware",
52   "XdndDrop",
53   "XdndEnter",
54   "XdndFinished",
55   "XdndLeave",
56   "XdndPosition",
57   "XdndProxy",  // Proxy windows?
58   kXdndSelection,
59   "XdndStatus",
60   "XdndTypeList",
61   NULL
62 };
63
64 static base::LazyInstance<
65     std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
66         g_live_client_map = LAZY_INSTANCE_INITIALIZER;
67
68 // Helper class to FindWindowFor which looks for a drag target under the
69 // cursor.
70 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
71  public:
72   DragTargetWindowFinder(XID ignored_icon_window,
73                          gfx::Point screen_loc)
74       : ignored_icon_window_(ignored_icon_window),
75         output_window_(None),
76         screen_loc_(screen_loc) {
77     ui::EnumerateTopLevelWindows(this);
78   }
79
80   virtual ~DragTargetWindowFinder() {}
81
82   XID window() const { return output_window_; }
83
84  protected:
85   virtual bool ShouldStopIterating(XID window) OVERRIDE {
86     if (window == ignored_icon_window_)
87       return false;
88
89     if (!ui::IsWindowVisible(window))
90       return false;
91
92     if (!ui::WindowContainsPoint(window, screen_loc_))
93       return false;
94
95     if (ui::PropertyExists(window, "WM_STATE")) {
96       output_window_ = window;
97       return true;
98     }
99
100     return false;
101   }
102
103  private:
104   XID ignored_icon_window_;
105   XID output_window_;
106   gfx::Point screen_loc_;
107
108   DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
109 };
110
111 // Returns the topmost X11 window at |screen_point| if it is advertising that
112 // is supports the Xdnd protocol. Will return the window under the pointer as
113 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
114 // |dest_window|.
115 void FindWindowFor(const gfx::Point& screen_point,
116                    ::Window* mouse_window, ::Window* dest_window) {
117   DragTargetWindowFinder finder(None, screen_point);
118   *mouse_window = finder.window();
119   *dest_window = None;
120
121   if (finder.window() == None)
122     return;
123
124   // Figure out which window we should test as XdndAware. If mouse_window has
125   // XdndProxy, it will set that proxy on target, and if not, |target|'s
126   // original value will remain.
127   XID target = *mouse_window;
128   ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
129
130   int version;
131   if (ui::GetIntProperty(target, "XdndAware", &version) &&
132       version >= kMinXdndVersion) {
133     *dest_window = target;
134   }
135 }
136
137 }  // namespace
138
139 namespace views {
140
141 DesktopDragDropClientAuraX11*
142 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
143
144 class DesktopDragDropClientAuraX11::X11DragContext :
145     public base::MessagePumpDispatcher {
146  public:
147   X11DragContext(ui::X11AtomCache* atom_cache,
148                  ::Window local_window,
149                  const XClientMessageEvent& event);
150   virtual ~X11DragContext();
151
152   // When we receive an XdndPosition message, we need to have all the data
153   // copied from the other window before we process the XdndPosition
154   // message. If we have that data already, dispatch immediately. Otherwise,
155   // delay dispatching until we do.
156   void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
157                                   ::Window source_window,
158                                   const gfx::Point& screen_point);
159
160   // Called to request the next target from the source window. This is only
161   // done on the first XdndPosition; after that, we cache the data offered by
162   // the source window.
163   void RequestNextTarget();
164
165   // Called when XSelection data has been copied to our process.
166   void OnSelectionNotify(const XSelectionEvent& xselection);
167
168   // Clones the fetched targets.
169   const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
170
171   // Reads the "XdndActionList" property from |source_window| and copies it
172   // into |actions|.
173   void ReadActions();
174
175   // Creates a ui::DragDropTypes::DragOperation representation of the current
176   // action list.
177   int GetDragOperation() const;
178
179  private:
180   // Overridden from MessagePumpDispatcher:
181   virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE;
182
183   // The atom cache owned by our parent.
184   ui::X11AtomCache* atom_cache_;
185
186   // The XID of our chrome local aura window handling our events.
187   ::Window local_window_;
188
189   // The XID of the window that's initiated the drag.
190   unsigned long source_window_;
191
192   // The client we inform once we're done with requesting data.
193   DesktopDragDropClientAuraX11* drag_drop_client_;
194
195   // Whether we're blocking the handling of an XdndPosition message by waiting
196   // for |unfetched_targets_| to be fetched.
197   bool waiting_to_handle_position_;
198
199   // Where the cursor is on screen.
200   gfx::Point screen_point_;
201
202   // A SelectionFormatMap of data that we have in our process.
203   ui::SelectionFormatMap fetched_targets_;
204
205   // The names of various data types offered by the other window that we
206   // haven't fetched and put in |fetched_targets_| yet.
207   std::vector<Atom> unfetched_targets_;
208
209   // Possible actions.
210   std::vector<Atom> actions_;
211
212   DISALLOW_COPY_AND_ASSIGN(X11DragContext);
213 };
214
215 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
216     ui::X11AtomCache* atom_cache,
217     ::Window local_window,
218     const XClientMessageEvent& event)
219     : atom_cache_(atom_cache),
220       local_window_(local_window),
221       source_window_(event.data.l[0]),
222       drag_drop_client_(NULL),
223       waiting_to_handle_position_(false) {
224   bool get_types = ((event.data.l[1] & 1) != 0);
225
226   if (get_types) {
227     if (!ui::GetAtomArrayProperty(source_window_,
228                                   "XdndTypeList",
229                                   &unfetched_targets_)) {
230       return;
231     }
232   } else {
233     // data.l[2,3,4] contain the first three types. Unused slots can be None.
234     for (int i = 0; i < 3; ++i) {
235       if (event.data.l[2+i] != None) {
236         unfetched_targets_.push_back(event.data.l[2+i]);
237       }
238     }
239   }
240
241   DesktopDragDropClientAuraX11* client =
242       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
243   if (!client) {
244     // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
245     // created by some other process. Listen for messages on it.
246     base::MessagePumpX11::Current()->AddDispatcherForWindow(
247         this, source_window_);
248     XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
249
250     // We must perform a full sync here because we could be racing
251     // |source_window_|.
252     XSync(gfx::GetXDisplay(), False);
253   } else {
254     // This drag originates from an aura window within our process. This means
255     // that we can shortcut the X11 server and ask the owning SelectionOwner
256     // for the data it's offering.
257     fetched_targets_ = client->GetFormatMap();
258     unfetched_targets_.clear();
259   }
260
261   ReadActions();
262 }
263
264 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
265   DesktopDragDropClientAuraX11* client =
266       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
267   if (!client) {
268     // Unsubscribe from message events.
269     base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
270         source_window_);
271   }
272 }
273
274 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
275     DesktopDragDropClientAuraX11* client,
276     ::Window source_window,
277     const gfx::Point& screen_point) {
278   DCHECK_EQ(source_window_, source_window);
279
280   if (!unfetched_targets_.empty()) {
281     // We have unfetched targets. That means we need to pause the handling of
282     // the position message and ask the other window for its data.
283     screen_point_ = screen_point;
284     drag_drop_client_ = client;
285     waiting_to_handle_position_ = true;
286
287     fetched_targets_ = ui::SelectionFormatMap();
288     RequestNextTarget();
289   } else {
290     client->CompleteXdndPosition(source_window, screen_point);
291   }
292 }
293
294 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
295   ::Atom target = unfetched_targets_.back();
296   unfetched_targets_.pop_back();
297
298   XConvertSelection(gfx::GetXDisplay(),
299                     atom_cache_->GetAtom(kXdndSelection),
300                     target,
301                     atom_cache_->GetAtom(kChromiumDragReciever),
302                     local_window_,
303                     CurrentTime);
304 }
305
306 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
307     const XSelectionEvent& event) {
308   DCHECK(waiting_to_handle_position_);
309   DCHECK(drag_drop_client_);
310   DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
311
312   scoped_refptr<base::RefCountedMemory> data;
313   ::Atom type = None;
314   if (ui::GetRawBytesOfProperty(local_window_, event.property,
315                                 &data, NULL, NULL, &type)) {
316     fetched_targets_.Insert(event.target, data);
317   }
318
319   if (!unfetched_targets_.empty()) {
320     RequestNextTarget();
321   } else {
322     waiting_to_handle_position_ = false;
323     drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
324     drag_drop_client_ = NULL;
325   }
326 }
327
328 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
329   DesktopDragDropClientAuraX11* client =
330       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
331   if (!client) {
332     std::vector<Atom> atom_array;
333     if (!ui::GetAtomArrayProperty(source_window_,
334                                   "XdndActionList",
335                                   &atom_array)) {
336       actions_.clear();
337     } else {
338       actions_.swap(atom_array);
339     }
340   } else {
341     // We have a property notify set up for other windows in case they change
342     // their action list. Thankfully, the views interface is static and you
343     // can't change the action list after you enter StartDragAndDrop().
344     actions_ = client->GetOfferedDragOperations();
345   }
346 }
347
348 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
349   int drag_operation = ui::DragDropTypes::DRAG_NONE;
350   for (std::vector<Atom>::const_iterator it = actions_.begin();
351        it != actions_.end(); ++it) {
352     if (*it == atom_cache_->GetAtom(kXdndActionCopy))
353       drag_operation |= ui::DragDropTypes::DRAG_COPY;
354     else if (*it == atom_cache_->GetAtom(kXdndActionMove))
355       drag_operation |= ui::DragDropTypes::DRAG_MOVE;
356     else if (*it == atom_cache_->GetAtom(kXdndActionLink))
357       drag_operation |= ui::DragDropTypes::DRAG_LINK;
358   }
359
360   return drag_operation;
361 }
362
363 uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
364     const base::NativeEvent& event) {
365   if (event->type == PropertyNotify &&
366       event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
367     ReadActions();
368   }
369   return POST_DISPATCH_NONE;
370 }
371
372 ///////////////////////////////////////////////////////////////////////////////
373
374 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
375     aura::Window* root_window,
376     views::DesktopNativeCursorManager* cursor_manager,
377     Display* xdisplay,
378     ::Window xwindow)
379     : move_loop_(this),
380       root_window_(root_window),
381       xdisplay_(xdisplay),
382       xwindow_(xwindow),
383       atom_cache_(xdisplay_, kAtomsToCache),
384       target_window_(NULL),
385       source_provider_(NULL),
386       source_current_window_(None),
387       drag_operation_(0),
388       resulting_operation_(0),
389       grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
390       copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
391       move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)) {
392   DCHECK(g_live_client_map.Get().find(xwindow) ==
393          g_live_client_map.Get().end());
394   g_live_client_map.Get().insert(std::make_pair(xwindow, this));
395
396   // Mark that we are aware of drag and drop concepts.
397   unsigned long xdnd_version = kMinXdndVersion;
398   XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
399                   XA_ATOM, 32, PropModeReplace,
400                   reinterpret_cast<unsigned char*>(&xdnd_version), 1);
401 }
402
403 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
404   g_live_client_map.Get().erase(xwindow_);
405 }
406
407 // static
408 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
409     ::Window window) {
410   std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
411       g_live_client_map.Get().find(window);
412   if (it == g_live_client_map.Get().end())
413     return NULL;
414   return it->second;
415 }
416
417 void DesktopDragDropClientAuraX11::OnXdndEnter(
418     const XClientMessageEvent& event) {
419   DVLOG(1) << "XdndEnter";
420
421   int version = (event.data.l[1] & 0xff000000) >> 24;
422   if (version < 3) {
423     LOG(ERROR) << "Received old XdndEnter message.";
424     return;
425   }
426
427   // Make sure that we've run ~X11DragContext() before creating another one.
428   target_current_context_.reset();
429   target_current_context_.reset(
430       new X11DragContext(&atom_cache_, xwindow_, event));
431
432   // In the Windows implementation, we immediately call DesktopDropTargetWin::
433   // Translate(). Here, we wait until we receive an XdndPosition message
434   // because the enter message doesn't convey any positioning
435   // information.
436 }
437
438 void DesktopDragDropClientAuraX11::OnXdndLeave(
439     const XClientMessageEvent& event) {
440   DVLOG(1) << "XdndLeave";
441   NotifyDragLeave();
442   target_current_context_.reset();
443 }
444
445 void DesktopDragDropClientAuraX11::OnXdndPosition(
446     const XClientMessageEvent& event) {
447   DVLOG(1) << "XdndPosition";
448
449   unsigned long source_window = event.data.l[0];
450   int x_root_window = event.data.l[2] >> 16;
451   int y_root_window = event.data.l[2] & 0xffff;
452
453   if (!target_current_context_.get()) {
454     NOTREACHED();
455     return;
456   }
457
458   // If we already have all the data from this drag, we complete it
459   // immediately.
460   target_current_context_->OnStartXdndPositionMessage(
461       this, source_window, gfx::Point(x_root_window, y_root_window));
462 }
463
464 void DesktopDragDropClientAuraX11::OnXdndStatus(
465     const XClientMessageEvent& event) {
466   DVLOG(1) << "XdndStatus";
467
468   unsigned long source_window = event.data.l[0];
469   int drag_operation = ui::DragDropTypes::DRAG_NONE;
470   if (event.data.l[1] & 1) {
471     ::Atom atom_operation = event.data.l[4];
472     negotiated_operation_[source_window] = atom_operation;
473     drag_operation = AtomToDragOperation(atom_operation);
474   }
475
476   switch (drag_operation) {
477     case ui::DragDropTypes::DRAG_COPY:
478       move_loop_.UpdateCursor(copy_grab_cursor_);
479       break;
480     case ui::DragDropTypes::DRAG_MOVE:
481       move_loop_.UpdateCursor(move_grab_cursor_);
482       break;
483     default:
484       move_loop_.UpdateCursor(grab_cursor_);
485       break;
486   }
487
488   // Note: event.data.[2,3] specify a rectangle. It is a request by the other
489   // window to not send further XdndPosition messages while the cursor is
490   // within it. However, it is considered advisory and (at least according to
491   // the spec) the other side must handle further position messages within
492   // it. GTK+ doesn't bother with this, so neither should we.
493
494   waiting_on_status_.erase(source_window);
495
496   if (ContainsKey(pending_drop_, source_window)) {
497     // We were waiting on the status message so we could send the XdndDrop.
498     SendXdndDrop(source_window);
499     return;
500   }
501
502   NextPositionMap::iterator it = next_position_message_.find(source_window);
503   if (it != next_position_message_.end()) {
504     // We were waiting on the status message so we could send off the next
505     // position message we queued up.
506     gfx::Point p = it->second.first;
507     unsigned long time = it->second.second;
508     next_position_message_.erase(it);
509
510     SendXdndPosition(source_window, p, time);
511   }
512 }
513
514 void DesktopDragDropClientAuraX11::OnXdndFinished(
515     const XClientMessageEvent& event) {
516   DVLOG(1) << "XdndFinished";
517   resulting_operation_ = AtomToDragOperation(
518       negotiated_operation_[event.data.l[0]]);
519   move_loop_.EndMoveLoop();
520 }
521
522 void DesktopDragDropClientAuraX11::OnXdndDrop(
523     const XClientMessageEvent& event) {
524   DVLOG(1) << "XdndDrop";
525
526   unsigned long source_window = event.data.l[0];
527
528   int drag_operation = ui::DragDropTypes::DRAG_NONE;
529   if (target_window_) {
530     aura::client::DragDropDelegate* delegate =
531         aura::client::GetDragDropDelegate(target_window_);
532     if (delegate) {
533       ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
534           xwindow_, target_current_context_->fetched_targets()));
535
536       ui::DropTargetEvent event(data,
537                                 target_window_location_,
538                                 target_window_root_location_,
539                                 target_current_context_->GetDragOperation());
540       drag_operation = delegate->OnPerformDrop(event);
541     }
542
543     target_window_->RemoveObserver(this);
544     target_window_ = NULL;
545   }
546
547   XEvent xev;
548   xev.xclient.type = ClientMessage;
549   xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
550   xev.xclient.format = 32;
551   xev.xclient.window = source_window;
552   xev.xclient.data.l[0] = xwindow_;
553   xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
554   xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
555
556   SendXClientEvent(source_window, &xev);
557 }
558
559 void DesktopDragDropClientAuraX11::OnSelectionNotify(
560     const XSelectionEvent& xselection) {
561   if (!target_current_context_) {
562     NOTIMPLEMENTED();
563     return;
564   }
565
566   target_current_context_->OnSelectionNotify(xselection);
567 }
568
569 int DesktopDragDropClientAuraX11::StartDragAndDrop(
570     const ui::OSExchangeData& data,
571     aura::Window* root_window,
572     aura::Window* source_window,
573     const gfx::Point& root_location,
574     int operation,
575     ui::DragDropTypes::DragEventSource source) {
576   source_current_window_ = None;
577   DCHECK(!g_current_drag_drop_client);
578   g_current_drag_drop_client = this;
579   drag_operation_ = operation;
580   resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
581
582   const ui::OSExchangeData::Provider* provider = &data.provider();
583   source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
584       provider);
585
586   source_provider_->TakeOwnershipOfSelection();
587
588   std::vector< ::Atom> actions = GetOfferedDragOperations();
589   ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
590
591   // Windows has a specific method, DoDragDrop(), which performs the entire
592   // drag. We have to emulate this, so we spin off a nested runloop which will
593   // track all cursor movement and reroute events to a specific handler.
594   move_loop_.SetDragImage(source_provider_->GetDragImage(),
595                           source_provider_->GetDragImageOffset());
596   move_loop_.RunMoveLoop(source_window, grab_cursor_);
597   move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF());
598
599   source_provider_ = NULL;
600   g_current_drag_drop_client = NULL;
601   drag_operation_ = 0;
602   XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
603
604   return resulting_operation_;
605 }
606
607 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
608                                               const ui::LocatedEvent& event) {
609   NOTIMPLEMENTED();
610 }
611
612 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
613                                         const ui::LocatedEvent& event) {
614   NOTIMPLEMENTED();
615 }
616
617 void DesktopDragDropClientAuraX11::DragCancel() {
618   move_loop_.EndMoveLoop();
619 }
620
621 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
622   return !!g_current_drag_drop_client;
623 }
624
625 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
626   DCHECK_EQ(target_window_, window);
627   target_window_ = NULL;
628 }
629
630 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
631   gfx::Point screen_point(event->x_root, event->y_root);
632
633   // Find the current window the cursor is over.
634   ::Window mouse_window = None;
635   ::Window dest_window = None;
636   FindWindowFor(screen_point, &mouse_window, &dest_window);
637
638   if (source_current_window_ != dest_window) {
639     if (source_current_window_ != None)
640       SendXdndLeave(source_current_window_);
641
642     source_current_window_ = dest_window;
643
644     if (source_current_window_ != None) {
645       negotiated_operation_.erase(source_current_window_);
646       SendXdndEnter(source_current_window_);
647     }
648   }
649
650   if (source_current_window_ != None) {
651     if (ContainsKey(waiting_on_status_, dest_window)) {
652       next_position_message_[dest_window] =
653           std::make_pair(screen_point, event->time);
654     } else {
655       SendXdndPosition(dest_window, screen_point, event->time);
656     }
657   }
658 }
659
660 void DesktopDragDropClientAuraX11::OnMouseReleased() {
661   if (source_current_window_ != None) {
662     if (ContainsKey(waiting_on_status_, source_current_window_)) {
663       // If we are waiting for an XdndStatus message, we need to wait for it to
664       // complete.
665       pending_drop_.insert(source_current_window_);
666       return;
667     }
668
669     std::map< ::Window, ::Atom>::iterator it =
670         negotiated_operation_.find(source_current_window_);
671     if (it != negotiated_operation_.end() && it->second != None) {
672       // We have negotiated an action with the other end.
673       SendXdndDrop(source_current_window_);
674       return;
675     }
676
677     SendXdndLeave(source_current_window_);
678     source_current_window_ = None;
679   }
680
681   move_loop_.EndMoveLoop();
682 }
683
684 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
685   target_current_context_.reset();
686 }
687
688 void DesktopDragDropClientAuraX11::DragTranslate(
689     const gfx::Point& root_window_location,
690     scoped_ptr<ui::OSExchangeData>* data,
691     scoped_ptr<ui::DropTargetEvent>* event,
692     aura::client::DragDropDelegate** delegate) {
693   gfx::Point root_location = root_window_location;
694   root_window_->GetDispatcher()->host()->ConvertPointFromNativeScreen(
695       &root_location);
696   aura::Window* target_window =
697       root_window_->GetEventHandlerForPoint(root_location);
698   bool target_window_changed = false;
699   if (target_window != target_window_) {
700     if (target_window_)
701       NotifyDragLeave();
702     target_window_ = target_window;
703     if (target_window_)
704       target_window_->AddObserver(this);
705     target_window_changed = true;
706   }
707   *delegate = NULL;
708   if (!target_window_)
709     return;
710   *delegate = aura::client::GetDragDropDelegate(target_window_);
711   if (!*delegate)
712     return;
713
714   data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
715       xwindow_, target_current_context_->fetched_targets())));
716   gfx::Point location = root_location;
717   aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
718
719   target_window_location_ = location;
720   target_window_root_location_ = root_location;
721
722   event->reset(new ui::DropTargetEvent(
723       *(data->get()),
724       location,
725       root_location,
726       target_current_context_->GetDragOperation()));
727   if (target_window_changed)
728     (*delegate)->OnDragEntered(*event->get());
729 }
730
731 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
732   if (!target_window_)
733     return;
734   DragDropDelegate* delegate =
735       aura::client::GetDragDropDelegate(target_window_);
736   if (delegate)
737     delegate->OnDragExited();
738   target_window_->RemoveObserver(this);
739   target_window_ = NULL;
740 }
741
742 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
743     int drag_operation) {
744   if (drag_operation & ui::DragDropTypes::DRAG_COPY)
745     return atom_cache_.GetAtom(kXdndActionCopy);
746   if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
747     return atom_cache_.GetAtom(kXdndActionMove);
748   if (drag_operation & ui::DragDropTypes::DRAG_LINK)
749     return atom_cache_.GetAtom(kXdndActionLink);
750
751   return None;
752 }
753
754 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
755   if (atom == atom_cache_.GetAtom(kXdndActionCopy))
756     return ui::DragDropTypes::DRAG_COPY;
757   if (atom == atom_cache_.GetAtom(kXdndActionMove))
758     return ui::DragDropTypes::DRAG_MOVE;
759   if (atom == atom_cache_.GetAtom(kXdndActionLink))
760     return ui::DragDropTypes::DRAG_LINK;
761
762   return ui::DragDropTypes::DRAG_NONE;
763 }
764
765 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
766   std::vector< ::Atom> operations;
767   if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
768     operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
769   if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
770     operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
771   if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
772     operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
773   return operations;
774 }
775
776 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
777   return source_provider_ ? source_provider_->GetFormatMap() :
778       ui::SelectionFormatMap();
779 }
780
781 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
782     ::Window source_window,
783     const gfx::Point& screen_point) {
784   int drag_operation = ui::DragDropTypes::DRAG_NONE;
785   scoped_ptr<ui::OSExchangeData> data;
786   scoped_ptr<ui::DropTargetEvent> drop_target_event;
787   DragDropDelegate* delegate = NULL;
788   DragTranslate(screen_point, &data, &drop_target_event, &delegate);
789   if (delegate)
790     drag_operation = delegate->OnDragUpdated(*drop_target_event);
791
792   // Sends an XdndStatus message back to the source_window. l[2,3]
793   // theoretically represent an area in the window where the current action is
794   // the same as what we're returning, but I can't find any implementation that
795   // actually making use of this. A client can return (0, 0) and/or set the
796   // first bit of l[1] to disable the feature, and it appears that gtk neither
797   // sets this nor respects it if set.
798   XEvent xev;
799   xev.xclient.type = ClientMessage;
800   xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
801   xev.xclient.format = 32;
802   xev.xclient.window = source_window;
803   xev.xclient.data.l[0] = xwindow_;
804   xev.xclient.data.l[1] = (drag_operation != 0) ?
805       (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
806   xev.xclient.data.l[2] = 0;
807   xev.xclient.data.l[3] = 0;
808   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
809
810   SendXClientEvent(source_window, &xev);
811 }
812
813 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
814   XEvent xev;
815   xev.xclient.type = ClientMessage;
816   xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
817   xev.xclient.format = 32;
818   xev.xclient.window = dest_window;
819   xev.xclient.data.l[0] = xwindow_;
820   xev.xclient.data.l[1] = (kMinXdndVersion << 24);  // The version number.
821   xev.xclient.data.l[2] = 0;
822   xev.xclient.data.l[3] = 0;
823   xev.xclient.data.l[4] = 0;
824
825   std::vector<Atom> targets;
826   source_provider_->RetrieveTargets(&targets);
827
828   if (targets.size() > 3) {
829     xev.xclient.data.l[1] |= 1;
830     ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
831   } else {
832     // Pack the targets into the enter message.
833     for (size_t i = 0; i < targets.size(); ++i)
834       xev.xclient.data.l[2 + i] = targets[i];
835   }
836
837   SendXClientEvent(dest_window, &xev);
838 }
839
840 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
841   // If we're sending a leave message, don't wait for status messages anymore.
842   waiting_on_status_.erase(dest_window);
843   NextPositionMap::iterator it = next_position_message_.find(dest_window);
844   if (it != next_position_message_.end())
845     next_position_message_.erase(it);
846
847   XEvent xev;
848   xev.xclient.type = ClientMessage;
849   xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
850   xev.xclient.format = 32;
851   xev.xclient.window = dest_window;
852   xev.xclient.data.l[0] = xwindow_;
853   xev.xclient.data.l[1] = 0;
854   xev.xclient.data.l[2] = 0;
855   xev.xclient.data.l[3] = 0;
856   xev.xclient.data.l[4] = 0;
857   SendXClientEvent(dest_window, &xev);
858 }
859
860 void DesktopDragDropClientAuraX11::SendXdndPosition(
861     ::Window dest_window,
862     const gfx::Point& screen_point,
863     unsigned long time) {
864   waiting_on_status_.insert(dest_window);
865
866   XEvent xev;
867   xev.xclient.type = ClientMessage;
868   xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
869   xev.xclient.format = 32;
870   xev.xclient.window = dest_window;
871   xev.xclient.data.l[0] = xwindow_;
872   xev.xclient.data.l[1] = 0;
873   xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
874   xev.xclient.data.l[3] = time;
875   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
876   SendXClientEvent(dest_window, &xev);
877 }
878
879 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
880   XEvent xev;
881   xev.xclient.type = ClientMessage;
882   xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
883   xev.xclient.format = 32;
884   xev.xclient.window = dest_window;
885   xev.xclient.data.l[0] = xwindow_;
886   xev.xclient.data.l[1] = 0;
887   xev.xclient.data.l[2] = CurrentTime;
888   xev.xclient.data.l[3] = None;
889   xev.xclient.data.l[4] = None;
890   SendXClientEvent(dest_window, &xev);
891 }
892
893 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
894                                                     XEvent* xev) {
895   DCHECK_EQ(ClientMessage, xev->type);
896
897   // Don't send messages to the X11 message queue if we can help it.
898   DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
899   if (short_circuit) {
900     Atom message_type = xev->xclient.message_type;
901     if (message_type == atom_cache_.GetAtom("XdndEnter")) {
902       short_circuit->OnXdndEnter(xev->xclient);
903       return;
904     } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
905       short_circuit->OnXdndLeave(xev->xclient);
906       return;
907     } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
908       short_circuit->OnXdndPosition(xev->xclient);
909       return;
910     } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
911       short_circuit->OnXdndStatus(xev->xclient);
912       return;
913     } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
914       short_circuit->OnXdndFinished(xev->xclient);
915       return;
916     } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
917       short_circuit->OnXdndDrop(xev->xclient);
918       return;
919     }
920   }
921
922   // I don't understand why the GTK+ code is doing what it's doing here. It
923   // goes out of its way to send the XEvent so that it receives a callback on
924   // success or failure, and when it fails, it then sends an internal
925   // GdkEvent about the failed drag. (And sending this message doesn't appear
926   // to go through normal xlib machinery, but instead passes through the low
927   // level xProto (the x11 wire format) that I don't understand.
928   //
929   // I'm unsure if I have to jump through those hoops, or if XSendEvent is
930   // sufficient.
931   XSendEvent(xdisplay_, xid, False, 0, xev);
932 }
933
934 }  // namespace views