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