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